From 83c5fcebdbab2bbc7c0653e542191cfff954991d Mon Sep 17 00:00:00 2001 From: Benjamin Sutas Date: Fri, 29 Aug 2025 12:19:12 +1000 Subject: [PATCH 01/21] progress --- Dat/FileParsing/SawyerStreamWriter.cs | 2 +- Dat/Loaders/AirportObjectLoader.cs | 2 +- Dat/Loaders/BridgeObjectLoader.cs | 2 +- Dat/Loaders/BuildingObjectLoader.cs | 2 +- Dat/Loaders/CargoObjectLoader.cs | 2 +- Dat/Loaders/CliffEdgeObjectLoader.cs | 2 +- Dat/Loaders/ClimateObjectLoader.cs | 2 +- Dat/Loaders/CompetitorObjectLoader.cs | 2 +- Dat/Loaders/CurrencyObjectLoader.cs | 2 +- Dat/Loaders/DockObjectLoader.cs | 2 +- Dat/Loaders/HillShapesObjectLoader.cs | 2 +- Dat/Loaders/IndustryObjectLoader.cs | 2 +- Dat/Loaders/InterfaceSkinObjectLoader.cs | 2 +- Dat/Loaders/LandObjectLoader.cs | 2 +- Dat/Loaders/LevelCrossingObjectLoader.cs | 2 +- Dat/Loaders/RegionObjectLoader.cs | 2 +- Dat/Loaders/RoadExtraObjectLoader.cs | 2 +- Dat/Loaders/RoadObjectLoader.cs | 2 +- Dat/Loaders/RoadStationObjectLoader.cs | 2 +- Dat/Loaders/ScaffoldingObjectLoader.cs | 2 +- Dat/Loaders/ScenarioTextObjectLoader.cs | 2 +- Dat/Loaders/SnowObjectLoader.cs | 2 +- Dat/Loaders/SteamObjectLoader.cs | 2 +- Dat/Loaders/StreetLightObjectLoader.cs | 2 +- Dat/Loaders/TrackExtraObjectLoader.cs | 2 +- Dat/Loaders/TrackObjectLoader.cs | 2 +- Dat/Loaders/TrackSignalObjectLoader.cs | 2 +- Dat/Loaders/TrackStationObjectLoader.cs | 2 +- Dat/Loaders/TreeObjectLoader.cs | 2 +- Dat/Loaders/TunnelObjectLoader.cs | 2 +- Dat/Loaders/Vehicle/VehicleObjectLoader.cs | 2 +- Dat/Loaders/WallObjectLoader.cs | 2 +- Dat/Loaders/WaterObjectLoader.cs | 2 +- Dat/Types/G1Dat.cs | 17 ++++- Definitions/ObjectModels/ImageTable.cs | 15 ++++ Definitions/ObjectModels/LocoObject.cs | 20 +++-- .../ObjectModels/Types/GraphicsElement.cs | 5 +- Gui/Models/ObjectEditorModel.cs | 12 --- Gui/Models/UiG1.cs | 12 --- Gui/ViewModels/DatTypes/G1ViewModel.cs | 4 +- .../DatTypes/ObjectEditorViewModel.cs | 4 +- .../SubObjectTypes/GraphicsElementJson.cs | 14 ++-- .../SubObjectTypes/ImageTableViewModel.cs | 19 +++-- .../SubObjectTypes/ImageViewModel.cs | 14 ++-- .../TableHandlers/ObjectRouteHandler.cs | 2 +- .../TableHandlers/V1RouteHandler.cs | 2 +- Tests/IdempotenceTests.cs | 2 +- Tests/ImagePaletteConversionTests.cs | 2 +- Tests/LoadSaveTests.cs | 76 +++++++++---------- 49 files changed, 142 insertions(+), 144 deletions(-) create mode 100644 Definitions/ObjectModels/ImageTable.cs diff --git a/Dat/FileParsing/SawyerStreamWriter.cs b/Dat/FileParsing/SawyerStreamWriter.cs index 972d85f4..7cf637ae 100644 --- a/Dat/FileParsing/SawyerStreamWriter.cs +++ b/Dat/FileParsing/SawyerStreamWriter.cs @@ -615,7 +615,7 @@ public static void SaveG1(string filename, G1Dat g1) { using (var fs = File.OpenWrite(filename)) { - WriteImageTable(fs, g1.GraphicsElements); + WriteImageTable(fs, g1.ImageTable.GraphicsElements); } } } diff --git a/Dat/Loaders/AirportObjectLoader.cs b/Dat/Loaders/AirportObjectLoader.cs index f2b0e4e0..35d0a2d5 100644 --- a/Dat/Loaders/AirportObjectLoader.cs +++ b/Dat/Loaders/AirportObjectLoader.cs @@ -152,7 +152,7 @@ public static void Save(Stream stream, LocoObject obj) SaveVariable(bw, model); // image table - SawyerStreamWriter.WriteImageTable(stream, obj.GraphicsElements); + SawyerStreamWriter.WriteImageTable(stream, obj.ImageTable.GraphicsElements); } } diff --git a/Dat/Loaders/BridgeObjectLoader.cs b/Dat/Loaders/BridgeObjectLoader.cs index c705f59c..49470a5c 100644 --- a/Dat/Loaders/BridgeObjectLoader.cs +++ b/Dat/Loaders/BridgeObjectLoader.cs @@ -109,7 +109,7 @@ public static void Save(Stream stream, LocoObject obj) bw.WriteS5HeaderList(model.CompatibleRoadObjects); // image table - SawyerStreamWriter.WriteImageTable(stream, obj.GraphicsElements); + SawyerStreamWriter.WriteImageTable(stream, obj.ImageTable.GraphicsElements); } } diff --git a/Dat/Loaders/BuildingObjectLoader.cs b/Dat/Loaders/BuildingObjectLoader.cs index b918fad5..9cfef602 100644 --- a/Dat/Loaders/BuildingObjectLoader.cs +++ b/Dat/Loaders/BuildingObjectLoader.cs @@ -146,7 +146,7 @@ public static void Save(Stream stream, LocoObject obj) SaveVariable(model, bw); // image table - SawyerStreamWriter.WriteImageTable(stream, obj.GraphicsElements); + SawyerStreamWriter.WriteImageTable(stream, obj.ImageTable.GraphicsElements); } } diff --git a/Dat/Loaders/CargoObjectLoader.cs b/Dat/Loaders/CargoObjectLoader.cs index 7788fb70..bed109f9 100644 --- a/Dat/Loaders/CargoObjectLoader.cs +++ b/Dat/Loaders/CargoObjectLoader.cs @@ -99,7 +99,7 @@ public static void Save(Stream stream, LocoObject obj) // N/A // image table - SawyerStreamWriter.WriteImageTable(stream, obj.GraphicsElements); + SawyerStreamWriter.WriteImageTable(stream, obj.ImageTable.GraphicsElements); } } } diff --git a/Dat/Loaders/CliffEdgeObjectLoader.cs b/Dat/Loaders/CliffEdgeObjectLoader.cs index 4dc30b13..472e10fe 100644 --- a/Dat/Loaders/CliffEdgeObjectLoader.cs +++ b/Dat/Loaders/CliffEdgeObjectLoader.cs @@ -59,7 +59,7 @@ public static void Save(Stream stream, LocoObject obj) // N/A // image table - SawyerStreamWriter.WriteImageTable(stream, obj.GraphicsElements); + SawyerStreamWriter.WriteImageTable(stream, obj.ImageTable.GraphicsElements); } } } diff --git a/Dat/Loaders/ClimateObjectLoader.cs b/Dat/Loaders/ClimateObjectLoader.cs index 8cd0d789..e4c3671b 100644 --- a/Dat/Loaders/ClimateObjectLoader.cs +++ b/Dat/Loaders/ClimateObjectLoader.cs @@ -83,7 +83,7 @@ public static void Save(Stream stream, LocoObject obj) // N/A // image table - SawyerStreamWriter.WriteImageTable(stream, obj.GraphicsElements); + SawyerStreamWriter.WriteImageTable(stream, obj.ImageTable.GraphicsElements); } } } diff --git a/Dat/Loaders/CompetitorObjectLoader.cs b/Dat/Loaders/CompetitorObjectLoader.cs index 9b859b6b..491bb9fd 100644 --- a/Dat/Loaders/CompetitorObjectLoader.cs +++ b/Dat/Loaders/CompetitorObjectLoader.cs @@ -86,7 +86,7 @@ public static void Save(Stream stream, LocoObject obj) // N/A // image table - SawyerStreamWriter.WriteImageTable(stream, obj.GraphicsElements); + SawyerStreamWriter.WriteImageTable(stream, obj.ImageTable.GraphicsElements); } } diff --git a/Dat/Loaders/CurrencyObjectLoader.cs b/Dat/Loaders/CurrencyObjectLoader.cs index 44e680a3..3b0ceb47 100644 --- a/Dat/Loaders/CurrencyObjectLoader.cs +++ b/Dat/Loaders/CurrencyObjectLoader.cs @@ -75,7 +75,7 @@ public static void Save(Stream stream, LocoObject obj) // N/A // image table - SawyerStreamWriter.WriteImageTable(stream, obj.GraphicsElements); + SawyerStreamWriter.WriteImageTable(stream, obj.ImageTable.GraphicsElements); } } } diff --git a/Dat/Loaders/DockObjectLoader.cs b/Dat/Loaders/DockObjectLoader.cs index 662954ca..af9ecabd 100644 --- a/Dat/Loaders/DockObjectLoader.cs +++ b/Dat/Loaders/DockObjectLoader.cs @@ -102,7 +102,7 @@ public static void Save(Stream stream, LocoObject obj) SaveVariable(model, bw); // image table - SawyerStreamWriter.WriteImageTable(stream, obj.GraphicsElements); + SawyerStreamWriter.WriteImageTable(stream, obj.ImageTable.GraphicsElements); } } diff --git a/Dat/Loaders/HillShapesObjectLoader.cs b/Dat/Loaders/HillShapesObjectLoader.cs index 680a640f..36949c61 100644 --- a/Dat/Loaders/HillShapesObjectLoader.cs +++ b/Dat/Loaders/HillShapesObjectLoader.cs @@ -74,7 +74,7 @@ public static void Save(Stream stream, LocoObject obj) // N/A // image table - SawyerStreamWriter.WriteImageTable(stream, obj.GraphicsElements); + SawyerStreamWriter.WriteImageTable(stream, obj.ImageTable.GraphicsElements); } } diff --git a/Dat/Loaders/IndustryObjectLoader.cs b/Dat/Loaders/IndustryObjectLoader.cs index 13d956e6..1521bc61 100644 --- a/Dat/Loaders/IndustryObjectLoader.cs +++ b/Dat/Loaders/IndustryObjectLoader.cs @@ -201,7 +201,7 @@ public static void Save(Stream stream, LocoObject obj) SaveVariable(model, bw); // image table - SawyerStreamWriter.WriteImageTable(stream, obj.GraphicsElements); + SawyerStreamWriter.WriteImageTable(stream, obj.ImageTable.GraphicsElements); } } diff --git a/Dat/Loaders/InterfaceSkinObjectLoader.cs b/Dat/Loaders/InterfaceSkinObjectLoader.cs index 8774feb9..02aef33b 100644 --- a/Dat/Loaders/InterfaceSkinObjectLoader.cs +++ b/Dat/Loaders/InterfaceSkinObjectLoader.cs @@ -103,7 +103,7 @@ public static void Save(Stream stream, LocoObject obj) // N/A // image table - SawyerStreamWriter.WriteImageTable(stream, obj.GraphicsElements); + SawyerStreamWriter.WriteImageTable(stream, obj.ImageTable.GraphicsElements); } } } diff --git a/Dat/Loaders/LandObjectLoader.cs b/Dat/Loaders/LandObjectLoader.cs index 61ff5992..de8661bc 100644 --- a/Dat/Loaders/LandObjectLoader.cs +++ b/Dat/Loaders/LandObjectLoader.cs @@ -106,7 +106,7 @@ public static void Save(Stream stream, LocoObject obj) } // image table - SawyerStreamWriter.WriteImageTable(stream, obj.GraphicsElements); + SawyerStreamWriter.WriteImageTable(stream, obj.ImageTable.GraphicsElements); } } diff --git a/Dat/Loaders/LevelCrossingObjectLoader.cs b/Dat/Loaders/LevelCrossingObjectLoader.cs index 59333b25..af828f3e 100644 --- a/Dat/Loaders/LevelCrossingObjectLoader.cs +++ b/Dat/Loaders/LevelCrossingObjectLoader.cs @@ -85,7 +85,7 @@ public static void Save(Stream stream, LocoObject obj) // N/A // image table - SawyerStreamWriter.WriteImageTable(stream, obj.GraphicsElements); + SawyerStreamWriter.WriteImageTable(stream, obj.ImageTable.GraphicsElements); } } } diff --git a/Dat/Loaders/RegionObjectLoader.cs b/Dat/Loaders/RegionObjectLoader.cs index d2f2a8f3..c1f95446 100644 --- a/Dat/Loaders/RegionObjectLoader.cs +++ b/Dat/Loaders/RegionObjectLoader.cs @@ -88,7 +88,7 @@ public static void Save(Stream stream, LocoObject obj) bw.WriteTerminator(); // end of dependent objects // image table - SawyerStreamWriter.WriteImageTable(stream, obj.GraphicsElements); + SawyerStreamWriter.WriteImageTable(stream, obj.ImageTable.GraphicsElements); } } diff --git a/Dat/Loaders/RoadExtraObjectLoader.cs b/Dat/Loaders/RoadExtraObjectLoader.cs index 8c910ce7..11b3cf89 100644 --- a/Dat/Loaders/RoadExtraObjectLoader.cs +++ b/Dat/Loaders/RoadExtraObjectLoader.cs @@ -70,7 +70,7 @@ public static void Save(Stream stream, LocoObject obj) // N/A // image table - SawyerStreamWriter.WriteImageTable(stream, obj.GraphicsElements); + SawyerStreamWriter.WriteImageTable(stream, obj.ImageTable.GraphicsElements); } } } diff --git a/Dat/Loaders/RoadObjectLoader.cs b/Dat/Loaders/RoadObjectLoader.cs index f7cba061..865a1f48 100644 --- a/Dat/Loaders/RoadObjectLoader.cs +++ b/Dat/Loaders/RoadObjectLoader.cs @@ -127,7 +127,7 @@ public static void Save(Stream stream, LocoObject obj) bw.WriteS5HeaderList(model.Stations); // image table - SawyerStreamWriter.WriteImageTable(stream, obj.GraphicsElements); + SawyerStreamWriter.WriteImageTable(stream, obj.ImageTable.GraphicsElements); } } diff --git a/Dat/Loaders/RoadStationObjectLoader.cs b/Dat/Loaders/RoadStationObjectLoader.cs index 1fad9a35..1be7c4fc 100644 --- a/Dat/Loaders/RoadStationObjectLoader.cs +++ b/Dat/Loaders/RoadStationObjectLoader.cs @@ -132,7 +132,7 @@ public static void Save(Stream stream, LocoObject obj) SaveVariable(model, bw); // image table - SawyerStreamWriter.WriteImageTable(stream, obj.GraphicsElements); + SawyerStreamWriter.WriteImageTable(stream, obj.ImageTable.GraphicsElements); } } diff --git a/Dat/Loaders/ScaffoldingObjectLoader.cs b/Dat/Loaders/ScaffoldingObjectLoader.cs index 8f13cdf9..1445bfbf 100644 --- a/Dat/Loaders/ScaffoldingObjectLoader.cs +++ b/Dat/Loaders/ScaffoldingObjectLoader.cs @@ -90,7 +90,7 @@ public static void Save(Stream stream, LocoObject obj) // N/A // image table - SawyerStreamWriter.WriteImageTable(stream, obj.GraphicsElements); + SawyerStreamWriter.WriteImageTable(stream, obj.ImageTable.GraphicsElements); } } } diff --git a/Dat/Loaders/ScenarioTextObjectLoader.cs b/Dat/Loaders/ScenarioTextObjectLoader.cs index 318284eb..a62ebe41 100644 --- a/Dat/Loaders/ScenarioTextObjectLoader.cs +++ b/Dat/Loaders/ScenarioTextObjectLoader.cs @@ -67,7 +67,7 @@ public static void Save(Stream stream, LocoObject obj) // N/A // image table - SawyerStreamWriter.WriteImageTable(stream, obj.GraphicsElements); + SawyerStreamWriter.WriteImageTable(stream, obj.ImageTable.GraphicsElements); } } } diff --git a/Dat/Loaders/SnowObjectLoader.cs b/Dat/Loaders/SnowObjectLoader.cs index 1b1ec1cb..42ca9243 100644 --- a/Dat/Loaders/SnowObjectLoader.cs +++ b/Dat/Loaders/SnowObjectLoader.cs @@ -62,7 +62,7 @@ public static void Save(Stream stream, LocoObject obj) // N/A // image table - SawyerStreamWriter.WriteImageTable(stream, obj.GraphicsElements); + SawyerStreamWriter.WriteImageTable(stream, obj.ImageTable.GraphicsElements); } } } diff --git a/Dat/Loaders/SteamObjectLoader.cs b/Dat/Loaders/SteamObjectLoader.cs index e39acd4d..a0ef59df 100644 --- a/Dat/Loaders/SteamObjectLoader.cs +++ b/Dat/Loaders/SteamObjectLoader.cs @@ -117,7 +117,7 @@ public static void Save(Stream stream, LocoObject obj) SaveVariable(model, bw); // image table - SawyerStreamWriter.WriteImageTable(stream, obj.GraphicsElements); + SawyerStreamWriter.WriteImageTable(stream, obj.ImageTable.GraphicsElements); } } diff --git a/Dat/Loaders/StreetLightObjectLoader.cs b/Dat/Loaders/StreetLightObjectLoader.cs index 0baec0ed..527b4d04 100644 --- a/Dat/Loaders/StreetLightObjectLoader.cs +++ b/Dat/Loaders/StreetLightObjectLoader.cs @@ -66,7 +66,7 @@ public static void Save(Stream stream, LocoObject obj) // N/A // image table - SawyerStreamWriter.WriteImageTable(stream, obj.GraphicsElements); + SawyerStreamWriter.WriteImageTable(stream, obj.ImageTable.GraphicsElements); } } } diff --git a/Dat/Loaders/TrackExtraObjectLoader.cs b/Dat/Loaders/TrackExtraObjectLoader.cs index b2d73d38..0d556a02 100644 --- a/Dat/Loaders/TrackExtraObjectLoader.cs +++ b/Dat/Loaders/TrackExtraObjectLoader.cs @@ -79,7 +79,7 @@ public static void Save(Stream stream, LocoObject obj) // N/A // image table - SawyerStreamWriter.WriteImageTable(stream, obj.GraphicsElements); + SawyerStreamWriter.WriteImageTable(stream, obj.ImageTable.GraphicsElements); } } } diff --git a/Dat/Loaders/TrackObjectLoader.cs b/Dat/Loaders/TrackObjectLoader.cs index 49f864e4..a0449c65 100644 --- a/Dat/Loaders/TrackObjectLoader.cs +++ b/Dat/Loaders/TrackObjectLoader.cs @@ -128,7 +128,7 @@ public static void Save(Stream stream, LocoObject obj) bw.WriteS5HeaderList(model.Stations); // image table - SawyerStreamWriter.WriteImageTable(stream, obj.GraphicsElements); + SawyerStreamWriter.WriteImageTable(stream, obj.ImageTable.GraphicsElements); } } diff --git a/Dat/Loaders/TrackSignalObjectLoader.cs b/Dat/Loaders/TrackSignalObjectLoader.cs index 459c17af..58235afb 100644 --- a/Dat/Loaders/TrackSignalObjectLoader.cs +++ b/Dat/Loaders/TrackSignalObjectLoader.cs @@ -93,7 +93,7 @@ public static void Save(Stream stream, LocoObject obj) bw.WriteS5HeaderList(model.CompatibleTrackObjects); // image table - SawyerStreamWriter.WriteImageTable(stream, obj.GraphicsElements); + SawyerStreamWriter.WriteImageTable(stream, obj.ImageTable.GraphicsElements); } } diff --git a/Dat/Loaders/TrackStationObjectLoader.cs b/Dat/Loaders/TrackStationObjectLoader.cs index ab772093..7017ef00 100644 --- a/Dat/Loaders/TrackStationObjectLoader.cs +++ b/Dat/Loaders/TrackStationObjectLoader.cs @@ -144,7 +144,7 @@ public static void Save(Stream stream, LocoObject obj) SaveVariable(model, bw); // image table - SawyerStreamWriter.WriteImageTable(stream, obj.GraphicsElements); + SawyerStreamWriter.WriteImageTable(stream, obj.ImageTable.GraphicsElements); } } diff --git a/Dat/Loaders/TreeObjectLoader.cs b/Dat/Loaders/TreeObjectLoader.cs index 4a2b028a..6a054159 100644 --- a/Dat/Loaders/TreeObjectLoader.cs +++ b/Dat/Loaders/TreeObjectLoader.cs @@ -105,7 +105,7 @@ public static void Save(Stream stream, LocoObject obj) // N/A // image table - SawyerStreamWriter.WriteImageTable(stream, obj.GraphicsElements); + SawyerStreamWriter.WriteImageTable(stream, obj.ImageTable.GraphicsElements); } } diff --git a/Dat/Loaders/TunnelObjectLoader.cs b/Dat/Loaders/TunnelObjectLoader.cs index dec153b4..fca34c11 100644 --- a/Dat/Loaders/TunnelObjectLoader.cs +++ b/Dat/Loaders/TunnelObjectLoader.cs @@ -65,7 +65,7 @@ public static void Save(Stream stream, LocoObject obj) // N/A // image table - SawyerStreamWriter.WriteImageTable(stream, obj.GraphicsElements); + SawyerStreamWriter.WriteImageTable(stream, obj.ImageTable.GraphicsElements); } } } diff --git a/Dat/Loaders/Vehicle/VehicleObjectLoader.cs b/Dat/Loaders/Vehicle/VehicleObjectLoader.cs index b2fdbba3..db7d6948 100644 --- a/Dat/Loaders/Vehicle/VehicleObjectLoader.cs +++ b/Dat/Loaders/Vehicle/VehicleObjectLoader.cs @@ -286,7 +286,7 @@ public static void Save(Stream stream, LocoObject obj) SaveVariable(model, bw); // image table - SawyerStreamWriter.WriteImageTable(stream, obj.GraphicsElements); + SawyerStreamWriter.WriteImageTable(stream, obj.ImageTable.GraphicsElements); } } diff --git a/Dat/Loaders/WallObjectLoader.cs b/Dat/Loaders/WallObjectLoader.cs index d8fed4b7..b27e5ed9 100644 --- a/Dat/Loaders/WallObjectLoader.cs +++ b/Dat/Loaders/WallObjectLoader.cs @@ -75,7 +75,7 @@ public static void Save(Stream stream, LocoObject obj) // N/A // image table - SawyerStreamWriter.WriteImageTable(stream, obj.GraphicsElements); + SawyerStreamWriter.WriteImageTable(stream, obj.ImageTable.GraphicsElements); } } diff --git a/Dat/Loaders/WaterObjectLoader.cs b/Dat/Loaders/WaterObjectLoader.cs index b2ff5348..4b73ed06 100644 --- a/Dat/Loaders/WaterObjectLoader.cs +++ b/Dat/Loaders/WaterObjectLoader.cs @@ -74,7 +74,7 @@ public static void Save(Stream stream, LocoObject obj) // N/A // image table - SawyerStreamWriter.WriteImageTable(stream, obj.GraphicsElements); + SawyerStreamWriter.WriteImageTable(stream, obj.ImageTable.GraphicsElements); } } } diff --git a/Dat/Types/G1Dat.cs b/Dat/Types/G1Dat.cs index 4f7cd838..ac5fee99 100644 --- a/Dat/Types/G1Dat.cs +++ b/Dat/Types/G1Dat.cs @@ -5,14 +5,23 @@ namespace Dat.Types; [TypeConverter(typeof(ExpandableObjectConverter))] -public class G1Dat(G1Header g1Header, List g1Elements) : IHasGraphicsElements, IImageTableNameProvider +public class G1Dat : IImageTableNameProvider { - public G1Header G1Header { get; set; } = g1Header; - public List GraphicsElements { get; set; } = g1Elements; + public G1Header G1Header { get; set; } + public ImageTable ImageTable { get; set; } public bool IsSteamG1 - => GraphicsElements.Count == 3896; + => ImageTable.GraphicsElements.Count == 3896; + + public G1Dat(G1Header g1Header, List graphicsElements) + { + G1Header = g1Header; + ImageTable = new ImageTable + { + Groups = [("All", graphicsElements)] + }; + } public bool TryGetImageName(int id, out string? value) => id < 3550 diff --git a/Definitions/ObjectModels/ImageTable.cs b/Definitions/ObjectModels/ImageTable.cs new file mode 100644 index 00000000..66b7889f --- /dev/null +++ b/Definitions/ObjectModels/ImageTable.cs @@ -0,0 +1,15 @@ +using Definitions.ObjectModels.Types; + +namespace Definitions.ObjectModels; + +public class ImageTable : IHasGraphicsElements +{ + // public/old interface + public List GraphicsElements + { + get => [.. Groups.SelectMany(x => x.GraphicsElements)]; + set => Groups.Add((")", value)); + } + + public List<(string Name, List GraphicsElements)> Groups { get; set; } = []; +} diff --git a/Definitions/ObjectModels/LocoObject.cs b/Definitions/ObjectModels/LocoObject.cs index 39ffd19d..b43a7462 100644 --- a/Definitions/ObjectModels/LocoObject.cs +++ b/Definitions/ObjectModels/LocoObject.cs @@ -2,26 +2,24 @@ namespace Definitions.ObjectModels; -public class LocoObject : IHasGraphicsElements +public class LocoObject { public LocoObject(ObjectType objectType, ILocoStruct obj, StringTable stringTable, List graphicsElements) { ObjectType = objectType; Object = obj; StringTable = stringTable; - GraphicsElements = graphicsElements; - } - - public LocoObject(ObjectType objectType, ILocoStruct obj, StringTable stringTable) - { - ObjectType = objectType; - Object = obj; - StringTable = stringTable; - GraphicsElements = []; + ImageTable = new ImageTable + { + Groups = [("All", graphicsElements)] + }; } public ObjectType ObjectType { get; init; } public ILocoStruct Object { get; set; } public StringTable StringTable { get; set; } - public List GraphicsElements { get; set; } + public ImageTable ImageTable { get; set; } + + //public List GraphicsElements + //{ get => ImageTable.GraphicsElements; set => throw new NotImplementedException(); } } diff --git a/Definitions/ObjectModels/Types/GraphicsElement.cs b/Definitions/ObjectModels/Types/GraphicsElement.cs index 3bdbdb95..2a308310 100644 --- a/Definitions/ObjectModels/Types/GraphicsElement.cs +++ b/Definitions/ObjectModels/Types/GraphicsElement.cs @@ -1,5 +1,3 @@ -using System.Text.Json.Serialization; - namespace Definitions.ObjectModels.Types; [Flags] @@ -24,5 +22,6 @@ public class GraphicsElement // follows G1Element32, except XOffset and YOffset public GraphicsElementFlags Flags { get; set; } public short ZoomOffset { get; set; } public byte[] ImageData { get; set; } = []; - // string Name - taken from IImageNameProvider + + public string Name { get; set; } // taken from IImageNameProvider } diff --git a/Gui/Models/ObjectEditorModel.cs b/Gui/Models/ObjectEditorModel.cs index cb9e4d0d..6cd43e82 100644 --- a/Gui/Models/ObjectEditorModel.cs +++ b/Gui/Models/ObjectEditorModel.cs @@ -326,7 +326,6 @@ bool TryLoadLocalFile(FileSystemItem filesystemItem, out UiDatLocoFile? locoDatF DatFileInfo? fileInfo = null; LocoObject? locoObject = null; MetadataModel? metadata = null; - //List> images = []; var filename = File.Exists(filesystemItem.FileName) ? filesystemItem.FileName @@ -343,17 +342,6 @@ bool TryLoadLocalFile(FileSystemItem filesystemItem, out UiDatLocoFile? locoDatF //DatObjects = [new(0)], }; // todo: look up the rest of the data from internet - //if (locoObject != null) - //{ - // foreach (var i in locoObject.GraphicsElements) - // { - // if (PaletteMap.TryConvertG1ToRgba32Bitmap(i, ColourRemapSwatch.PrimaryRemap, ColourRemapSwatch.SecondaryRemap, out var image)) - // { - // images.Add(image!); - // } - // } - //} - locoDatFile = new UiDatLocoFile() { DatFileInfo = fileInfo, LocoObject = locoObject, Metadata = metadata }; return true; } diff --git a/Gui/Models/UiG1.cs b/Gui/Models/UiG1.cs index 041ec7ac..1c0dc5c0 100644 --- a/Gui/Models/UiG1.cs +++ b/Gui/Models/UiG1.cs @@ -1,6 +1,4 @@ using Dat.Types; -using Definitions.ObjectModels.Types; -using System.Collections.Generic; using System.ComponentModel; namespace Gui.Models; @@ -9,14 +7,4 @@ namespace Gui.Models; public class UiG1 : IUiObject { public required G1Dat G1 { get; set; } - - public List G1Elements - { - get => G1.GraphicsElements; - set - { - G1.GraphicsElements.Clear(); - G1.GraphicsElements.AddRange(value); - } - } } diff --git a/Gui/ViewModels/DatTypes/G1ViewModel.cs b/Gui/ViewModels/DatTypes/G1ViewModel.cs index a104f60e..10ecd0c5 100644 --- a/Gui/ViewModels/DatTypes/G1ViewModel.cs +++ b/Gui/ViewModels/DatTypes/G1ViewModel.cs @@ -27,7 +27,7 @@ public override void Load() return; } - ImageTableViewModel = new ImageTableViewModel(Model.G1.GraphicsElements, Model.G1, Model.PaletteMap, logger); + ImageTableViewModel = new ImageTableViewModel(Model.G1.ImageTable, Model.G1, Model.PaletteMap, logger); } public override void Save() @@ -38,7 +38,7 @@ public override void Save() return; } - Model.G1.GraphicsElements = [.. ImageTableViewModel.ImageViewModels.Select(x => x.ToGraphicsElement())]; + Model.G1.ImageTable.GraphicsElements = [.. ImageTableViewModel.ImageViewModels.Select(x => x.ToGraphicsElement())]; var savePath = CurrentFile.FileLocation == FileLocation.Local ? Path.Combine(Model.Settings.ObjDataDirectory, CurrentFile.FileName) diff --git a/Gui/ViewModels/DatTypes/ObjectEditorViewModel.cs b/Gui/ViewModels/DatTypes/ObjectEditorViewModel.cs index f69f0c5c..c04468c2 100644 --- a/Gui/ViewModels/DatTypes/ObjectEditorViewModel.cs +++ b/Gui/ViewModels/DatTypes/ObjectEditorViewModel.cs @@ -158,7 +158,7 @@ public override void Load() ExtraContentViewModel = CurrentObject.LocoObject.Object is SoundObject soundObject ? new AudioViewModel(logger, CurrentObject.DatFileInfo.S5Header.Name, soundObject.SoundObjectData.PcmHeader, soundObject.PcmData) - : new ImageTableViewModel(CurrentObject.LocoObject.GraphicsElements, imageNameProvider, Model.PaletteMap, Model.Logger); + : new ImageTableViewModel(CurrentObject.LocoObject.ImageTable, imageNameProvider, Model.PaletteMap, Model.Logger); } else { @@ -276,7 +276,7 @@ void SaveCore(string filename, SawyerEncoding? encodingToUse = null) if (ExtraContentViewModel is ImageTableViewModel itvm) { - CurrentObject.LocoObject.GraphicsElements = itvm.ImageViewModels.Select(x => x.ToGraphicsElement()).ToList(); + CurrentObject.LocoObject.ImageTable.GraphicsElements = itvm.ImageViewModels.Select(x => x.ToGraphicsElement()).ToList(); } // this is hacky but it should work diff --git a/Gui/ViewModels/SubObjectTypes/GraphicsElementJson.cs b/Gui/ViewModels/SubObjectTypes/GraphicsElementJson.cs index 85acfa4d..efb1a712 100644 --- a/Gui/ViewModels/SubObjectTypes/GraphicsElementJson.cs +++ b/Gui/ViewModels/SubObjectTypes/GraphicsElementJson.cs @@ -8,21 +8,23 @@ public record GraphicsElementJson( [property: JsonPropertyName("x")] int16_t XOffset, [property: JsonPropertyName("y")] int16_t YOffset, [property: JsonPropertyName("zoomOffset")] int16_t? ZoomOffset, - [property: JsonPropertyName("flags")] GraphicsElementFlags? Flags + [property: JsonPropertyName("flags")] GraphicsElementFlags? Flags, + [property: JsonPropertyName("name")] string? Name + // todo: add name ) { public GraphicsElementJson() - : this("", 0, 0, null, null) + : this("", 0, 0, null, null, null) { } - public GraphicsElementJson(string path, int16_t xOffset, int16_t yOffset) - : this(path, xOffset, yOffset, null, null) + public GraphicsElementJson(string path, int16_t xOffset, int16_t yOffset, string name) + : this(path, xOffset, yOffset, null, null, name) { } public GraphicsElementJson(string path, GraphicsElement g1Element) - : this(path, g1Element.XOffset, g1Element.YOffset, g1Element.ZoomOffset, g1Element.Flags) + : this(path, g1Element.XOffset, g1Element.YOffset, g1Element.ZoomOffset, g1Element.Flags, g1Element.Name) { } public static GraphicsElementJson Zero - => new(string.Empty, 0, 0, 0, GraphicsElementFlags.None); + => new(string.Empty, 0, 0, 0, GraphicsElementFlags.None, string.Empty); } diff --git a/Gui/ViewModels/SubObjectTypes/ImageTableViewModel.cs b/Gui/ViewModels/SubObjectTypes/ImageTableViewModel.cs index 0795928f..8599658b 100644 --- a/Gui/ViewModels/SubObjectTypes/ImageTableViewModel.cs +++ b/Gui/ViewModels/SubObjectTypes/ImageTableViewModel.cs @@ -75,15 +75,14 @@ public class ImageTableViewModel : ReactiveObject, IExtraContentViewModel public PaletteMap PaletteMap { get; init; } public readonly ILogger Logger; - public ImageTableViewModel(IList graphicsElements, IImageTableNameProvider imageNameProvider, PaletteMap paletteMap, ILogger logger) + public ImageTableViewModel(ImageTable imageTable, IImageTableNameProvider imageNameProvider, PaletteMap paletteMap, ILogger logger) { ArgumentNullException.ThrowIfNull(paletteMap); var index = 0; - foreach (var ge in graphicsElements) + foreach (var ge in imageTable.GraphicsElements) { - var success = imageNameProvider.TryGetImageName(index, out var imageName); - ImageViewModels.Add(new ImageViewModel(index, success ? imageName! : $"{index}-unnamed", ge, paletteMap)); + ImageViewModels.Add(new ImageViewModel(ge, paletteMap)); index++; } @@ -279,7 +278,7 @@ public void CropAllImages() public string GetImageName(int index) => NameProvider.TryGetImageName(index, out var value) && !string.IsNullOrEmpty(value) ? value - : index.ToString(); + : $"{index}-unnamed"; public static string TrimZeroes(string str) { @@ -324,7 +323,7 @@ public async Task ImportImages(string directory) var sanitised = files.Select(TrimZeroes).ToList(); offsets = [.. ImageViewModels - .Select((x, i) => new GraphicsElementJson($"{sanitised[i]}.png", (short)x.XOffset, (short)x.YOffset)) + .Select((x, i) => new GraphicsElementJson($"{sanitised[i]}.png", (short)x.XOffset, (short)x.YOffset, string.Empty)) .Fill(files.Length, GraphicsElementJson.Zero)]; Logger.Debug($"Didn't find sprites.json file, using existing G1Element32 offsets with {offsets.Count} images"); @@ -341,9 +340,8 @@ public async Task ImportImages(string directory) var img = is1Pixel ? OnePixelTransparent : Image.Load(Path.Combine(directory, offset.Path)); var newOffset = is1Pixel ? offset with { Flags = GraphicsElementFlags.HasTransparency } : offset; var graphicsElement = GraphicsElementFromImage(newOffset, img, PaletteMap); - - _ = NameProvider.TryGetImageName(i, out var imageName); - ImageViewModels.Add(new ImageViewModel(i, imageName ?? "", graphicsElement, PaletteMap)); + graphicsElement.Name = string.IsNullOrEmpty(graphicsElement.Name) ? GetImageName(i) : graphicsElement.Name; + ImageViewModels.Add(new ImageViewModel(graphicsElement, PaletteMap)); } Logger.Debug($"Imported {ImageViewModels.Count} images successfully"); @@ -365,7 +363,8 @@ static GraphicsElement GraphicsElementFromImage(GraphicsElementJson ele, Image UnderlyingImage.Width; public int Height => UnderlyingImage.Height; + public string Name { get; set; } + [Reactive] public int XOffset { get; set; } [Reactive] public int YOffset { get; set; } @@ -43,10 +43,9 @@ public Avalonia.Rect SelectedBitmapPreviewBorder readonly PaletteMap PaletteMap; - public ImageViewModel(int imageIndex, string imageName, GraphicsElement graphicsElement, PaletteMap paletteMap) + public ImageViewModel(GraphicsElement graphicsElement, PaletteMap paletteMap) { - ImageIndex = imageIndex; - ImageName = imageName; + Name = graphicsElement.Name; XOffset = graphicsElement.XOffset; YOffset = graphicsElement.YOffset; Flags = graphicsElement.Flags; @@ -147,10 +146,11 @@ static Rectangle FindCropRegion(Image image) public GraphicsElement ToGraphicsElement() { - if (ImageIndex < 0 || UnderlyingImage == null) + if (UnderlyingImage == null) { - throw new InvalidOperationException("Cannot convert to GraphicsElement when ImageIndex is less than 0 or UnderlyingImage is null"); + throw new InvalidOperationException("Cannot convert to GraphicsElement when UnderlyingImage is null"); } + // turn rgba32 into raw palette image var rawData = PaletteMap.ConvertRgba32ImageToG1Data(UnderlyingImage, Flags); return new GraphicsElement diff --git a/ObjectService/RouteHandlers/TableHandlers/ObjectRouteHandler.cs b/ObjectService/RouteHandlers/TableHandlers/ObjectRouteHandler.cs index 71d68c89..72ef5242 100644 --- a/ObjectService/RouteHandlers/TableHandlers/ObjectRouteHandler.cs +++ b/ObjectService/RouteHandlers/TableHandlers/ObjectRouteHandler.cs @@ -571,7 +571,7 @@ static async Task GetObjectImagesAsync([FromRoute] UniqueObjectId id, [ using (var zipArchive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true)) { var count = 0; - foreach (var g1 in locoObj!.LocoObject!.GraphicsElements) + foreach (var g1 in locoObj!.LocoObject!.ImageTable.GraphicsElements) { if (!pm.TryConvertG1ToRgba32Bitmap(g1, ColourRemapSwatch.PrimaryRemap, ColourRemapSwatch.SecondaryRemap, out var image)) { diff --git a/ObjectService/RouteHandlers/TableHandlers/V1RouteHandler.cs b/ObjectService/RouteHandlers/TableHandlers/V1RouteHandler.cs index 9329d780..39dd5bc3 100644 --- a/ObjectService/RouteHandlers/TableHandlers/V1RouteHandler.cs +++ b/ObjectService/RouteHandlers/TableHandlers/V1RouteHandler.cs @@ -246,7 +246,7 @@ public static async Task GetObjectImages(UniqueObjectId uniqueObjectId, using (var zipArchive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true)) { var count = 0; - foreach (var g1 in locoObj!.LocoObject!.GraphicsElements) + foreach (var g1 in locoObj!.LocoObject!.ImageTable.GraphicsElements) { if (!pm.TryConvertG1ToRgba32Bitmap(g1, ColourRemapSwatch.PrimaryRemap, ColourRemapSwatch.SecondaryRemap, out var image)) { diff --git a/Tests/IdempotenceTests.cs b/Tests/IdempotenceTests.cs index a73f95fb..5978d176 100644 --- a/Tests/IdempotenceTests.cs +++ b/Tests/IdempotenceTests.cs @@ -67,7 +67,7 @@ public void LoadSaveLoad(string filename) var pm = new PaletteMap("C:\\Users\\bigba\\source\\repos\\OpenLoco\\ObjectEditor\\Gui\\Assets\\palette.png"); var i = 0; - foreach (var ae in actual.GraphicsElements.Zip(expected.GraphicsElements)) + foreach (var ae in actual.ImageTable.GraphicsElements.Zip(expected.ImageTable.GraphicsElements)) { var ac = JsonSerializer.Serialize(ae.First); var ex = JsonSerializer.Serialize(ae.Second); diff --git a/Tests/ImagePaletteConversionTests.cs b/Tests/ImagePaletteConversionTests.cs index 630535a2..e2719cce 100644 --- a/Tests/ImagePaletteConversionTests.cs +++ b/Tests/ImagePaletteConversionTests.cs @@ -55,7 +55,7 @@ public void G1ElementToPNGAndBack(string objectSource) var paletteFile = Path.Combine(BasePalettePath, PaletteFileName); var paletteMap = new PaletteMap(paletteFile); var obj = SawyerStreamReader.LoadFullObject(Path.Combine(BaseObjDataPath, objectSource), Logger); - var g1Elements = obj!.LocoObject!.GraphicsElements; + var g1Elements = obj!.LocoObject!.ImageTable.GraphicsElements; using (Assert.EnterMultipleScope()) { diff --git a/Tests/LoadSaveTests.cs b/Tests/LoadSaveTests.cs index d6b016b1..114065af 100644 --- a/Tests/LoadSaveTests.cs +++ b/Tests/LoadSaveTests.cs @@ -145,7 +145,7 @@ void assertFunc(LocoObject obj, AirportObject struc) => Assert.Multiple(() => Assert.That(struc.var_B6[2], Is.Zero, nameof(struc.var_B6) + "[2]"); Assert.That(struc.var_B6[3], Is.Zero, nameof(struc.var_B6) + "[3]"); - Assert.That(obj.GraphicsElements, Has.Count.EqualTo(377)); + Assert.That(obj.ImageTable.GraphicsElements, Has.Count.EqualTo(377)); }); LoadSaveGenericTest(objectName, assertFunc); } @@ -185,7 +185,7 @@ void assertFunc(LocoObject obj, BridgeObject struc) => Assert.Multiple(() => //CollectionAssert.AreEqual(struc.RoadMods, Array.CreateInstance(typeof(byte), 7), nameof(struc.RoadMods)); Assert.That(struc.DesignedYear, Is.Zero, nameof(struc.DesignedYear)); - Assert.That(obj.GraphicsElements, Has.Count.EqualTo(124)); + Assert.That(obj.ImageTable.GraphicsElements, Has.Count.EqualTo(124)); }); LoadSaveGenericTest(objectName, assertFunc); } @@ -224,7 +224,7 @@ void assertFunc(LocoObject obj, BuildingObject struc) => Assert.Multiple(() => //Assert.That(struc.ElevatorHeightSequences[2].Count, Is.Zero, nameof(struc.ElevatorHeightSequences) + "[2]"); //Assert.That(struc.ElevatorHeightSequences[3].Count, Is.Zero, nameof(struc.ElevatorHeightSequences) + "[3]"); - Assert.That(obj.GraphicsElements, Has.Count.EqualTo(64)); + Assert.That(obj.ImageTable.GraphicsElements, Has.Count.EqualTo(64)); }); LoadSaveGenericTest(objectName, assertFunc); } @@ -249,7 +249,7 @@ void assertFunc(LocoObject obj, CargoObject struc) => Assert.Multiple(() => Assert.That(struc.PaymentIndex, Is.EqualTo(10), nameof(struc.PaymentIndex)); Assert.That(struc.UnitSize, Is.EqualTo(10), nameof(struc.UnitSize)); - Assert.That(obj.GraphicsElements, Has.Count.EqualTo(9)); + Assert.That(obj.ImageTable.GraphicsElements, Has.Count.EqualTo(9)); }); LoadSaveGenericTest(objectName, assertFunc); } @@ -269,7 +269,7 @@ void assertFunc(LocoObject obj, CliffEdgeObject _) => Assert.Multiple(() => Assert.That(entry[LanguageId.English_UK], Is.EqualTo("Brown Rock")); Assert.That(entry[LanguageId.English_US], Is.EqualTo("Brown Rock")); - Assert.That(obj.GraphicsElements, Has.Count.EqualTo(70)); + Assert.That(obj.ImageTable.GraphicsElements, Has.Count.EqualTo(70)); }); LoadSaveGenericTest(objectName, assertFunc); } @@ -287,7 +287,7 @@ void assertFunc(LocoObject obj, ClimateObject struc) => Assert.Multiple(() => Assert.That(struc.WinterSnowLine, Is.EqualTo(48), nameof(struc.WinterSnowLine)); Assert.That(struc.SummerSnowLine, Is.EqualTo(76), nameof(struc.SummerSnowLine)); - Assert.That(obj.GraphicsElements, Is.Empty); + Assert.That(obj.ImageTable.GraphicsElements, Is.Empty); }); LoadSaveGenericTest(objectName, assertFunc); } @@ -331,7 +331,7 @@ void assertFunc(LocoObject obj, CompetitorObject struc) => Assert.Multiple(() => Assert.That(struc.Competitiveness, Is.EqualTo(6), nameof(struc.Competitiveness)); Assert.That(struc.var_37, Is.Zero, nameof(struc.var_37)); - Assert.That(obj.GraphicsElements, Has.Count.EqualTo(18)); + Assert.That(obj.ImageTable.GraphicsElements, Has.Count.EqualTo(18)); }); LoadSaveGenericTest(objectName, assertFunc); } @@ -345,7 +345,7 @@ void assertFunc(LocoObject obj, CurrencyObject struc) => Assert.Multiple(() => Assert.That(struc.Separator, Is.Zero, nameof(struc.Separator)); Assert.That(struc.Factor, Is.EqualTo(1), nameof(struc.Factor)); - Assert.That(obj.GraphicsElements, Has.Count.EqualTo(5)); + Assert.That(obj.ImageTable.GraphicsElements, Has.Count.EqualTo(5)); }); LoadSaveGenericTest(objectName, assertFunc); } @@ -372,7 +372,7 @@ void assertFunc(LocoObject obj, DockObject struc) => Assert.Multiple(() => Assert.That(struc.ObsoleteYear, Is.EqualTo(65535), nameof(struc.ObsoleteYear)); Assert.That(struc.BoatPosition, Is.EqualTo(new Pos2(48, 0)), nameof(struc.BoatPosition)); - Assert.That(obj.GraphicsElements, Has.Count.EqualTo(9)); + Assert.That(obj.ImageTable.GraphicsElements, Has.Count.EqualTo(9)); }); LoadSaveGenericTest(objectName, assertFunc); } @@ -387,7 +387,7 @@ void assertFunc(LocoObject obj, HillShapesObject struc) => Assert.Multiple(() => //Assert.That(struc.var_08, Is.EqualTo(0), nameof(struc.var_08)); Assert.That(struc.IsHeightMap, Is.EqualTo(false), nameof(struc.IsHeightMap)); - Assert.That(obj.GraphicsElements, Has.Count.EqualTo(5)); + Assert.That(obj.ImageTable.GraphicsElements, Has.Count.EqualTo(5)); }); LoadSaveGenericTest(objectName, assertFunc); } @@ -487,7 +487,7 @@ void assertFunc(LocoObject obj, IndustryObject struc) => Assert.Multiple(() => Assert.That(struc.BuildingWallEntrance!.Name, Is.EqualTo("SECFENCG")); Assert.That(struc.BuildingWallEntrance.ObjectType, Is.EqualTo(ObjectType.Wall)); - Assert.That(obj.GraphicsElements, Has.Count.EqualTo(61)); + Assert.That(obj.ImageTable.GraphicsElements, Has.Count.EqualTo(61)); }); LoadSaveGenericTest(objectName, assertFunc); } @@ -569,7 +569,7 @@ void assertFunc(LocoObject obj, IndustryObject struc) => Assert.Multiple(() => Assert.That(struc.BuildingWall, Is.Null); Assert.That(struc.BuildingWallEntrance, Is.Null); - Assert.That(obj.GraphicsElements, Has.Count.EqualTo(37)); + Assert.That(obj.ImageTable.GraphicsElements, Has.Count.EqualTo(37)); }); LoadSaveGenericTest(objectName, assertFunc); } @@ -598,7 +598,7 @@ void assertFunc(LocoObject obj, InterfaceSkinObject struc) => Assert.Multiple(() Assert.That(struc.PlayerInfoToolbarColour, Is.EqualTo(Colour.Grey), nameof(struc.PlayerInfoToolbarColour)); Assert.That(struc.TimeToolbarColour, Is.EqualTo(Colour.Grey), nameof(struc.TimeToolbarColour)); - Assert.That(obj.GraphicsElements, Has.Count.EqualTo(470)); + Assert.That(obj.ImageTable.GraphicsElements, Has.Count.EqualTo(470)); }); LoadSaveGenericTest(objectName, assertFunc); } @@ -625,7 +625,7 @@ void assertFunc(LocoObject obj, LandObject struc) => Assert.Multiple(() => Assert.That(struc.UnkObjectHeader, Is.Null, nameof(struc.UnkObjectHeader)); - Assert.That(obj.GraphicsElements, Has.Count.EqualTo(417)); + Assert.That(obj.ImageTable.GraphicsElements, Has.Count.EqualTo(417)); }); LoadSaveGenericTest(objectName, assertFunc); } @@ -648,7 +648,7 @@ void assertFunc(LocoObject obj, LevelCrossingObject struc) => Assert.Multiple(() Assert.That(struc.DesignedYear, Is.EqualTo(1955), nameof(struc.DesignedYear)); - Assert.That(obj.GraphicsElements, Has.Count.EqualTo(128)); + Assert.That(obj.ImageTable.GraphicsElements, Has.Count.EqualTo(128)); }); LoadSaveGenericTest(objectName, assertFunc); } @@ -663,7 +663,7 @@ void assertFunc(LocoObject obj, RegionObject struc) => Assert.Multiple(() => Assert.That(struc.DependentObjects, Has.Count.EqualTo(239), nameof(struc.DependentObjects)); Assert.That(struc.CargoInfluenceTownFilter, Is.EquivalentTo(Enumerable.Repeat(CargoInfluenceTownFilterType.AllTowns, 4)), nameof(struc.CargoInfluenceTownFilter)); - Assert.That(obj.GraphicsElements, Has.Count.EqualTo(1)); + Assert.That(obj.ImageTable.GraphicsElements, Has.Count.EqualTo(1)); }); LoadSaveGenericTest(objectName, assertFunc); } @@ -680,7 +680,7 @@ void assertFunc(LocoObject obj, RoadExtraObject struc) => Assert.Multiple(() => Assert.That(struc.SellCostFactor, Is.EqualTo(-3), nameof(struc.SellCostFactor)); //Assert.That(struc.BaseImageOffset, Is.Zero, nameof(struc.BaseImageOffset)); - Assert.That(obj.GraphicsElements, Has.Count.EqualTo(46)); + Assert.That(obj.ImageTable.GraphicsElements, Has.Count.EqualTo(46)); }); LoadSaveGenericTest(objectName, assertFunc); } @@ -756,7 +756,7 @@ void assertFunc(LocoObject obj, RoadObject struc) => Assert.Multiple(() => Assert.That(struc.Tunnel.ObjectSource, Is.EqualTo(ObjectSource.Custom), nameof(struc.Tunnel.ObjectSource)); Assert.That(struc.Tunnel.ObjectType, Is.EqualTo(ObjectType.Tunnel), nameof(struc.Tunnel.ObjectType)); - Assert.That(obj.GraphicsElements, Has.Count.EqualTo(73)); + Assert.That(obj.ImageTable.GraphicsElements, Has.Count.EqualTo(73)); }); LoadSaveGenericTest(objectName, assertFunc); } @@ -779,7 +779,7 @@ void assertFunc(LocoObject obj, RoadStationObject struc) => Assert.Multiple(() = Assert.That(struc.RoadPieces, Is.EqualTo(RoadTraitFlags.None), nameof(struc.RoadPieces)); Assert.That(struc.SellCostFactor, Is.EqualTo(-17), nameof(struc.SellCostFactor)); - Assert.That(obj.GraphicsElements, Has.Count.EqualTo(18)); + Assert.That(obj.ImageTable.GraphicsElements, Has.Count.EqualTo(18)); }); LoadSaveGenericTest(objectName, assertFunc); } @@ -797,7 +797,7 @@ void assertFunc(LocoObject obj, ScaffoldingObject struc) => Assert.Multiple(() = Assert.That(struc.RoofHeights[1], Is.Zero, nameof(struc.RoofHeights) + "[1]"); Assert.That(struc.RoofHeights[2], Is.EqualTo(14), nameof(struc.RoofHeights) + "[2]"); - Assert.That(obj.GraphicsElements, Has.Count.EqualTo(36)); + Assert.That(obj.ImageTable.GraphicsElements, Has.Count.EqualTo(36)); }); LoadSaveGenericTest(objectName, assertFunc); } @@ -807,7 +807,7 @@ public void ScenarioTextObject(string objectName) { void assertFunc(LocoObject obj, ScenarioTextObject struc) => Assert.Multiple(() => { - Assert.That(obj.GraphicsElements, Is.Empty); + Assert.That(obj.ImageTable.GraphicsElements, Is.Empty); }); LoadSaveGenericTest(objectName, assertFunc); } @@ -818,9 +818,9 @@ public void SnowObject(string objectName) void assertFunc(LocoObject obj, SnowObject struc) => Assert.Multiple(() => { Assert.That(obj.StringTable.Table, Has.Count.EqualTo(1), nameof(obj.StringTable.Table)); - Assert.That(obj.GraphicsElements, Has.Count.EqualTo(139), nameof(obj.GraphicsElements)); + Assert.That(obj.ImageTable.GraphicsElements, Has.Count.EqualTo(139), nameof(obj.ImageTable.GraphicsElements)); - Assert.That(obj.GraphicsElements, Has.Count.EqualTo(139)); + Assert.That(obj.ImageTable.GraphicsElements, Has.Count.EqualTo(139)); }); LoadSaveGenericTest(objectName, assertFunc); } @@ -847,7 +847,7 @@ void assertFunc(LocoObject obj, SoundObject struc) => Assert.Multiple(() => Assert.That(struc.SoundObjectData.PcmHeader.SampleRate, Is.EqualTo(22050), nameof(struc.SoundObjectData.PcmHeader.SampleRate)); Assert.That(struc.SoundObjectData.PcmHeader.WaveFormatTag, Is.EqualTo(1), nameof(struc.SoundObjectData.PcmHeader.WaveFormatTag)); - Assert.That(obj.GraphicsElements, Is.Empty); + Assert.That(obj.ImageTable.GraphicsElements, Is.Empty); }); LoadSaveGenericTest(objectName, assertFunc); } @@ -874,7 +874,7 @@ void assertFunc(LocoObject obj, SteamObject struc) => Assert.Multiple(() => Assert.That(struc.var_0A, Is.Zero, nameof(struc.var_0A)); // SoundEffects - Assert.That(obj.GraphicsElements, Has.Count.EqualTo(57)); + Assert.That(obj.ImageTable.GraphicsElements, Has.Count.EqualTo(57)); }); LoadSaveGenericTest(objectName, assertFunc); } @@ -891,7 +891,7 @@ void assertFunc(LocoObject obj, StreetLightObject struc) => Assert.Multiple(() = Assert.That(obj.StringTable["Name"][LanguageId.English_UK], Is.EqualTo("Street Lights")); Assert.That(obj.StringTable["Name"][LanguageId.English_US], Is.EqualTo("Street Lights")); - Assert.That(obj.GraphicsElements, Has.Count.EqualTo(12)); + Assert.That(obj.ImageTable.GraphicsElements, Has.Count.EqualTo(12)); }); LoadSaveGenericTest(objectName, assertFunc); } @@ -928,7 +928,7 @@ void assertFunc(LocoObject obj, TownNamesObject struc) => Assert.Multiple(() => Assert.That(obj.StringTable["Name"][LanguageId.English_UK], Is.EqualTo("North-American style town names")); Assert.That(obj.StringTable["Name"][LanguageId.English_US], Is.EqualTo("North-American style town names")); - Assert.That(obj.GraphicsElements, Is.Empty); + Assert.That(obj.ImageTable.GraphicsElements, Is.Empty); }); LoadSaveGenericTest(objectName, assertFunc); } @@ -944,7 +944,7 @@ void assertFunc(LocoObject obj, TrackExtraObject struc) => Assert.Multiple(() => Assert.That(struc.BuildCostFactor, Is.EqualTo(2), nameof(struc.BuildCostFactor)); Assert.That(struc.SellCostFactor, Is.EqualTo(-1), nameof(struc.SellCostFactor)); - Assert.That(obj.GraphicsElements, Has.Count.EqualTo(134)); + Assert.That(obj.ImageTable.GraphicsElements, Has.Count.EqualTo(134)); }); LoadSaveGenericTest(objectName, assertFunc); } @@ -978,10 +978,10 @@ void assertFunc(LocoObject obj, TrackObject struc) => Assert.Multiple(() => Assert.That(struc.var_06, Is.Zero, nameof(struc.var_06)); Assert.That(obj.StringTable.Table, Has.Count.EqualTo(1), nameof(obj.StringTable.Table)); - Assert.That(obj.GraphicsElements, Is.Not.Null); - Assert.That(obj.GraphicsElements, Has.Count.EqualTo(400), nameof(obj.GraphicsElements)); + Assert.That(obj.ImageTable.GraphicsElements, Is.Not.Null); + Assert.That(obj.ImageTable.GraphicsElements, Has.Count.EqualTo(400), nameof(obj.ImageTable.GraphicsElements)); - Assert.That(obj.GraphicsElements, Has.Count.EqualTo(400)); + Assert.That(obj.ImageTable.GraphicsElements, Has.Count.EqualTo(400)); }); LoadSaveGenericTest(objectName, assertFunc); } @@ -1003,7 +1003,7 @@ void assertFunc(LocoObject obj, TrackSignalObject struc) => Assert.Multiple(() = Assert.That(struc.SellCostFactor, Is.EqualTo(-3), nameof(struc.SellCostFactor)); Assert.That(struc.var_0B, Is.Zero, nameof(struc.var_0B)); - Assert.That(obj.GraphicsElements, Has.Count.EqualTo(56)); + Assert.That(obj.ImageTable.GraphicsElements, Has.Count.EqualTo(56)); }); LoadSaveGenericTest(objectName, assertFunc); } @@ -1029,7 +1029,7 @@ void assertFunc(LocoObject obj, TrackStationObject struc) => Assert.Multiple(() Assert.That(struc.var_0B, Is.EqualTo(2), nameof(struc.var_0B)); Assert.That(struc.var_0D, Is.Zero, nameof(struc.var_0D)); - Assert.That(obj.GraphicsElements, Has.Count.EqualTo(36)); + Assert.That(obj.ImageTable.GraphicsElements, Has.Count.EqualTo(36)); }); LoadSaveGenericTest(objectName, assertFunc); } @@ -1059,7 +1059,7 @@ void assertFunc(LocoObject obj, TreeObject struc) => Assert.Multiple(() => Assert.That(struc.Rating, Is.EqualTo(10), nameof(struc.Rating)); Assert.That(struc.DemolishRatingReduction, Is.EqualTo(-15), nameof(struc.DemolishRatingReduction)); - Assert.That(obj.GraphicsElements, Has.Count.EqualTo(32)); + Assert.That(obj.ImageTable.GraphicsElements, Has.Count.EqualTo(32)); }); LoadSaveGenericTest(objectName, assertFunc); } @@ -1069,7 +1069,7 @@ public void TunnelObject(string objectName) { void assertFunc(LocoObject obj, TunnelObject struc) => Assert.Multiple(() => { - Assert.That(obj.GraphicsElements, Has.Count.EqualTo(4)); + Assert.That(obj.ImageTable.GraphicsElements, Has.Count.EqualTo(4)); }); LoadSaveGenericTest(objectName, assertFunc); } @@ -1142,7 +1142,7 @@ void assertFunc(LocoObject obj, VehicleObject struc) => Assert.Multiple(() => Assert.That(struc.StartSounds[1].ObjectSource, Is.EqualTo(ObjectSource.Custom), nameof(struc.StartSounds) + "[1]Checksum"); Assert.That(struc.StartSounds[1].ObjectType, Is.EqualTo(ObjectType.Sound), nameof(struc.StartSounds) + "[1]Flags"); - Assert.That(obj.GraphicsElements, Has.Count.EqualTo(168)); + Assert.That(obj.ImageTable.GraphicsElements, Has.Count.EqualTo(168)); }); LoadSaveGenericTest(objectName, assertFunc); } @@ -1157,7 +1157,7 @@ void assertFunc(LocoObject obj, WallObject struc) => Assert.Multiple(() => Assert.That(struc.Height, Is.EqualTo(2), nameof(struc.Height)); Assert.That(struc.Flags2, Is.EqualTo(WallObjectFlags2.Opaque), nameof(struc.Flags2)); - Assert.That(obj.GraphicsElements, Has.Count.EqualTo(6)); + Assert.That(obj.ImageTable.GraphicsElements, Has.Count.EqualTo(6)); }); LoadSaveGenericTest(objectName, assertFunc); } @@ -1172,7 +1172,7 @@ void assertFunc(LocoObject obj, WaterObject struc) => Assert.Multiple(() => Assert.That(struc.CostFactor, Is.EqualTo(51), nameof(struc.CostFactor)); //Assert.That(struc.var_0A, Is.EqualTo(0), nameof(struc.var_0A)); - Assert.That(obj.GraphicsElements, Has.Count.EqualTo(76)); + Assert.That(obj.ImageTable.GraphicsElements, Has.Count.EqualTo(76)); }); LoadSaveGenericTest(objectName, assertFunc); } From abaaa582fa4e8b2ee6c392dc33c45a5220c51e42 Mon Sep 17 00:00:00 2001 From: Benjamin Sutas Date: Fri, 29 Aug 2025 13:29:49 +1000 Subject: [PATCH 02/21] building example --- Dat/Loaders/BuildingObjectLoader.cs | 29 ++++++++++-- Definitions/ObjectModels/ImageTable.cs | 2 +- Definitions/ObjectModels/LocoObject.cs | 13 +++--- .../SubObjectTypes/ImageTableViewModel.cs | 28 +++++++++--- Gui/Views/ImageTableView.axaml | 44 +++++++++++-------- Tests/G1Tests.cs | 6 +-- 6 files changed, 83 insertions(+), 39 deletions(-) diff --git a/Dat/Loaders/BuildingObjectLoader.cs b/Dat/Loaders/BuildingObjectLoader.cs index 9cfef602..11ae8bff 100644 --- a/Dat/Loaders/BuildingObjectLoader.cs +++ b/Dat/Loaders/BuildingObjectLoader.cs @@ -25,6 +25,11 @@ public static class StructSizes public const int BuildingPartAnimation = 0x02; } + public static class ImageGroups + { + public const int Base = 4; + } + public static LocoObject Load(Stream stream) { var initialStreamPosition = stream.Position; @@ -32,8 +37,6 @@ public static LocoObject Load(Stream stream) using (var br = new LocoBinaryReader(stream)) { var model = new BuildingObject(); - var stringTable = new StringTable(); - var imageTable = new List(); // fixed br.SkipStringId(); // Name offset, not part of object definition @@ -70,18 +73,36 @@ public static LocoObject Load(Stream stream) ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); // string table - stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType.Building), null); + var stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType.Building), null); // variable LoadVariable(br, model, numBuildingParts, numBuildingVariations, numElevatorSequences); // image table - imageTable = SawyerStreamReader.ReadImageTable(br).Table; + var imageList = SawyerStreamReader.ReadImageTable(br).Table; + + // define groups + var imageTable = CreateImageTable(imageList); return new LocoObject(ObjectType.Building, model, stringTable, imageTable); } } + private static ImageTable CreateImageTable(List imageList) + { + var imageTable = new ImageTable(); + + var chunks = imageList.Chunk(4); + imageTable.Groups.Add(("Base", chunks.First().ToList())); + var floorCount = 0; + foreach (var chunk in chunks.Skip(1)) + { + imageTable.Groups.Add(($"Floor {floorCount++}", chunk.ToList())); + } + + return imageTable; + } + private static void LoadVariable(LocoBinaryReader br, BuildingObject model, byte numBuildingParts, byte numBuildingVariations, byte numElevatorSequences) { model.BuildingHeights = br.ReadBuildingHeights(numBuildingParts); diff --git a/Definitions/ObjectModels/ImageTable.cs b/Definitions/ObjectModels/ImageTable.cs index 66b7889f..ee86e38e 100644 --- a/Definitions/ObjectModels/ImageTable.cs +++ b/Definitions/ObjectModels/ImageTable.cs @@ -8,7 +8,7 @@ public class ImageTable : IHasGraphicsElements public List GraphicsElements { get => [.. Groups.SelectMany(x => x.GraphicsElements)]; - set => Groups.Add((")", value)); + set => Groups.Add(("All", value)); } public List<(string Name, List GraphicsElements)> Groups { get; set; } = []; diff --git a/Definitions/ObjectModels/LocoObject.cs b/Definitions/ObjectModels/LocoObject.cs index b43a7462..1d3c9c2a 100644 --- a/Definitions/ObjectModels/LocoObject.cs +++ b/Definitions/ObjectModels/LocoObject.cs @@ -1,25 +1,24 @@ using Definitions.ObjectModels.Types; +using System.Text.RegularExpressions; namespace Definitions.ObjectModels; public class LocoObject { public LocoObject(ObjectType objectType, ILocoStruct obj, StringTable stringTable, List graphicsElements) + : this(objectType, obj, stringTable, new ImageTable { Groups = [("All", graphicsElements)] }) + { } + + public LocoObject(ObjectType objectType, ILocoStruct obj, StringTable stringTable, ImageTable imageTable) { ObjectType = objectType; Object = obj; StringTable = stringTable; - ImageTable = new ImageTable - { - Groups = [("All", graphicsElements)] - }; + ImageTable = imageTable; } public ObjectType ObjectType { get; init; } public ILocoStruct Object { get; set; } public StringTable StringTable { get; set; } public ImageTable ImageTable { get; set; } - - //public List GraphicsElements - //{ get => ImageTable.GraphicsElements; set => throw new NotImplementedException(); } } diff --git a/Gui/ViewModels/SubObjectTypes/ImageTableViewModel.cs b/Gui/ViewModels/SubObjectTypes/ImageTableViewModel.cs index 42c17ea2..76de9112 100644 --- a/Gui/ViewModels/SubObjectTypes/ImageTableViewModel.cs +++ b/Gui/ViewModels/SubObjectTypes/ImageTableViewModel.cs @@ -1,3 +1,4 @@ +using Avalonia.Controls; using Avalonia.Controls.Selection; using Avalonia.Threading; using Common; @@ -5,6 +6,7 @@ using Common.Logging; using Definitions.ObjectModels; using Definitions.ObjectModels.Types; +using Gui.Models; using ReactiveUI; using ReactiveUI.Fody.Helpers; using SixLabors.ImageSharp; @@ -75,15 +77,17 @@ public class ImageTableViewModel : ReactiveObject, IExtraContentViewModel public PaletteMap PaletteMap { get; init; } public readonly ILogger Logger; + [Reactive] + public ObservableCollection GroupedImageViewModels { get; set; } = []; + public ImageTableViewModel(ImageTable imageTable, IImageTableNameProvider imageNameProvider, PaletteMap paletteMap, ILogger logger) { ArgumentNullException.ThrowIfNull(paletteMap); - var index = 0; - foreach (var ge in imageTable.GraphicsElements) + foreach (var group in imageTable.Groups) { - ImageViewModels.Add(new ImageViewModel(ge, paletteMap)); - index++; + var imageViewModels = group.GraphicsElements.Select(ge => new ImageViewModel(ge, paletteMap)); + GroupedImageViewModels.Add(new GroupedImageViewModel(group.Name, imageViewModels)); } NameProvider = imageNameProvider; @@ -240,7 +244,7 @@ public async Task ReplaceImage() return; } - ImageViewModels[SelectedImageIndex].UnderlyingImage = Image.Load(filename); + ImageViewModels[SelectedImageIndex].UnderlyingImage = SixLabors.ImageSharp.Image.Load(filename); } // model stuff @@ -337,7 +341,7 @@ public async Task ImportImages(string directory) foreach (var (offset, i) in offsets.Select((x, i) => (x, i))) { var is1Pixel = string.IsNullOrEmpty(offset.Path); - var img = is1Pixel ? OnePixelTransparent : Image.Load(Path.Combine(directory, offset.Path)); + var img = is1Pixel ? OnePixelTransparent : SixLabors.ImageSharp.Image.Load(Path.Combine(directory, offset.Path)); var newOffset = is1Pixel ? offset with { Flags = GraphicsElementFlags.HasTransparency } : offset; var graphicsElement = GraphicsElementFromImage(newOffset, img, PaletteMap); graphicsElement.Name = string.IsNullOrEmpty(graphicsElement.Name) ? GetImageName(i) : graphicsElement.Name; @@ -406,3 +410,15 @@ public async Task ExportImages(string directory) await JsonFile.SerializeToFileAsync(offsets, offsetsFile); } } + +public class GroupedImageViewModel +{ + public string GroupName { get; set; } + public ObservableCollection Images { get; set; } + + public GroupedImageViewModel(string groupName, IEnumerable images) + { + GroupName = groupName; + Images = new ObservableCollection(images); + } +} diff --git a/Gui/Views/ImageTableView.axaml b/Gui/Views/ImageTableView.axaml index d65d8cbd..58a30a90 100644 --- a/Gui/Views/ImageTableView.axaml +++ b/Gui/Views/ImageTableView.axaml @@ -71,25 +71,33 @@ - - - - - - - + + - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - + + @@ -144,7 +152,7 @@ - + diff --git a/Tests/G1Tests.cs b/Tests/G1Tests.cs index 8b070022..54086cc1 100644 --- a/Tests/G1Tests.cs +++ b/Tests/G1Tests.cs @@ -28,12 +28,12 @@ public void LoadSaveLoadG1() { Assert.That(g1.G1Header.NumEntries, Is.EqualTo(g1a.G1Header.NumEntries)); Assert.That(g1.G1Header.TotalSize, Is.EqualTo(g1a.G1Header.TotalSize)); - Assert.That(g1.GraphicsElements, Has.Count.EqualTo(g1a.GraphicsElements.Count)); + Assert.That(g1.ImageTable.GraphicsElements, Has.Count.EqualTo(g1a.ImageTable.GraphicsElements.Count)); } using (Assert.EnterMultipleScope()) { - foreach (var (expected, actual, i) in g1.GraphicsElements.Zip(g1a.GraphicsElements).Select((item, i) => (item.First, item.Second, i))) + foreach (var (expected, actual, i) in g1.ImageTable.GraphicsElements.Zip(g1a.ImageTable.GraphicsElements).Select((item, i) => (item.First, item.Second, i))) { AssertG1ElementsEqual(expected, actual, i); } @@ -52,7 +52,7 @@ public void LoadSaveLoadG1() public void LoadSaveLoadG1_RLERunsGreaterThan127(int element) { var g1 = SawyerStreamReader.LoadG1(g1File, Logger); - var d1 = g1!.GraphicsElements[element]; + var d1 = g1!.ImageTable.GraphicsElements[element]; var e1 = SawyerStreamWriter.EncodeRLEImageData(d1); var dd1 = new DatG1Element32(0, d1.Width, d1.Height, d1.XOffset, d1.YOffset, (DatG1ElementFlags)d1.Flags, d1.ZoomOffset) { From c9047773a38cf175f3d68327f3d11f05ae06756a Mon Sep 17 00:00:00 2001 From: Benjamin Sutas Date: Fri, 29 Aug 2025 16:33:12 +1000 Subject: [PATCH 03/21] progress --- Dat/Converters/GraphicsElementConverter.cs | 1 + Dat/FileParsing/SawyerStreamReader.cs | 8 +- Dat/Loaders/RoadStationObjectLoader.cs | 1 + .../ObjectModels/IImageTableNameProvider.cs | 6 +- .../Objects/RoadStation/RoadStationObject.cs | 2 + .../TrackStation/TrackStationObject.cs | 2 +- Gui/App.axaml | 6 +- .../{SubObjectTypes => }/AudioViewModel.cs | 0 Gui/ViewModels/DatTypes/G1ViewModel.cs | 6 +- .../DatTypes/ObjectEditorViewModel.cs | 11 +- .../DatTypes/Objects/TrackStationViewModel.cs | 6 +- .../GraphicsElementJson.cs | 2 +- .../Graphics/GroupedImageViewModel.cs | 43 +++ .../ImageTableViewModel.cs | 352 ++++++++---------- .../ImageViewModel.cs | 2 +- .../IExtraContentViewModel.cs | 0 .../StringTableViewModel.cs | 0 Gui/Views/ImageTableView.axaml | 10 +- Gui/Views/ImageView.axaml | 7 +- Tests/Models/ImageTableModelTests.cs | 2 +- 20 files changed, 243 insertions(+), 224 deletions(-) rename Gui/ViewModels/{SubObjectTypes => }/AudioViewModel.cs (100%) rename Gui/ViewModels/{SubObjectTypes => Graphics}/GraphicsElementJson.cs (96%) create mode 100644 Gui/ViewModels/Graphics/GroupedImageViewModel.cs rename Gui/ViewModels/{SubObjectTypes => Graphics}/ImageTableViewModel.cs (50%) rename Gui/ViewModels/{SubObjectTypes => Graphics}/ImageViewModel.cs (99%) rename Gui/ViewModels/{SubObjectTypes => }/IExtraContentViewModel.cs (100%) rename Gui/ViewModels/{SubObjectTypes => }/StringTableViewModel.cs (100%) diff --git a/Dat/Converters/GraphicsElementConverter.cs b/Dat/Converters/GraphicsElementConverter.cs index 267f9e6d..162d2d80 100644 --- a/Dat/Converters/GraphicsElementConverter.cs +++ b/Dat/Converters/GraphicsElementConverter.cs @@ -1,4 +1,5 @@ using Dat.Types; +using Definitions.ObjectModels; using Definitions.ObjectModels.Types; namespace Dat.Converters; diff --git a/Dat/FileParsing/SawyerStreamReader.cs b/Dat/FileParsing/SawyerStreamReader.cs index a1966406..ac3cce8e 100644 --- a/Dat/FileParsing/SawyerStreamReader.cs +++ b/Dat/FileParsing/SawyerStreamReader.cs @@ -252,6 +252,8 @@ public static (G1Header Header, List Table) ReadImageTable(Loco var imageData = br.ReadToEnd(); g1Header.ImageData = [.. imageData]; + var graphicsElements = new List(); + // set image data for (var i = 0; i < g1Header.NumEntries; ++i) { @@ -278,9 +280,13 @@ public static (G1Header Header, List Table) ReadImageTable(Loco { currElement.ImageData = DecodeRLEImageData(currElement); } + + var ge = currElement.Convert(); + ge.Name = DefaultImageTableNameProvider.GetImageName(i++); + graphicsElements.Add(ge); } - return (g1Header, g1Element32s.Select(x => x.Convert()).ToList()); + return (g1Header, graphicsElements); } static uint GetNextNonDuplicateOffset(List g1Element32s, int i, uint imageDateLength) diff --git a/Dat/Loaders/RoadStationObjectLoader.cs b/Dat/Loaders/RoadStationObjectLoader.cs index 1be7c4fc..d90aca6c 100644 --- a/Dat/Loaders/RoadStationObjectLoader.cs +++ b/Dat/Loaders/RoadStationObjectLoader.cs @@ -15,6 +15,7 @@ public static class Constants public const int MaxImageOffsets = 4; public const int MaxNumCompatible = 7; public const int CargoOffsetBytesSize = 16; + public const int MaxStationCargoDensity = 15; } public static class StructSizes diff --git a/Definitions/ObjectModels/IImageTableNameProvider.cs b/Definitions/ObjectModels/IImageTableNameProvider.cs index e76b2346..bdbd8b83 100644 --- a/Definitions/ObjectModels/IImageTableNameProvider.cs +++ b/Definitions/ObjectModels/IImageTableNameProvider.cs @@ -1,4 +1,5 @@ using Definitions.ObjectModels.Types; +using System; namespace Definitions.ObjectModels; @@ -16,7 +17,10 @@ public class DefaultImageTableNameProvider : IImageTableNameProvider { public bool TryGetImageName(int id, out string? value) { - value = id.ToString(); + value = GetImageName(id); return true; } + + public static string GetImageName(int id) + => $"{id}-unnamed"; } diff --git a/Definitions/ObjectModels/Objects/RoadStation/RoadStationObject.cs b/Definitions/ObjectModels/Objects/RoadStation/RoadStationObject.cs index 0df8f4e9..ae490c30 100644 --- a/Definitions/ObjectModels/Objects/RoadStation/RoadStationObject.cs +++ b/Definitions/ObjectModels/Objects/RoadStation/RoadStationObject.cs @@ -1,4 +1,5 @@ using Definitions.ObjectModels.Objects.Road; +using Definitions.ObjectModels.Objects.TrackStation; using Definitions.ObjectModels.Types; namespace Definitions.ObjectModels.Objects.RoadStation; @@ -21,6 +22,7 @@ public class RoadStationObject : ILocoStruct, IImageTableNameProvider // for drawing the cargo on the station public uint8_t[][][] CargoOffsetBytes { get; set; } + //public CargoOffset[] CargoOffsets { get; init; } = [.. Enumerable.Repeat(new CargoOffset { A = Pos3.Zero, B = Pos3.Zero }, 15)]; public bool Validate() { diff --git a/Definitions/ObjectModels/Objects/TrackStation/TrackStationObject.cs b/Definitions/ObjectModels/Objects/TrackStation/TrackStationObject.cs index 6c07c3f3..fcde4ad4 100644 --- a/Definitions/ObjectModels/Objects/TrackStation/TrackStationObject.cs +++ b/Definitions/ObjectModels/Objects/TrackStation/TrackStationObject.cs @@ -19,7 +19,7 @@ public class TrackStationObject : ILocoStruct, IImageTableNameProvider public List CompatibleTrackObjects { get; set; } = []; public uint8_t[][][] CargoOffsetBytes { get; set; } public uint8_t[][] var_6E { get; set; } - public CargoOffset[] CargoOffsets { get; init; } = [.. Enumerable.Repeat(new CargoOffset { A = Pos3.Zero, B = Pos3.Zero }, 15)]; + //public List CargoOffsets { get; init; } = []; //.. Enumerable.Repeat(new CargoOffset { A = Pos3.Zero, B = Pos3.Zero }, 15)]; public bool Validate() { diff --git a/Gui/App.axaml b/Gui/App.axaml index 8bd96c11..3c8dd1dd 100644 --- a/Gui/App.axaml +++ b/Gui/App.axaml @@ -6,7 +6,7 @@ xmlns:vm="using:Gui.ViewModels" xmlns:vi="using:Gui.Views" xmlns:oldt="using:Dat.Types" - xmlns:pgc="clr-namespace:Avalonia.PropertyGrid.Controls;assembly=Avalonia.PropertyGrid" + xmlns:pgc="clr-namespace:Avalonia.PropertyGrid.Controls;assembly=Avalonia.PropertyGrid" x:Class="Gui.App" RequestedThemeVariant="Default"> @@ -28,10 +28,10 @@ - + - + diff --git a/Gui/ViewModels/SubObjectTypes/AudioViewModel.cs b/Gui/ViewModels/AudioViewModel.cs similarity index 100% rename from Gui/ViewModels/SubObjectTypes/AudioViewModel.cs rename to Gui/ViewModels/AudioViewModel.cs diff --git a/Gui/ViewModels/DatTypes/G1ViewModel.cs b/Gui/ViewModels/DatTypes/G1ViewModel.cs index 10ecd0c5..01b274fc 100644 --- a/Gui/ViewModels/DatTypes/G1ViewModel.cs +++ b/Gui/ViewModels/DatTypes/G1ViewModel.cs @@ -1,6 +1,6 @@ using Dat.FileParsing; -using Definitions.ObjectModels.Objects.Currency; using Gui.Models; +using Gui.ViewModels.Graphics; using ReactiveUI.Fody.Helpers; using System.IO; using System.Linq; @@ -27,7 +27,7 @@ public override void Load() return; } - ImageTableViewModel = new ImageTableViewModel(Model.G1.ImageTable, Model.G1, Model.PaletteMap, logger); + ImageTableViewModel = new ImageTableViewModel(Model.G1.ImageTable, Model.PaletteMap, logger); } public override void Save() @@ -38,7 +38,7 @@ public override void Save() return; } - Model.G1.ImageTable.GraphicsElements = [.. ImageTableViewModel.ImageViewModels.Select(x => x.ToGraphicsElement())]; + //Model.G1.ImageTable.GraphicsElements = [.. ImageTableViewModel.ImageViewModels.Select(x => x.ToGraphicsElement())]; var savePath = CurrentFile.FileLocation == FileLocation.Local ? Path.Combine(Model.Settings.ObjDataDirectory, CurrentFile.FileName) diff --git a/Gui/ViewModels/DatTypes/ObjectEditorViewModel.cs b/Gui/ViewModels/DatTypes/ObjectEditorViewModel.cs index c04468c2..4b275f88 100644 --- a/Gui/ViewModels/DatTypes/ObjectEditorViewModel.cs +++ b/Gui/ViewModels/DatTypes/ObjectEditorViewModel.cs @@ -8,6 +8,7 @@ using Definitions.ObjectModels.Objects.Sound; using Gui.Models; using Gui.Models.Audio; +using Gui.ViewModels.Graphics; using Gui.Views; using ReactiveUI; using ReactiveUI.Fody.Helpers; @@ -152,13 +153,13 @@ public override void Load() CurrentObjectViewModel = GetViewModelFromStruct(CurrentObject.LocoObject.Object); StringTableViewModel = new(CurrentObject.LocoObject.StringTable); - var imageNameProvider = (CurrentObject.LocoObject.Object is IImageTableNameProvider itnp) - ? itnp - : new DefaultImageTableNameProvider(); + //var imageNameProvider = (CurrentObject.LocoObject.Object is IImageTableNameProvider itnp) + // ? itnp + // : new DefaultImageTableNameProvider(); ExtraContentViewModel = CurrentObject.LocoObject.Object is SoundObject soundObject ? new AudioViewModel(logger, CurrentObject.DatFileInfo.S5Header.Name, soundObject.SoundObjectData.PcmHeader, soundObject.PcmData) - : new ImageTableViewModel(CurrentObject.LocoObject.ImageTable, imageNameProvider, Model.PaletteMap, Model.Logger); + : new ImageTableViewModel(CurrentObject.LocoObject.ImageTable, Model.PaletteMap, Model.Logger); } else { @@ -276,7 +277,7 @@ void SaveCore(string filename, SawyerEncoding? encodingToUse = null) if (ExtraContentViewModel is ImageTableViewModel itvm) { - CurrentObject.LocoObject.ImageTable.GraphicsElements = itvm.ImageViewModels.Select(x => x.ToGraphicsElement()).ToList(); + //CurrentObject.LocoObject.ImageTable.GraphicsElements = itvm.ImageViewModels.Select(x => x.ToGraphicsElement()).ToList(); } // this is hacky but it should work diff --git a/Gui/ViewModels/DatTypes/Objects/TrackStationViewModel.cs b/Gui/ViewModels/DatTypes/Objects/TrackStationViewModel.cs index 613c5995..3c4767f0 100644 --- a/Gui/ViewModels/DatTypes/Objects/TrackStationViewModel.cs +++ b/Gui/ViewModels/DatTypes/Objects/TrackStationViewModel.cs @@ -30,7 +30,7 @@ public class TrackStationViewModel : LocoObjectViewModel [Browsable(false)] public uint8_t[][] var_6E { get; set; } - public CargoOffset[] CargoOffsets { get; init; } = [.. Enumerable.Repeat(new CargoOffset() { A = Pos3.Zero, B = Pos3.Zero }, 15)]; + public List CargoOffsets { get; set; } public TrackStationViewModel(TrackStationObject tso) { @@ -47,7 +47,7 @@ public TrackStationViewModel(TrackStationObject tso) var_0D = tso.var_0D; CargoOffsetBytes = tso.CargoOffsetBytes; var_6E = tso.var_6E; - CargoOffsets = tso.CargoOffsets; + //CargoOffsets = tso.CargoOffsets; CompatibleTrackObjects = [.. tso.CompatibleTrackObjects.ConvertAll(x => new ObjectModelHeaderViewModel(x))]; } @@ -69,7 +69,7 @@ public override TrackStationObject GetAsModel() var_0D = var_0D, CargoOffsetBytes = CargoOffsetBytes, var_6E = var_6E, - CargoOffsets = CargoOffsets, + //CargoOffsets = CargoOffsets, CompatibleTrackObjects = CompatibleTrackObjects.ConvertAll(x => x.GetAsModel()), }; } diff --git a/Gui/ViewModels/SubObjectTypes/GraphicsElementJson.cs b/Gui/ViewModels/Graphics/GraphicsElementJson.cs similarity index 96% rename from Gui/ViewModels/SubObjectTypes/GraphicsElementJson.cs rename to Gui/ViewModels/Graphics/GraphicsElementJson.cs index efb1a712..a832af84 100644 --- a/Gui/ViewModels/SubObjectTypes/GraphicsElementJson.cs +++ b/Gui/ViewModels/Graphics/GraphicsElementJson.cs @@ -1,7 +1,7 @@ using Definitions.ObjectModels.Types; using System.Text.Json.Serialization; -namespace Gui.ViewModels; +namespace Gui.ViewModels.Graphics; public record GraphicsElementJson( [property: JsonPropertyName("path")] string Path, diff --git a/Gui/ViewModels/Graphics/GroupedImageViewModel.cs b/Gui/ViewModels/Graphics/GroupedImageViewModel.cs new file mode 100644 index 00000000..ac7dfc91 --- /dev/null +++ b/Gui/ViewModels/Graphics/GroupedImageViewModel.cs @@ -0,0 +1,43 @@ +using Avalonia.Controls.Selection; +using ReactiveUI; +using ReactiveUI.Fody.Helpers; +using System.Collections.Generic; +using System.Collections.ObjectModel; + +namespace Gui.ViewModels.Graphics; + +public class GroupedImageViewModel : ReactiveObject +{ + public string GroupName { get; set; } + public ObservableCollection Images { get; set; } + + [Reactive] + public SelectionModel SelectionModel { get; set; } + + public GroupedImageViewModel(string groupName, IEnumerable images) + { + GroupName = groupName; + Images = new ObservableCollection(images); + + SelectionModel = new SelectionModel + { + SingleSelect = false + }; + //SelectionModel.SelectionChanged += SelectionChanged; + } + + //void SelectionChanged(object sender, SelectionModelSelectionChangedEventArgs e) + //{ + // var sm = (SelectionModel)sender; + + // if (sm.SelectedIndexes.Count > 0) + // { + // //SelectedImageIndex = sm.SelectedIndex; + // } + + // if (sm.SelectedItems.Count == 0) + // { + // return; + // } + //} +} diff --git a/Gui/ViewModels/SubObjectTypes/ImageTableViewModel.cs b/Gui/ViewModels/Graphics/ImageTableViewModel.cs similarity index 50% rename from Gui/ViewModels/SubObjectTypes/ImageTableViewModel.cs rename to Gui/ViewModels/Graphics/ImageTableViewModel.cs index 76de9112..ea9d0013 100644 --- a/Gui/ViewModels/SubObjectTypes/ImageTableViewModel.cs +++ b/Gui/ViewModels/Graphics/ImageTableViewModel.cs @@ -1,4 +1,3 @@ -using Avalonia.Controls; using Avalonia.Controls.Selection; using Avalonia.Threading; using Common; @@ -6,7 +5,6 @@ using Common.Logging; using Definitions.ObjectModels; using Definitions.ObjectModels.Types; -using Gui.Models; using ReactiveUI; using ReactiveUI.Fody.Helpers; using SixLabors.ImageSharp; @@ -20,7 +18,7 @@ using System.Threading.Tasks; using System.Windows.Input; -namespace Gui.ViewModels; +namespace Gui.ViewModels.Graphics; public class ImageTableViewModel : ReactiveObject, IExtraContentViewModel { @@ -55,13 +53,6 @@ public class ImageTableViewModel : ReactiveObject, IExtraContentViewModel [Reactive] public ICommand CenterOffsetAllImagesCommand { get; set; } - // what is displaying on the ui - [Reactive] - public ObservableCollection ImageViewModels { get; set; } = []; - - [Reactive] - public int SelectedImageIndex { get; set; } = -1; - [Reactive] public SelectionModel SelectionModel { get; set; } @@ -73,45 +64,39 @@ public class ImageTableViewModel : ReactiveObject, IExtraContentViewModel readonly DispatcherTimer animationTimer; int currentFrameIndex; - public readonly IImageTableNameProvider NameProvider; // Can remove this when we put name inside of GraphicsElement public PaletteMap PaletteMap { get; init; } public readonly ILogger Logger; [Reactive] public ObservableCollection GroupedImageViewModels { get; set; } = []; - public ImageTableViewModel(ImageTable imageTable, IImageTableNameProvider imageNameProvider, PaletteMap paletteMap, ILogger logger) + public ImageTableViewModel(ImageTable imageTable, PaletteMap paletteMap, ILogger logger) { ArgumentNullException.ThrowIfNull(paletteMap); foreach (var group in imageTable.Groups) { var imageViewModels = group.GraphicsElements.Select(ge => new ImageViewModel(ge, paletteMap)); - GroupedImageViewModels.Add(new GroupedImageViewModel(group.Name, imageViewModels)); + var givm = new GroupedImageViewModel(group.Name, imageViewModels); + givm.SelectionModel.SelectionChanged += SelectionChanged; + GroupedImageViewModels.Add(givm); } - NameProvider = imageNameProvider; PaletteMap = paletteMap; Logger = logger; - SelectionModel = new SelectionModel - { - SingleSelect = false - }; - SelectionModel.SelectionChanged += SelectionChanged; - _ = this.WhenAnyValue(o => o.SelectedPrimarySwatch).Skip(1) .Subscribe(_ => RecolourImages(SelectedPrimarySwatch, SelectedSecondarySwatch)); _ = this.WhenAnyValue(o => o.SelectedSecondarySwatch).Skip(1) .Subscribe(_ => RecolourImages(SelectedPrimarySwatch, SelectedSecondarySwatch)); - _ = this.WhenAnyValue(o => o.SelectedImageIndex) - .Subscribe(index => - { - SelectedImage = SelectedImageIndexIsValid() - ? ImageViewModels[SelectedImageIndex] - : null; - }); + //_ = this.WhenAnyValue(o => o.SelectedImageIndex) + // .Subscribe(index => + // { + // SelectedImage = SelectedImageIndexIsValid() + // ? ImageViewModels[SelectedImageIndex] + // : null; + // }); _ = this.WhenAnyValue(o => o.AnimationSpeed) .Where(_ => animationTimer != null) @@ -125,22 +110,28 @@ public ImageTableViewModel(ImageTable imageTable, IImageTableNameProvider imageN ZeroOffsetAllImagesCommand = ReactiveCommand.Create(() => { - foreach (var ivm in ImageViewModels) - { - ivm.XOffset = 0; - ivm.YOffset = 0; - } + //foreach (var ivm in ImageViewModels) + //{ + // ivm.XOffset = 0; + // ivm.YOffset = 0; + //} }); CenterOffsetAllImagesCommand = ReactiveCommand.Create(() => { - foreach (var ivm in ImageViewModels) - { - ivm.XOffset = (short)(-ivm.Width / 2); - ivm.YOffset = (short)(-ivm.Height / 2); - } + //foreach (var ivm in ImageViewModels) + //{ + // ivm.XOffset = (short)(-ivm.Width / 2); + // ivm.YOffset = (short)(-ivm.Height / 2); + //} }); + SelectionModel = new SelectionModel + { + SingleSelect = false + }; + SelectionModel.SelectionChanged += SelectionChanged; + // Set up the animation timer animationTimer = new DispatcherTimer { @@ -150,47 +141,40 @@ public ImageTableViewModel(ImageTable imageTable, IImageTableNameProvider imageN animationTimer.Start(); } - bool SelectedImageIndexIsValid() - => SelectedImageIndex >= 0 && SelectedImageIndex < ImageViewModels.Count; - - void SelectionChanged(object sender, SelectionModelSelectionChangedEventArgs e) - { - var sm = (SelectionModel)sender; - - if (sm.SelectedIndexes.Count > 0) - { - SelectedImageIndex = sm.SelectedIndex; - } - - if (sm.SelectedItems.Count == 0) - { - return; - } - } - void AnimationTimer_Tick(object? sender, EventArgs e) { - if (SelectionModel == null || SelectionModel.SelectedIndexes.Count == 0) + if (SelectionModel == null || SelectionModel.SelectedItems.Count == 0) { return; } - if (currentFrameIndex >= SelectionModel.SelectedIndexes.Count) + if (currentFrameIndex >= SelectionModel.SelectedItems.Count) { currentFrameIndex = 0; } // Update the displayed image viewmodel - SelectedImageIndex = SelectionModel.SelectedIndexes[currentFrameIndex]; // disabling this also makes the memory leaks stop + SelectedImage = SelectionModel.SelectedItems[currentFrameIndex]; // disabling this also makes the memory leaks stop // Move to the next frame, looping back to the beginning if necessary - currentFrameIndex = (currentFrameIndex + 1) % SelectionModel.SelectedIndexes.Count; + currentFrameIndex = (currentFrameIndex + 1) % SelectionModel.SelectedItems.Count; + } + + void SelectionChanged(object sender, SelectionModelSelectionChangedEventArgs e) + { + var sm = (SelectionModel)sender; + SelectionModel = sm; + SelectedImage = SelectionModel.SelectedItems.Count > 0 ? sm.SelectedItems[0] : null; } public void ClearSelectionModel() { + foreach (var givm in GroupedImageViewModels) + { + givm.SelectionModel.Clear(); + } + SelectionModel.Clear(); - SelectedImageIndex = -1; } public async Task ImportImages() @@ -227,11 +211,6 @@ public async Task ExportImages() public async Task ReplaceImage() { - if (SelectedImageIndex == -1) - { - return; - } - var openFile = await PlatformSpecific.OpenFilePicker(PlatformSpecific.PngFileTypes); if (openFile == null) { @@ -244,46 +223,41 @@ public async Task ReplaceImage() return; } - ImageViewModels[SelectedImageIndex].UnderlyingImage = SixLabors.ImageSharp.Image.Load(filename); + //ImageViewModels[SelectedImageIndex].UnderlyingImage = Image.Load(filename); } // model stuff public void RecolourImages(ColourRemapSwatch primary, ColourRemapSwatch secondary) { - if (SelectedImageIndexIsValid()) - { - return; - } - - foreach (var ivm in ImageViewModels) - { - ivm.RecolourImage(primary, secondary); - } + //if (SelectedImageIndexIsValid()) + //{ + // return; + //} + + //foreach (var ivm in ImageViewModels) + //{ + // ivm.RecolourImage(primary, secondary); + //} } public void CropImage() { - if (!SelectedImageIndexIsValid()) - { - return; - } + //if (!SelectedImageIndexIsValid()) + //{ + // return; + //} - ImageViewModels[SelectedImageIndex].CropImage(); + //ImageViewModels[SelectedImageIndex].CropImage(); } public void CropAllImages() { - foreach (var ivm in ImageViewModels) - { - ivm.CropImage(); - } + //foreach (var ivm in ImageViewModels) + //{ + // ivm.CropImage(); + //} } - public string GetImageName(int index) - => NameProvider.TryGetImageName(index, out var value) && !string.IsNullOrEmpty(value) - ? value - : $"{index}-unnamed"; - public static string TrimZeroes(string str) { var result = str.Trim().TrimStart('0'); @@ -292,68 +266,68 @@ public static string TrimZeroes(string str) public async Task ImportImages(string directory) { - if (string.IsNullOrEmpty(directory)) - { - Logger.Error($"Directory is invalid: \"{directory}\""); - return; - } - - if (!Directory.Exists(directory)) - { - Logger.Error($"Directory does not exist: \"{directory}\""); - return; - } - - Logger.Info($"Importing images from {directory}"); - - ClearSelectionModel(); - - try - { - Logger.Debug($"{ImageViewModels.Count} images in current object"); - ICollection offsets; - - // check for offsets file - var offsetsFile = Path.Combine(directory, "sprites.json"); - if (File.Exists(offsetsFile)) - { - offsets = await JsonFile.DeserializeFromFileAsync>(offsetsFile); // sprites.json is an unnamed array so we need ICollection here, not IEnumerable - ArgumentNullException.ThrowIfNull(offsets); - Logger.Debug($"Found sprites.json file with {offsets.Count} images"); - } - else - { - var files = Directory.GetFiles(directory, "*.png", SearchOption.AllDirectories); - var sanitised = files.Select(TrimZeroes).ToList(); - - offsets = [.. ImageViewModels - .Select((x, i) => new GraphicsElementJson(sanitised[i], (short)x.XOffset, (short)x.YOffset, string.Empty)) - .Fill(files.Length, GraphicsElementJson.Zero)]; - - Logger.Debug($"Didn't find sprites.json file, using existing GraphicsElement offsets with {offsets.Count} images"); - } - - // clear existing images - Logger.Debug("Clearing current images"); - ImageViewModels.Clear(); - - // load files - foreach (var (offset, i) in offsets.Select((x, i) => (x, i))) - { - var is1Pixel = string.IsNullOrEmpty(offset.Path); - var img = is1Pixel ? OnePixelTransparent : SixLabors.ImageSharp.Image.Load(Path.Combine(directory, offset.Path)); - var newOffset = is1Pixel ? offset with { Flags = GraphicsElementFlags.HasTransparency } : offset; - var graphicsElement = GraphicsElementFromImage(newOffset, img, PaletteMap); - graphicsElement.Name = string.IsNullOrEmpty(graphicsElement.Name) ? GetImageName(i) : graphicsElement.Name; - ImageViewModels.Add(new ImageViewModel(graphicsElement, PaletteMap)); - } - - Logger.Debug($"Imported {ImageViewModels.Count} images successfully"); - } - catch (Exception ex) - { - Logger.Error(ex); - } + //if (string.IsNullOrEmpty(directory)) + //{ + // Logger.Error($"Directory is invalid: \"{directory}\""); + // return; + //} + + //if (!Directory.Exists(directory)) + //{ + // Logger.Error($"Directory does not exist: \"{directory}\""); + // return; + //} + + //Logger.Info($"Importing images from {directory}"); + + //ClearSelectionModel(); + + //try + //{ + // Logger.Debug($"{ImageViewModels.Count} images in current object"); + // ICollection offsets; + + // // check for offsets file + // var offsetsFile = Path.Combine(directory, "sprites.json"); + // if (File.Exists(offsetsFile)) + // { + // offsets = await JsonFile.DeserializeFromFileAsync>(offsetsFile); // sprites.json is an unnamed array so we need ICollection here, not IEnumerable + // ArgumentNullException.ThrowIfNull(offsets); + // Logger.Debug($"Found sprites.json file with {offsets.Count} images"); + // } + // else + // { + // var files = Directory.GetFiles(directory, "*.png", SearchOption.AllDirectories); + // var sanitised = files.Select(TrimZeroes).ToList(); + + // offsets = [.. ImageViewModels + // .Select((x, i) => new GraphicsElementJson(sanitised[i], (short)x.XOffset, (short)x.YOffset, string.Empty)) + // .Fill(files.Length, GraphicsElementJson.Zero)]; + + // Logger.Debug($"Didn't find sprites.json file, using existing GraphicsElement offsets with {offsets.Count} images"); + // } + + // // clear existing images + // Logger.Debug("Clearing current images"); + // ImageViewModels.Clear(); + + // // load files + // foreach (var (offset, i) in offsets.Select((x, i) => (x, i))) + // { + // var is1Pixel = string.IsNullOrEmpty(offset.Path); + // var img = is1Pixel ? OnePixelTransparent : Image.Load(Path.Combine(directory, offset.Path)); + // var newOffset = is1Pixel ? offset with { Flags = GraphicsElementFlags.HasTransparency } : offset; + // var graphicsElement = GraphicsElementFromImage(newOffset, img, PaletteMap); + // graphicsElement.Name = string.IsNullOrEmpty(graphicsElement.Name) ? GetImageName(i) : graphicsElement.Name; + // ImageViewModels.Add(new ImageViewModel(graphicsElement, PaletteMap)); + // } + + // Logger.Debug($"Imported {ImageViewModels.Count} images successfully"); + //} + //catch (Exception ex) + //{ + // Logger.Error(ex); + //} } static GraphicsElement GraphicsElementFromImage(GraphicsElementJson ele, Image img, PaletteMap paletteMap) @@ -376,49 +350,37 @@ static GraphicsElement GraphicsElementFromImage(GraphicsElementJson ele, Image(); - - foreach (var image in ImageViewModels) - { - var imageName = counter.ToString(); // todo: maybe use image name provider below (but number must still exist) - counter++; - - var fileName = $"{imageName}.png"; - var path = Path.Combine(directory, fileName); - await image.UnderlyingImage.SaveAsPngAsync(path); - - offsets.Add(new GraphicsElementJson(fileName, image.ToGraphicsElement())); - } - - var offsetsFile = Path.Combine(directory, "sprites.json"); - Logger.Info($"Saving sprite offsets to {offsetsFile}"); - await JsonFile.SerializeToFileAsync(offsets, offsetsFile); - } -} - -public class GroupedImageViewModel -{ - public string GroupName { get; set; } - public ObservableCollection Images { get; set; } - - public GroupedImageViewModel(string groupName, IEnumerable images) - { - GroupName = groupName; - Images = new ObservableCollection(images); + //if (string.IsNullOrEmpty(directory)) + //{ + // Logger.Error($"Directory is invalid: \"{directory}\""); + // return; + //} + + //if (!Directory.Exists(directory)) + //{ + // Logger.Error($"Directory does not exist: \"{directory}\""); + // return; + //} + + //Logger.Info($"Exporting images to {directory}"); + + //var counter = 0; + //var offsets = new List(); + + //foreach (var image in ImageViewModels) + //{ + // var imageName = counter.ToString(); // todo: maybe use image name provider below (but number must still exist) + // counter++; + + // var fileName = $"{imageName}.png"; + // var path = Path.Combine(directory, fileName); + // await image.UnderlyingImage.SaveAsPngAsync(path); + + // offsets.Add(new GraphicsElementJson(fileName, image.ToGraphicsElement())); + //} + + //var offsetsFile = Path.Combine(directory, "sprites.json"); + //Logger.Info($"Saving sprite offsets to {offsetsFile}"); + //await JsonFile.SerializeToFileAsync(offsets, offsetsFile); } } diff --git a/Gui/ViewModels/SubObjectTypes/ImageViewModel.cs b/Gui/ViewModels/Graphics/ImageViewModel.cs similarity index 99% rename from Gui/ViewModels/SubObjectTypes/ImageViewModel.cs rename to Gui/ViewModels/Graphics/ImageViewModel.cs index e10c464b..5ce2e8b3 100644 --- a/Gui/ViewModels/SubObjectTypes/ImageViewModel.cs +++ b/Gui/ViewModels/Graphics/ImageViewModel.cs @@ -11,7 +11,7 @@ using System.ComponentModel; using System.Reactive.Linq; -namespace Gui.ViewModels; +namespace Gui.ViewModels.Graphics; public class ImageViewModel : ReactiveObject { diff --git a/Gui/ViewModels/SubObjectTypes/IExtraContentViewModel.cs b/Gui/ViewModels/IExtraContentViewModel.cs similarity index 100% rename from Gui/ViewModels/SubObjectTypes/IExtraContentViewModel.cs rename to Gui/ViewModels/IExtraContentViewModel.cs diff --git a/Gui/ViewModels/SubObjectTypes/StringTableViewModel.cs b/Gui/ViewModels/StringTableViewModel.cs similarity index 100% rename from Gui/ViewModels/SubObjectTypes/StringTableViewModel.cs rename to Gui/ViewModels/StringTableViewModel.cs diff --git a/Gui/Views/ImageTableView.axaml b/Gui/Views/ImageTableView.axaml index 58a30a90..bafd274c 100644 --- a/Gui/Views/ImageTableView.axaml +++ b/Gui/Views/ImageTableView.axaml @@ -3,7 +3,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:vm="using:Gui.ViewModels" + xmlns:vmg="using:Gui.ViewModels.Graphics" xmlns:materialIcons="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia" xmlns:pgc="clr-namespace:Avalonia.PropertyGrid.Controls;assembly=Avalonia.PropertyGrid" xmlns:amxc="using:Avalonia.Markup.Xaml.Converters" @@ -14,7 +14,7 @@ d:DesignWidth="1200" d:DesignHeight="900" x:Class="Gui.Views.ImageTableView" - x:DataType="vm:ImageTableViewModel"> + x:DataType="vmg:ImageTableViewModel"> @@ -75,7 +75,7 @@ - + @@ -86,8 +86,8 @@ - - + + diff --git a/Gui/Views/ImageView.axaml b/Gui/Views/ImageView.axaml index 916bd448..f67379ba 100644 --- a/Gui/Views/ImageView.axaml +++ b/Gui/Views/ImageView.axaml @@ -3,15 +3,14 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:vm="using:Gui.ViewModels" + xmlns:vmg="using:Gui.ViewModels.Graphics" xmlns:materialIcons="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia" - xmlns:pgc="clr-namespace:Avalonia.PropertyGrid.Controls;assembly=Avalonia.PropertyGrid" - + xmlns:pgc="clr-namespace:Avalonia.PropertyGrid.Controls;assembly=Avalonia.PropertyGrid" xmlns:amxc="using:Avalonia.Markup.Xaml.Converters" xmlns:gmc="using:Gui.Models.Converters" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="Gui.Views.ImageView" - x:DataType="vm:ImageViewModel"> + x:DataType="vmg:ImageViewModel"> diff --git a/Tests/Models/ImageTableModelTests.cs b/Tests/Models/ImageTableModelTests.cs index f94892ed..7737a13f 100644 --- a/Tests/Models/ImageTableModelTests.cs +++ b/Tests/Models/ImageTableModelTests.cs @@ -1,5 +1,5 @@ +using Gui.ViewModels.Graphics; using NUnit.Framework; -using Gui.ViewModels; namespace Models.Tests; From 05dfeb3d23ef089f7239fb77a3a7e91e6c5b9418 Mon Sep 17 00:00:00 2001 From: Benjamin Sutas Date: Mon, 1 Sep 2025 00:37:46 +1000 Subject: [PATCH 04/21] building variations --- Dat/FileParsing/LocoBinaryReader.cs | 2 +- Dat/FileParsing/LocoBinaryWriter.cs | 2 +- Dat/FileParsing/SawyerStreamReader.cs | 2 +- Dat/Loaders/AirportObjectLoader.cs | 25 +- Dat/Loaders/BuildingObjectLoader.cs | 33 +- Dat/Loaders/DockObjectLoader.cs | 25 +- Dat/Loaders/ImageTableLoader.cs | 24 ++ Dat/Loaders/IndustryObjectLoader.cs | 26 +- .../DataTables/Objects/TblObjectDock.cs | 4 +- .../ObjectModels/IImageTableNameProvider.cs | 2 +- .../Objects/Airport/AirportObject.cs | 13 +- .../Objects/Building/BuildingObject.cs | 12 +- .../Objects/Common/BuildingComponents.cs | 22 ++ .../BuildingPartAnimation.cs | 2 +- .../ObjectModels/Objects/Dock/DockObject.cs | 13 +- .../Objects/Industry/IndustryObject.cs | 15 +- Gui/ViewModels/DatTypes/G1ViewModel.cs | 1 - Gui/ViewModels/DatTypes/HexAnnotationLine.cs | 3 - .../DatTypes/ObjectEditorViewModel.cs | 5 +- .../DatTypes/Objects/AirportViewModel.cs | 36 +- .../DatTypes/Objects/BuildingViewModel.cs | 42 +-- .../DatTypes/Objects/DockViewModel.cs | 24 +- .../DatTypes/Objects/IndustryViewModel.cs | 36 +- .../Graphics/GroupedImageViewModel.cs | 16 - .../Graphics/ImageTableViewModel.cs | 314 ++++++++++-------- Gui/Views/ImageTableView.axaml | 43 ++- Gui/Views/WindowInteractionExtensions.cs | 2 +- ImageConversion/ImageConversion.csproj | 15 + ImageConversion/Program.cs | 40 +++ ObjectEditor.sln | 14 + Tests/LoadSaveTests.cs | 134 ++++---- 31 files changed, 560 insertions(+), 387 deletions(-) create mode 100644 Dat/Loaders/ImageTableLoader.cs create mode 100644 Definitions/ObjectModels/Objects/Common/BuildingComponents.cs rename Definitions/ObjectModels/Objects/{Building => Common}/BuildingPartAnimation.cs (90%) delete mode 100644 Gui/ViewModels/DatTypes/HexAnnotationLine.cs create mode 100644 ImageConversion/ImageConversion.csproj create mode 100644 ImageConversion/Program.cs diff --git a/Dat/FileParsing/LocoBinaryReader.cs b/Dat/FileParsing/LocoBinaryReader.cs index 09901a86..d7a79dc3 100644 --- a/Dat/FileParsing/LocoBinaryReader.cs +++ b/Dat/FileParsing/LocoBinaryReader.cs @@ -1,7 +1,7 @@ using Dat.Converters; using Dat.Types; using Definitions.ObjectModels.Objects.Airport; -using Definitions.ObjectModels.Objects.Building; +using Definitions.ObjectModels.Objects.Common; using Definitions.ObjectModels.Objects.Sound; using Definitions.ObjectModels.Objects.Vehicle; using Definitions.ObjectModels.Types; diff --git a/Dat/FileParsing/LocoBinaryWriter.cs b/Dat/FileParsing/LocoBinaryWriter.cs index f8c656f8..14f99e25 100644 --- a/Dat/FileParsing/LocoBinaryWriter.cs +++ b/Dat/FileParsing/LocoBinaryWriter.cs @@ -1,7 +1,7 @@ using Common; using Dat.Converters; using Dat.Types; -using Definitions.ObjectModels.Objects.Building; +using Definitions.ObjectModels.Objects.Common; using Definitions.ObjectModels.Objects.Sound; using Definitions.ObjectModels.Objects.Vehicle; using Definitions.ObjectModels.Types; diff --git a/Dat/FileParsing/SawyerStreamReader.cs b/Dat/FileParsing/SawyerStreamReader.cs index ac3cce8e..1181d384 100644 --- a/Dat/FileParsing/SawyerStreamReader.cs +++ b/Dat/FileParsing/SawyerStreamReader.cs @@ -282,7 +282,7 @@ public static (G1Header Header, List Table) ReadImageTable(Loco } var ge = currElement.Convert(); - ge.Name = DefaultImageTableNameProvider.GetImageName(i++); + ge.Name = DefaultImageTableNameProvider.GetImageName(i); graphicsElements.Add(ge); } diff --git a/Dat/Loaders/AirportObjectLoader.cs b/Dat/Loaders/AirportObjectLoader.cs index 35d0a2d5..4d5e65e3 100644 --- a/Dat/Loaders/AirportObjectLoader.cs +++ b/Dat/Loaders/AirportObjectLoader.cs @@ -32,8 +32,6 @@ public static LocoObject Load(Stream stream) using (var br = new LocoBinaryReader(stream)) { var model = new AirportObject(); - var stringTable = new StringTable(); - var imageTable = new List(); // fixed br.SkipStringId(); // Name offset, not part of object definition @@ -67,13 +65,16 @@ public static LocoObject Load(Stream stream) ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); // string table - stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType.Airport), null); + var stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType.Airport), null); // variable LoadVariable(br, model, numBuildingParts, numBuildingVariations, numMovementNodes, numMovementEdges); // image table - imageTable = SawyerStreamReader.ReadImageTable(br).Table; + var imageList = SawyerStreamReader.ReadImageTable(br).Table; + + // define groups + var imageTable = ImageTableLoader.CreateImageTable(imageList); return new LocoObject(ObjectType.Airport, model, stringTable, imageTable); } @@ -81,9 +82,9 @@ public static LocoObject Load(Stream stream) private static void LoadVariable(LocoBinaryReader br, AirportObject model, int numBuildingParts, int numBuildingVariations, byte numMovementNodes, byte numMovementEdges) { - model.BuildingHeights = br.ReadBuildingHeights(numBuildingParts); - model.BuildingAnimations = br.ReadBuildingAnimations(numBuildingParts); - model.BuildingVariations = br.ReadBuildingVariations(numBuildingVariations); + model.BuildingComponents.BuildingHeights = br.ReadBuildingHeights(numBuildingParts); + model.BuildingComponents.BuildingAnimations = br.ReadBuildingAnimations(numBuildingParts); + model.BuildingComponents.BuildingVariations = br.ReadBuildingVariations(numBuildingVariations); // building positions while (br.PeekByte() != LocoConstants.Terminator) @@ -123,8 +124,8 @@ public static void Save(Stream stream, LocoObject obj) bw.WriteEmptyImageId(); // Image, not part of object definition bw.WriteEmptyImageId(); // Image offset, not part of object definition bw.Write(model.AllowedPlaneTypes); - bw.Write((uint8_t)model.BuildingHeights.Count); - bw.Write((uint8_t)model.BuildingVariations.Count); + bw.Write((uint8_t)model.BuildingComponents.BuildingHeights.Count); + bw.Write((uint8_t)model.BuildingComponents.BuildingVariations.Count); bw.WriteEmptyPointer(); // BuildingHeights bw.WriteEmptyPointer(); // BuildingAnimations bw.WriteEmptyPointer(Constants.BuildingVariationCount); // BuildingVariations @@ -158,9 +159,9 @@ public static void Save(Stream stream, LocoObject obj) private static void SaveVariable(LocoBinaryWriter bw, AirportObject model) { - bw.Write(model.BuildingHeights); - bw.Write(model.BuildingAnimations); - bw.Write(model.BuildingVariations); + bw.Write(model.BuildingComponents.BuildingHeights); + bw.Write(model.BuildingComponents.BuildingAnimations); + bw.Write(model.BuildingComponents.BuildingVariations); // positions foreach (var x in model.BuildingPositions) diff --git a/Dat/Loaders/BuildingObjectLoader.cs b/Dat/Loaders/BuildingObjectLoader.cs index 11ae8bff..33a2fb5c 100644 --- a/Dat/Loaders/BuildingObjectLoader.cs +++ b/Dat/Loaders/BuildingObjectLoader.cs @@ -82,32 +82,17 @@ public static LocoObject Load(Stream stream) var imageList = SawyerStreamReader.ReadImageTable(br).Table; // define groups - var imageTable = CreateImageTable(imageList); + var imageTable = ImageTableLoader.CreateImageTable(imageList); return new LocoObject(ObjectType.Building, model, stringTable, imageTable); } } - private static ImageTable CreateImageTable(List imageList) - { - var imageTable = new ImageTable(); - - var chunks = imageList.Chunk(4); - imageTable.Groups.Add(("Base", chunks.First().ToList())); - var floorCount = 0; - foreach (var chunk in chunks.Skip(1)) - { - imageTable.Groups.Add(($"Floor {floorCount++}", chunk.ToList())); - } - - return imageTable; - } - private static void LoadVariable(LocoBinaryReader br, BuildingObject model, byte numBuildingParts, byte numBuildingVariations, byte numElevatorSequences) { - model.BuildingHeights = br.ReadBuildingHeights(numBuildingParts); - model.BuildingAnimations = br.ReadBuildingAnimations(numBuildingParts); - model.BuildingVariations = br.ReadBuildingVariations(numBuildingVariations); + model.BuildingComponents.BuildingHeights = br.ReadBuildingHeights(numBuildingParts); + model.BuildingComponents.BuildingAnimations = br.ReadBuildingAnimations(numBuildingParts); + model.BuildingComponents.BuildingVariations = br.ReadBuildingVariations(numBuildingVariations); model.ProducedCargo = br.ReadS5HeaderList(Constants.MaxProducedCargoType); model.RequiredCargo = br.ReadS5HeaderList(Constants.MaxRequiredCargoType); @@ -129,8 +114,8 @@ public static void Save(Stream stream, LocoObject obj) { bw.WriteEmptyStringId(); // Name offset, not part of object definition bw.WriteEmptyImageId(); // Image offset, not part of object definition - bw.Write((uint8_t)model.BuildingAnimations.Count); // NumBuildingParts - bw.Write((uint8_t)model.BuildingVariations.Count); + bw.Write((uint8_t)model.BuildingComponents.BuildingAnimations.Count); // NumBuildingParts + bw.Write((uint8_t)model.BuildingComponents.BuildingVariations.Count); bw.WriteEmptyPointer(); bw.WriteEmptyPointer(); bw.WriteEmptyPointer(Constants.BuildingVariationCount); @@ -173,9 +158,9 @@ public static void Save(Stream stream, LocoObject obj) private static void SaveVariable(BuildingObject model, LocoBinaryWriter bw) { - bw.Write(model.BuildingHeights); - bw.Write(model.BuildingAnimations); - bw.Write(model.BuildingVariations); + bw.Write(model.BuildingComponents.BuildingHeights); + bw.Write(model.BuildingComponents.BuildingAnimations); + bw.Write(model.BuildingComponents.BuildingVariations); bw.WriteS5HeaderList(model.ProducedCargo, Constants.MaxProducedCargoType); bw.WriteS5HeaderList(model.RequiredCargo, Constants.MaxRequiredCargoType); diff --git a/Dat/Loaders/DockObjectLoader.cs b/Dat/Loaders/DockObjectLoader.cs index af9ecabd..693fbcad 100644 --- a/Dat/Loaders/DockObjectLoader.cs +++ b/Dat/Loaders/DockObjectLoader.cs @@ -28,8 +28,6 @@ public static LocoObject Load(Stream stream) using (var br = new LocoBinaryReader(stream)) { var model = new DockObject(); - var stringTable = new StringTable(); - var imageTable = new List(); // fixed br.SkipStringId(); // Name offset, not part of object definition @@ -53,15 +51,18 @@ public static LocoObject Load(Stream stream) ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); // string table - stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType.Dock), null); + var stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType.Dock), null); // variable - model.BuildingHeights = br.ReadBuildingHeights(numBuildingParts); - model.BuildingAnimations = br.ReadBuildingAnimations(numBuildingParts); - model.BuildingVariations = br.ReadBuildingVariations(numBuildingVariations); + model.BuildingComponents.BuildingHeights = br.ReadBuildingHeights(numBuildingParts); + model.BuildingComponents.BuildingAnimations = br.ReadBuildingAnimations(numBuildingParts); + model.BuildingComponents.BuildingVariations = br.ReadBuildingVariations(numBuildingVariations); // image table - imageTable = SawyerStreamReader.ReadImageTable(br).Table; + var imageList = SawyerStreamReader.ReadImageTable(br).Table; + + // define groups + var imageTable = ImageTableLoader.CreateImageTable(imageList); return new LocoObject(ObjectType.Dock, model, stringTable, imageTable); } @@ -82,8 +83,8 @@ public static void Save(Stream stream, LocoObject obj) bw.WriteEmptyImageId(); // Image, not part of object definition bw.WriteEmptyImageId(); // UnkImage, not part of object definition bw.Write((uint16_t)model.Flags.Convert()); - bw.Write((uint8_t)model.BuildingAnimations.Count); - bw.Write((uint8_t)model.BuildingVariations.Count); + bw.Write((uint8_t)model.BuildingComponents.BuildingAnimations.Count); + bw.Write((uint8_t)model.BuildingComponents.BuildingVariations.Count); bw.WriteEmptyPointer(); // BuildingPartHeights bw.WriteEmptyPointer(); // BuildingPartAnimations bw.WriteEmptyPointer(); // BuildingVariationParts @@ -108,9 +109,9 @@ public static void Save(Stream stream, LocoObject obj) private static void SaveVariable(DockObject model, LocoBinaryWriter bw) { - bw.Write(model.BuildingHeights); - bw.Write(model.BuildingAnimations); - bw.Write(model.BuildingVariations); + bw.Write(model.BuildingComponents.BuildingHeights); + bw.Write(model.BuildingComponents.BuildingAnimations); + bw.Write(model.BuildingComponents.BuildingVariations); } [Flags] diff --git a/Dat/Loaders/ImageTableLoader.cs b/Dat/Loaders/ImageTableLoader.cs new file mode 100644 index 00000000..a8e1e1e0 --- /dev/null +++ b/Dat/Loaders/ImageTableLoader.cs @@ -0,0 +1,24 @@ +using Definitions.ObjectModels; +using Definitions.ObjectModels.Types; + +namespace Dat.Loaders; +public static class ImageTableLoader +{ + public static ImageTable CreateImageTable(List imageList) + { + var imageTable = new ImageTable(); + CreateBasicGroups(imageList, imageTable); + return imageTable; + } + + private static void CreateBasicGroups(List imageList, ImageTable imageTable) + { + var chunks = imageList.Chunk(4); + imageTable.Groups.Add(("Base", chunks.First().ToList())); + var floorCount = 0; + foreach (var chunk in chunks.Skip(1)) + { + imageTable.Groups.Add(($"Floor {floorCount++}", chunk.ToList())); + } + } +} diff --git a/Dat/Loaders/IndustryObjectLoader.cs b/Dat/Loaders/IndustryObjectLoader.cs index 0a19c364..c9dc3884 100644 --- a/Dat/Loaders/IndustryObjectLoader.cs +++ b/Dat/Loaders/IndustryObjectLoader.cs @@ -3,7 +3,6 @@ using Definitions.ObjectModels; using Definitions.ObjectModels.Objects.Industry; using Definitions.ObjectModels.Types; -using System.Diagnostics; using static Dat.Loaders.IndustryObjectLoader; namespace Dat.Loaders; @@ -36,8 +35,6 @@ public static LocoObject Load(Stream stream) using (var br = new LocoBinaryReader(stream)) { var model = new IndustryObject(); - var stringTable = new StringTable(); - var imageTable = new List(); // fixed br.SkipStringId(); // Name offset, not part of object definition @@ -93,13 +90,16 @@ public static LocoObject Load(Stream stream) ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); // string table - stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType.Industry), null); + var stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType.Industry), null); // variable LoadVariable(br, model, numBuildingParts, numBuildingVariations); // image table - imageTable = SawyerStreamReader.ReadImageTable(br).Table; + var imageList = SawyerStreamReader.ReadImageTable(br).Table; + + // define groups + var imageTable = ImageTableLoader.CreateImageTable(imageList); return new LocoObject(ObjectType.Industry, model, stringTable, imageTable); } @@ -107,8 +107,8 @@ public static LocoObject Load(Stream stream) private static void LoadVariable(LocoBinaryReader br, IndustryObject model, byte numBuildingParts, byte numBuildingVariations) { - model.BuildingHeights = br.ReadBuildingHeights(numBuildingParts); - model.BuildingAnimations = br.ReadBuildingAnimations(numBuildingParts); + model.BuildingComponents.BuildingHeights = br.ReadBuildingHeights(numBuildingParts); + model.BuildingComponents.BuildingAnimations = br.ReadBuildingAnimations(numBuildingParts); // animation sequences for (var i = 0; i < Constants.AnimationSequencesCount; ++i) @@ -125,7 +125,7 @@ private static void LoadVariable(LocoBinaryReader br, IndustryObject model, byte } br.SkipTerminator(); - model.BuildingVariations = br.ReadBuildingVariations(numBuildingVariations); + model.BuildingComponents.BuildingVariations = br.ReadBuildingVariations(numBuildingVariations); model.UnkBuildingData = [.. br.ReadBytes(model.MaxNumBuildings)]; model.ProducedCargo = br.ReadS5HeaderList(Constants.MaxProducedCargoType); model.RequiredCargo = br.ReadS5HeaderList(Constants.MaxRequiredCargoType); @@ -152,8 +152,8 @@ public static void Save(Stream stream, LocoObject obj) bw.WriteEmptyImageId(); // BaseBuildingImageId, not part of object definition bw.WriteEmptyImageId(); // BaseFarmImageIds, not part of object definition bw.Write(model.FarmImagesPerGrowthStage); - bw.Write((uint8_t)model.BuildingHeights.Count); - bw.Write((uint8_t)model.BuildingVariations.Count); + bw.Write((uint8_t)model.BuildingComponents.BuildingHeights.Count); + bw.Write((uint8_t)model.BuildingComponents.BuildingVariations.Count); bw.WriteEmptyPointer(); // BuildingHeights, not part of object definition bw.WriteEmptyPointer(); // BuildingAnimations, not part of object definition bw.WriteEmptyPointer(Constants.AnimationSequencesCount); // AnimationSequences, not part of object definition @@ -207,8 +207,8 @@ public static void Save(Stream stream, LocoObject obj) private static void SaveVariable(IndustryObject model, LocoBinaryWriter bw) { - bw.Write(model.BuildingHeights); - bw.Write(model.BuildingAnimations); + bw.Write(model.BuildingComponents.BuildingHeights); + bw.Write(model.BuildingComponents.BuildingAnimations); // animation sequences foreach (var x in model.AnimationSequences) @@ -225,7 +225,7 @@ private static void SaveVariable(IndustryObject model, LocoBinaryWriter bw) } bw.WriteTerminator(); - bw.Write(model.BuildingVariations); + bw.Write(model.BuildingComponents.BuildingVariations); bw.Write((ReadOnlySpan)[.. model.UnkBuildingData]); bw.WriteS5HeaderList(model.ProducedCargo, Constants.MaxProducedCargoType); bw.WriteS5HeaderList(model.RequiredCargo, Constants.MaxRequiredCargoType); diff --git a/Definitions/Database/DataTables/Objects/TblObjectDock.cs b/Definitions/Database/DataTables/Objects/TblObjectDock.cs index bd3db2f1..1fcdb505 100644 --- a/Definitions/Database/DataTables/Objects/TblObjectDock.cs +++ b/Definitions/Database/DataTables/Objects/TblObjectDock.cs @@ -30,8 +30,8 @@ public static TblObjectDock FromObject(TblObject tbl, DockObject obj) SellCostFactor = obj.SellCostFactor, CostIndex = obj.CostIndex, Flags = obj.Flags, - NumBuildingPartAnimations = (uint8_t)obj.BuildingAnimations.Count, - NumBuildingVariationParts = (uint8_t)obj.BuildingVariations.Count, + NumBuildingPartAnimations = (uint8_t)obj.BuildingComponents.BuildingAnimations.Count, + NumBuildingVariationParts = (uint8_t)obj.BuildingComponents.BuildingVariations.Count, DesignedYear = obj.DesignedYear, ObsoleteYear = obj.ObsoleteYear, BoatPositionX = obj.BoatPosition.X, diff --git a/Definitions/ObjectModels/IImageTableNameProvider.cs b/Definitions/ObjectModels/IImageTableNameProvider.cs index bdbd8b83..b6c0c367 100644 --- a/Definitions/ObjectModels/IImageTableNameProvider.cs +++ b/Definitions/ObjectModels/IImageTableNameProvider.cs @@ -5,7 +5,7 @@ namespace Definitions.ObjectModels; public interface IHasGraphicsElements { - public List GraphicsElements { get; set; } + public List GraphicsElements { get; set; } // todo: probably change to IEnumerable } public interface IImageTableNameProvider diff --git a/Definitions/ObjectModels/Objects/Airport/AirportObject.cs b/Definitions/ObjectModels/Objects/Airport/AirportObject.cs index 4418d6a5..dc0694cd 100644 --- a/Definitions/ObjectModels/Objects/Airport/AirportObject.cs +++ b/Definitions/ObjectModels/Objects/Airport/AirportObject.cs @@ -1,8 +1,8 @@ -using Definitions.ObjectModels.Objects.Building; +using Definitions.ObjectModels.Objects.Common; namespace Definitions.ObjectModels.Objects.Airport; -public class AirportObject : ILocoStruct +public class AirportObject : ILocoStruct, IHasBuildingComponents { public int16_t BuildCostFactor { get; set; } public int16_t SellCostFactor { get; set; } @@ -17,9 +17,7 @@ public class AirportObject : ILocoStruct public uint16_t DesignedYear { get; set; } public uint16_t ObsoleteYear { get; set; } - public List BuildingHeights { get; set; } = []; - public List BuildingAnimations { get; set; } = []; - public List> BuildingVariations { get; set; } = []; + public BuildingComponents BuildingComponents { get; set; } = new(); public List BuildingPositions { get; set; } = []; public List MovementNodes { get; set; } = []; @@ -28,6 +26,11 @@ public class AirportObject : ILocoStruct public bool Validate() { + if (!BuildingComponents.Validate()) + { + return false; + } + if (CostIndex > 32) { return false; diff --git a/Definitions/ObjectModels/Objects/Building/BuildingObject.cs b/Definitions/ObjectModels/Objects/Building/BuildingObject.cs index cf0fcfc0..11249f73 100644 --- a/Definitions/ObjectModels/Objects/Building/BuildingObject.cs +++ b/Definitions/ObjectModels/Objects/Building/BuildingObject.cs @@ -1,8 +1,9 @@ +using Definitions.ObjectModels.Objects.Common; using Definitions.ObjectModels.Types; namespace Definitions.ObjectModels.Objects.Building; -public class BuildingObject : ILocoStruct +public class BuildingObject : ILocoStruct, IHasBuildingComponents { public uint16_t DesignedYear { get; set; } public uint16_t ObsoleteYear { get; set; } @@ -23,9 +24,7 @@ public class BuildingObject : ILocoStruct public uint8_t var_AC { get; set; } - public List BuildingHeights { get; set; } = []; - public List BuildingAnimations { get; set; } = []; - public List> BuildingVariations { get; set; } = []; + public BuildingComponents BuildingComponents { get; set; } = new(); public List ProducedQuantity { get; set; } = []; public List ProducedCargo { get; set; } = []; @@ -35,8 +34,5 @@ public class BuildingObject : ILocoStruct public bool Validate() => ProducedQuantity.Count == 2 - && BuildingHeights.Count is not 0 and not > 63 - && BuildingAnimations.Count is not 0 and not > 63 - && BuildingHeights.Count == BuildingAnimations.Count - && BuildingVariations.Count is not 0 and <= 31; + && BuildingComponents.Validate(); } diff --git a/Definitions/ObjectModels/Objects/Common/BuildingComponents.cs b/Definitions/ObjectModels/Objects/Common/BuildingComponents.cs new file mode 100644 index 00000000..a7c3cfd4 --- /dev/null +++ b/Definitions/ObjectModels/Objects/Common/BuildingComponents.cs @@ -0,0 +1,22 @@ +using System.ComponentModel; + +namespace Definitions.ObjectModels.Objects.Common; + +public interface IHasBuildingComponents +{ + BuildingComponents BuildingComponents { get; set; } +} + +[TypeConverter(typeof(ExpandableObjectConverter))] +public class BuildingComponents +{ + public List BuildingHeights { get; set; } = []; + public List BuildingAnimations { get; set; } = []; + public List> BuildingVariations { get; set; } = []; + + public bool Validate() + => BuildingHeights.Count is not 0 and not > 63 + && BuildingAnimations.Count is not 0 and not > 63 + && BuildingHeights.Count == BuildingAnimations.Count + && BuildingVariations.Count is not 0 and <= 31; +} diff --git a/Definitions/ObjectModels/Objects/Building/BuildingPartAnimation.cs b/Definitions/ObjectModels/Objects/Common/BuildingPartAnimation.cs similarity index 90% rename from Definitions/ObjectModels/Objects/Building/BuildingPartAnimation.cs rename to Definitions/ObjectModels/Objects/Common/BuildingPartAnimation.cs index ae78f983..5f4e6a1b 100644 --- a/Definitions/ObjectModels/Objects/Building/BuildingPartAnimation.cs +++ b/Definitions/ObjectModels/Objects/Common/BuildingPartAnimation.cs @@ -1,6 +1,6 @@ using System.ComponentModel; -namespace Definitions.ObjectModels.Objects.Building; +namespace Definitions.ObjectModels.Objects.Common; [TypeConverter(typeof(ExpandableObjectConverter))] public class BuildingPartAnimation diff --git a/Definitions/ObjectModels/Objects/Dock/DockObject.cs b/Definitions/ObjectModels/Objects/Dock/DockObject.cs index e7fbd68a..e919569a 100644 --- a/Definitions/ObjectModels/Objects/Dock/DockObject.cs +++ b/Definitions/ObjectModels/Objects/Dock/DockObject.cs @@ -1,9 +1,9 @@ -using Definitions.ObjectModels.Objects.Building; +using Definitions.ObjectModels.Objects.Common; using Definitions.ObjectModels.Types; namespace Definitions.ObjectModels.Objects.Dock; -public class DockObject : ILocoStruct +public class DockObject : ILocoStruct, IHasBuildingComponents { public int16_t BuildCostFactor { get; set; } public int16_t SellCostFactor { get; set; } @@ -14,12 +14,15 @@ public class DockObject : ILocoStruct public uint16_t ObsoleteYear { get; set; } public Pos2 BoatPosition { get; set; } - public List BuildingHeights { get; set; } = []; - public List BuildingAnimations { get; set; } = []; - public List> BuildingVariations { get; set; } = []; + public BuildingComponents BuildingComponents { get; set; } = new(); public bool Validate() { + if (BuildingComponents.Validate()) + { + return false; + } + if (CostIndex > 32) { return false; diff --git a/Definitions/ObjectModels/Objects/Industry/IndustryObject.cs b/Definitions/ObjectModels/Objects/Industry/IndustryObject.cs index 9d9ff731..322035d8 100644 --- a/Definitions/ObjectModels/Objects/Industry/IndustryObject.cs +++ b/Definitions/ObjectModels/Objects/Industry/IndustryObject.cs @@ -1,9 +1,9 @@ -using Definitions.ObjectModels.Objects.Building; +using Definitions.ObjectModels.Objects.Common; using Definitions.ObjectModels.Types; namespace Definitions.ObjectModels.Objects.Industry; -public class IndustryObject : ILocoStruct +public class IndustryObject : ILocoStruct, IHasBuildingComponents { public uint32_t FarmImagesPerGrowthStage { get; set; } public uint8_t MinNumBuildings { get; set; } @@ -34,9 +34,7 @@ public class IndustryObject : ILocoStruct public ObjectModelHeader? BuildingWall { get; set; } // Wall types that can be built around this industry public ObjectModelHeader? BuildingWallEntrance { get; set; } // Wall types that can be built around this industry - public List BuildingHeights { get; set; } = []; - public List BuildingAnimations { get; set; } = []; - public List> BuildingVariations { get; set; } = []; + public BuildingComponents BuildingComponents { get; set; } = new(); public List> AnimationSequences { get; set; } = []; // Access with getAnimationSequence helper method public List UnkBuildingData { get; set; } = []; @@ -44,12 +42,7 @@ public class IndustryObject : ILocoStruct public bool Validate() { - if (BuildingHeights.Count == 0 || BuildingAnimations.Count == 0) - { - return false; - } - - if (BuildingVariations.Count is 0 or > 31) + if (!BuildingComponents.Validate()) { return false; } diff --git a/Gui/ViewModels/DatTypes/G1ViewModel.cs b/Gui/ViewModels/DatTypes/G1ViewModel.cs index 01b274fc..6be42f90 100644 --- a/Gui/ViewModels/DatTypes/G1ViewModel.cs +++ b/Gui/ViewModels/DatTypes/G1ViewModel.cs @@ -3,7 +3,6 @@ using Gui.ViewModels.Graphics; using ReactiveUI.Fody.Helpers; using System.IO; -using System.Linq; using System.Threading.Tasks; namespace Gui.ViewModels; diff --git a/Gui/ViewModels/DatTypes/HexAnnotationLine.cs b/Gui/ViewModels/DatTypes/HexAnnotationLine.cs deleted file mode 100644 index ecb831d1..00000000 --- a/Gui/ViewModels/DatTypes/HexAnnotationLine.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace Gui.ViewModels; - -public record HexAnnotationLine(string Address, string Data, int? SelectionStart, int? SelectionEnd); diff --git a/Gui/ViewModels/DatTypes/ObjectEditorViewModel.cs b/Gui/ViewModels/DatTypes/ObjectEditorViewModel.cs index 4b275f88..efadf0e2 100644 --- a/Gui/ViewModels/DatTypes/ObjectEditorViewModel.cs +++ b/Gui/ViewModels/DatTypes/ObjectEditorViewModel.cs @@ -5,6 +5,8 @@ using Dat.Data; using Dat.FileParsing; using Definitions.ObjectModels; +using Definitions.ObjectModels.Objects.Building; +using Definitions.ObjectModels.Objects.Common; using Definitions.ObjectModels.Objects.Sound; using Gui.Models; using Gui.Models.Audio; @@ -13,6 +15,7 @@ using ReactiveUI; using ReactiveUI.Fody.Helpers; using System; +using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; using System.Linq; @@ -159,7 +162,7 @@ public override void Load() ExtraContentViewModel = CurrentObject.LocoObject.Object is SoundObject soundObject ? new AudioViewModel(logger, CurrentObject.DatFileInfo.S5Header.Name, soundObject.SoundObjectData.PcmHeader, soundObject.PcmData) - : new ImageTableViewModel(CurrentObject.LocoObject.ImageTable, Model.PaletteMap, Model.Logger); + : new ImageTableViewModel(CurrentObject.LocoObject.ImageTable, Model.PaletteMap, Model.Logger, (CurrentObject.LocoObject.Object as IHasBuildingComponents)?.BuildingComponents); } else { diff --git a/Gui/ViewModels/DatTypes/Objects/AirportViewModel.cs b/Gui/ViewModels/DatTypes/Objects/AirportViewModel.cs index aad18192..db82c914 100644 --- a/Gui/ViewModels/DatTypes/Objects/AirportViewModel.cs +++ b/Gui/ViewModels/DatTypes/Objects/AirportViewModel.cs @@ -1,7 +1,8 @@ using Dat.Loaders; using Definitions.ObjectModels.Objects.Airport; -using Definitions.ObjectModels.Objects.Building; +using Definitions.ObjectModels.Objects.Common; using PropertyModels.Extensions; +using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Linq; @@ -21,12 +22,12 @@ public class AirportViewModel : LocoObjectViewModel [Category("Cost")] public uint8_t CostIndex { get; set; } [Category("Cost")] public int16_t BuildCostFactor { get; set; } [Category("Cost")] public int16_t SellCostFactor { get; set; } - [Category("Building"), Length(1, AirportObjectLoader.Constants.BuildingVariationCount)] public BindingList> BuildingVariations { get; set; } // NumBuildingVariations - [Category("Building"), Length(1, AirportObjectLoader.Constants.BuildingHeightCount)] public BindingList BuildingHeights { get; set; } // NumBuildingParts - [Category("Building"), Length(1, AirportObjectLoader.Constants.BuildingAnimationCount)] public BindingList BuildingAnimations { get; set; } // NumBuildingParts - [Category("Building")] public BindingList BuildingPositions { get; set; } - [Category("Movement")] public BindingList MovementNodes { get; set; } // NumMovementNodes - [Category("Movement")] public BindingList MovementEdges { get; set; } // NumMovementEdges + [Category("Building"), Length(1, AirportObjectLoader.Constants.BuildingVariationCount)] public List> BuildingVariations { get; set; } // NumBuildingVariations + [Category("Building"), Length(1, AirportObjectLoader.Constants.BuildingHeightCount)] public List BuildingHeights { get; set; } // NumBuildingParts + [Category("Building"), Length(1, AirportObjectLoader.Constants.BuildingAnimationCount)] public List BuildingAnimations { get; set; } // NumBuildingParts + [Category("Building")] public List BuildingPositions { get; set; } + [Category("Movement")] public List MovementNodes { get; set; } // NumMovementNodes + [Category("Movement")] public List MovementEdges { get; set; } // NumMovementEdges [Category("")] public uint8_t var_07 { get; set; } [Category(""), Length(4, 4)] public BindingList var_B6 { get; set; } @@ -37,10 +38,10 @@ public AirportViewModel(AirportObject ao) SellCostFactor = ao.SellCostFactor; var_07 = ao.var_07; AllowedPlaneTypes = ao.AllowedPlaneTypes; - BuildingHeights = new(ao.BuildingHeights); - BuildingAnimations = new(ao.BuildingAnimations); - BuildingVariations = new(ao.BuildingVariations.Select(x => new BindingList(x)).ToBindingList()); - BuildingPositions = new(ao.BuildingPositions); + BuildingAnimations = [.. ao.BuildingComponents.BuildingAnimations]; + BuildingHeights = [.. ao.BuildingComponents.BuildingHeights]; + BuildingVariations = [.. ao.BuildingComponents.BuildingVariations.Select(x => new List(x))]; + BuildingPositions = [.. ao.BuildingPositions]; LargeTiles = ao.LargeTiles; MinX = ao.MinX; MinY = ao.MinY; @@ -48,8 +49,8 @@ public AirportViewModel(AirportObject ao) MaxY = ao.MaxY; DesignedYear = ao.DesignedYear; ObsoleteYear = ao.ObsoleteYear; - MovementNodes = new(ao.MovementNodes); - MovementEdges = new(ao.MovementEdges); + MovementNodes = [.. ao.MovementNodes]; + MovementEdges = [.. ao.MovementEdges]; var_B6 = [.. ao.var_B6]; } @@ -71,9 +72,12 @@ public override AirportObject GetAsModel() DesignedYear = DesignedYear, ObsoleteYear = ObsoleteYear, var_B6 = [.. var_B6], - BuildingHeights = [.. BuildingHeights], - BuildingAnimations = [.. BuildingAnimations], - BuildingVariations = BuildingVariations.ToList().ConvertAll(x => x.ToList()), + BuildingComponents = new() + { + BuildingHeights = [.. BuildingHeights], + BuildingAnimations = [.. BuildingAnimations], + BuildingVariations = BuildingVariations.ToList().ConvertAll(x => x.ToList()), + }, BuildingPositions = [.. BuildingPositions], MovementNodes = [.. MovementNodes], MovementEdges = [.. MovementEdges], diff --git a/Gui/ViewModels/DatTypes/Objects/BuildingViewModel.cs b/Gui/ViewModels/DatTypes/Objects/BuildingViewModel.cs index f5ed00b3..0fb255c0 100644 --- a/Gui/ViewModels/DatTypes/Objects/BuildingViewModel.cs +++ b/Gui/ViewModels/DatTypes/Objects/BuildingViewModel.cs @@ -1,6 +1,6 @@ using Dat.Loaders; -using Definitions.ObjectModels.Objects.Airport; using Definitions.ObjectModels.Objects.Building; +using Definitions.ObjectModels.Objects.Common; using Definitions.ObjectModels.Types; using PropertyModels.ComponentModel.DataAnnotations; using PropertyModels.Extensions; @@ -25,19 +25,20 @@ public class BuildingViewModel : LocoObjectViewModel [Category("Stats")] public uint16_t ObsoleteYear { get; set; } [Category("Cost")] public uint8_t CostIndex { get; set; } [Category("Cost")] public uint16_t SellCostFactor { get; set; } - [Category("Production"), Length(0, BuildingObjectLoader.Constants.MaxProducedCargoType)] public BindingList ProducedCargo { get; set; } - [Category("Production"), Length(0, BuildingObjectLoader.Constants.MaxProducedCargoType)] public BindingList RequiredCargo { get; set; } - [Category("Production"), Length(1, BuildingObjectLoader.Constants.MaxProducedCargoType)] public BindingList ProducedQuantity { get; set; } - [Category("Building"), Length(1, BuildingObjectLoader.Constants.BuildingVariationCount)] public BindingList> BuildingVariations { get; set; } // NumBuildingVariations - [Category("Building"), Length(1, BuildingObjectLoader.Constants.BuildingHeightCount)] public BindingList BuildingHeights { get; set; } // NumBuildingParts - [Category("Building"), Length(1, BuildingObjectLoader.Constants.BuildingAnimationCount)] public BindingList BuildingAnimations { get; set; } // NumBuildingParts + [Category("Production"), Length(0, BuildingObjectLoader.Constants.MaxProducedCargoType)] public List ProducedCargo { get; set; } + [Category("Production"), Length(0, BuildingObjectLoader.Constants.MaxProducedCargoType)] public List RequiredCargo { get; set; } + [Category("Production"), Length(1, BuildingObjectLoader.Constants.MaxProducedCargoType)] public List ProducedQuantity { get; set; } + [Category("Building"), Length(1, BuildingObjectLoader.Constants.BuildingVariationCount)] public List> BuildingVariations { get; set; } // NumBuildingVariations + [Category("Building"), Length(1, BuildingObjectLoader.Constants.BuildingHeightCount)] public List BuildingHeights { get; set; } // NumBuildingParts + [Category("Building"), Length(1, BuildingObjectLoader.Constants.BuildingAnimationCount)] public List BuildingAnimations { get; set; } // NumBuildingParts // note: these height sequences are massive. BLDCTY28 has 2 sequences, 512 in length and 1024 in length. Avalonia PropertyGrid takes 30+ seconds to render this. todo: don't use property grid in future //[Reactive, Category("Building"), Length(1, BuildingObject.MaxElevatorHeightSequences), Browsable(false)] public BindingList> ElevatorHeightSequences { get; set; } // NumElevatorSequences - public uint8_t[]? ElevatorSequence1 { get; set; } - public uint8_t[]? ElevatorSequence2 { get; set; } - public uint8_t[]? ElevatorSequence3 { get; set; } - public uint8_t[]? ElevatorSequence4 { get; set; } + + [Category("Elevator"), Browsable(false)] public uint8_t[]? ElevatorSequence1 { get; set; } + [Category("Elevator"), Browsable(false)] public uint8_t[]? ElevatorSequence2 { get; set; } + [Category("Elevator"), Browsable(false)] public uint8_t[]? ElevatorSequence3 { get; set; } + [Category("Elevator"), Browsable(false)] public uint8_t[]? ElevatorSequence4 { get; set; } [Reactive, Category("")] public uint8_t var_A6 { get; set; } [Reactive, Category("")] public uint8_t var_A7 { get; set; } @@ -58,12 +59,12 @@ public BuildingViewModel(BuildingObject bo) ObsoleteYear = bo.ObsoleteYear; CostIndex = bo.CostIndex; SellCostFactor = bo.SellCostFactor; - ProducedCargo = new(bo.ProducedCargo.ConvertAll(x => new ObjectModelHeaderViewModel(x))); - RequiredCargo = new(bo.RequiredCargo.ConvertAll(x => new ObjectModelHeaderViewModel(x))); + ProducedCargo = [.. bo.ProducedCargo.ConvertAll(x => new ObjectModelHeaderViewModel(x))]; + RequiredCargo = [.. bo.RequiredCargo.ConvertAll(x => new ObjectModelHeaderViewModel(x))]; ProducedQuantity = [.. bo.ProducedQuantity]; - BuildingHeights = new(bo.BuildingHeights); - BuildingAnimations = new(bo.BuildingAnimations); - BuildingVariations = new(bo.BuildingVariations.Select(x => new BindingList(x)).ToBindingList()); + BuildingAnimations = [.. bo.BuildingComponents.BuildingAnimations]; + BuildingHeights = [.. bo.BuildingComponents.BuildingHeights]; + BuildingVariations = [.. bo.BuildingComponents.BuildingVariations.Select(x => new List(x))]; ElevatorSequence1 = bo.ElevatorHeightSequences.Count > 0 ? bo.ElevatorHeightSequences[0] : null; ElevatorSequence2 = bo.ElevatorHeightSequences.Count > 1 ? bo.ElevatorHeightSequences[1] : null; ElevatorSequence3 = bo.ElevatorHeightSequences.Count > 2 ? bo.ElevatorHeightSequences[2] : null; @@ -81,9 +82,12 @@ public BuildingViewModel(BuildingObject bo) public override BuildingObject GetAsModel() => new BuildingObject() { - BuildingAnimations = [.. BuildingAnimations], - BuildingHeights = [.. BuildingHeights], - BuildingVariations = BuildingVariations.ToList().ConvertAll(x => x.ToList()), + BuildingComponents = new BuildingComponents() + { + BuildingHeights = [.. BuildingHeights], + BuildingAnimations = [.. BuildingAnimations], + BuildingVariations = BuildingVariations.ToList().ConvertAll(x => x.ToList()), + }, Flags = Flags, Colours = Colours, ScaffoldingColour = ScaffoldingColour, diff --git a/Gui/ViewModels/DatTypes/Objects/DockViewModel.cs b/Gui/ViewModels/DatTypes/Objects/DockViewModel.cs index b7bf37b8..4b3c72dc 100644 --- a/Gui/ViewModels/DatTypes/Objects/DockViewModel.cs +++ b/Gui/ViewModels/DatTypes/Objects/DockViewModel.cs @@ -1,10 +1,11 @@ using Dat.Loaders; using Definitions.ObjectModels.Objects.Airport; -using Definitions.ObjectModels.Objects.Building; +using Definitions.ObjectModels.Objects.Common; using Definitions.ObjectModels.Objects.Dock; using Definitions.ObjectModels.Types; using PropertyModels.ComponentModel.DataAnnotations; using PropertyModels.Extensions; +using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Linq; @@ -20,9 +21,9 @@ public class DockViewModel : LocoObjectViewModel [Category("Cost")] public uint8_t CostIndex { get; set; } [Category("Cost")] public int16_t BuildCostFactor { get; set; } [Category("Cost")] public int16_t SellCostFactor { get; set; } - [Category("Building"), Length(1, DockObjectLoader.Constants.BuildingVariationCount)] public BindingList> BuildingVariations { get; set; } // NumBuildingVariations - [Category("Building"), Length(1, DockObjectLoader.Constants.BuildingHeightCount)] public BindingList BuildingHeights { get; set; } // NumBuildingParts - [Category("Building"), Length(1, DockObjectLoader.Constants.BuildingAnimationCount)] public BindingList BuildingAnimations { get; set; } // NumBuildingParts + [Category("Building"), Length(1, DockObjectLoader.Constants.BuildingVariationCount)] public List> BuildingVariations { get; set; } // NumBuildingVariations + [Category("Building"), Length(1, DockObjectLoader.Constants.BuildingHeightCount)] public List BuildingHeights { get; set; } // NumBuildingParts + [Category("Building"), Length(1, DockObjectLoader.Constants.BuildingAnimationCount)] public List BuildingAnimations { get; set; } // NumBuildingParts [Category("")] public uint8_t var_07 { get; set; } public DockViewModel(DockObject @do) @@ -35,9 +36,9 @@ public DockViewModel(DockObject @do) SellCostFactor = @do.SellCostFactor; BoatPosition = @do.BoatPosition; var_07 = @do.var_07; - BuildingHeights = new(@do.BuildingHeights); - BuildingAnimations = new(@do.BuildingAnimations); - BuildingVariations = new(@do.BuildingVariations.Select(x => new BindingList(x)).ToBindingList()); + BuildingAnimations = [.. @do.BuildingComponents.BuildingAnimations]; + BuildingHeights = [.. @do.BuildingComponents.BuildingHeights]; + BuildingVariations = [.. @do.BuildingComponents.BuildingVariations.Select(x => new List(x))]; } public override DockObject GetAsModel() @@ -52,9 +53,12 @@ public override DockObject GetAsModel() SellCostFactor = SellCostFactor, BoatPosition = BoatPosition, var_07 = var_07, - BuildingHeights = [.. BuildingHeights], - BuildingAnimations = [.. BuildingAnimations], - BuildingVariations = BuildingVariations.ToList().ConvertAll(x => x.ToList()), + BuildingComponents = new BuildingComponents() + { + BuildingHeights = [.. BuildingHeights], + BuildingAnimations = [.. BuildingAnimations], + BuildingVariations = BuildingVariations.ToList().ConvertAll(x => x.ToList()), + }, }; return dockObject; } diff --git a/Gui/ViewModels/DatTypes/Objects/IndustryViewModel.cs b/Gui/ViewModels/DatTypes/Objects/IndustryViewModel.cs index 88a83b85..942ea229 100644 --- a/Gui/ViewModels/DatTypes/Objects/IndustryViewModel.cs +++ b/Gui/ViewModels/DatTypes/Objects/IndustryViewModel.cs @@ -1,10 +1,11 @@ using Dat.Loaders; using Definitions.ObjectModels.Objects.Airport; -using Definitions.ObjectModels.Objects.Building; +using Definitions.ObjectModels.Objects.Common; using Definitions.ObjectModels.Objects.Industry; using Definitions.ObjectModels.Types; using PropertyModels.ComponentModel.DataAnnotations; using PropertyModels.Extensions; +using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Linq; @@ -26,20 +27,20 @@ public class IndustryViewModel : LocoObjectViewModel [Category("Cost")] public uint8_t CostIndex { get; set; } [Category("Cost")] public int16_t BuildCostFactor { get; set; } [Category("Cost")] public int16_t SellCostFactor { get; set; } - [Category("Building"), Length(IndustryObjectLoader.Constants.AnimationSequencesCount, IndustryObjectLoader.Constants.AnimationSequencesCount)] public BindingList> AnimationSequences { get; set; } - [Category("Building"), Length(1, IndustryObjectLoader.Constants.BuildingVariationCount)] public BindingList> BuildingVariations { get; set; } // NumBuildingVariations - [Category("Building"), Length(1, IndustryObjectLoader.Constants.BuildingHeightCount)] public BindingList BuildingHeights { get; set; } // NumBuildingParts - [Category("Building"), Length(1, IndustryObjectLoader.Constants.BuildingAnimationCount)] public BindingList BuildingAnimations { get; set; } // NumBuildingParts + [Category("Building"), Length(IndustryObjectLoader.Constants.AnimationSequencesCount, IndustryObjectLoader.Constants.AnimationSequencesCount)] public List> AnimationSequences { get; set; } + [Category("Building"), Length(1, IndustryObjectLoader.Constants.BuildingVariationCount)] public List> BuildingVariations { get; set; } // NumBuildingVariations + [Category("Building"), Length(1, IndustryObjectLoader.Constants.BuildingHeightCount)] public List BuildingHeights { get; set; } // NumBuildingParts + [Category("Building"), Length(1, IndustryObjectLoader.Constants.BuildingAnimationCount)] public List BuildingAnimations { get; set; } // NumBuildingParts [Category("Building")] public uint8_t MinNumBuildings { get; set; } [Category("Building")] public uint8_t MaxNumBuildings { get; set; } [Category("Building")] public BindingList UnkBuildingData { get; set; } [Category("Building")] public uint32_t BuildingSizeFlags { get; set; } [Category("Building")] public uint8_t ScaffoldingSegmentType { get; set; } [Category("Building")] public Colour ScaffoldingColour { get; set; } - [Category("Building"), Length(0, IndustryObjectLoader.Constants.MaxWallTypeCount)] public BindingList WallTypes { get; set; } + [Category("Building"), Length(0, IndustryObjectLoader.Constants.MaxWallTypeCount)] public List WallTypes { get; set; } [Category("Building")] public ObjectModelHeaderViewModel? BuildingWall { get; set; } [Category("Building")] public ObjectModelHeaderViewModel? BuildingWallEntrance { get; set; } - [Category("")] public BindingList var_38 { get; set; } + [Category("")] public List var_38 { get; set; } [Category("")] public uint8_t var_E8 { get; set; } [Category("Farm")] public uint8_t FarmTileNumImageAngles { get; set; } [Category("Farm")] public uint8_t FarmGrowthStageWithNoProduction { get; set; } @@ -48,10 +49,10 @@ public class IndustryViewModel : LocoObjectViewModel public IndustryViewModel(IndustryObject io) { - AnimationSequences = new(io.AnimationSequences.Select(x => new BindingList(x)).ToBindingList()); - BuildingAnimations = new(io.BuildingAnimations); - BuildingHeights = new(io.BuildingHeights); - BuildingVariations = new(io.BuildingVariations.Select(x => new BindingList(x)).ToBindingList()); + AnimationSequences = [.. io.AnimationSequences.Select(x => new List(x))]; + BuildingAnimations = [.. io.BuildingComponents.BuildingAnimations]; + BuildingHeights = [.. io.BuildingComponents.BuildingHeights]; + BuildingVariations = [.. io.BuildingComponents.BuildingVariations.Select(x => new List(x))]; UnkBuildingData = new(io.UnkBuildingData); BuildingSizeFlags = io.BuildingSizeFlags; BuildingWall = io.BuildingWall == null ? null : new(io.BuildingWall); @@ -61,7 +62,7 @@ public IndustryViewModel(IndustryObject io) InitialProductionRate = new(io.InitialProductionRate); ProducedCargo = new(io.ProducedCargo.ConvertAll(x => new ObjectModelHeaderViewModel(x))); RequiredCargo = new(io.RequiredCargo.ConvertAll(x => new ObjectModelHeaderViewModel(x))); - WallTypes = new(io.WallTypes.ConvertAll(x => new ObjectModelHeaderViewModel(x))); + WallTypes = [.. io.WallTypes.ConvertAll(x => new ObjectModelHeaderViewModel(x))]; Colours = io.Colours; DesignedYear = io.DesignedYear; ObsoleteYear = io.ObsoleteYear; @@ -79,7 +80,7 @@ public IndustryViewModel(IndustryObject io) FarmNumStagesOfGrowth = io.FarmNumStagesOfGrowth; MonthlyClosureChance = io.MonthlyClosureChance; var_E8 = io.var_E8; - var_38 = new(io.var_38); + var_38 = [.. io.var_38]; } // validation: @@ -88,9 +89,12 @@ public override IndustryObject GetAsModel() => new() { AnimationSequences = AnimationSequences.ToList().ConvertAll(x => x.ToList()), - BuildingAnimations = [.. BuildingAnimations], - BuildingHeights = [.. BuildingHeights], - BuildingVariations = BuildingVariations.ToList().ConvertAll(x => x.ToList()), + BuildingComponents = new BuildingComponents() + { + BuildingHeights = [.. BuildingHeights], + BuildingAnimations = [.. BuildingAnimations], + BuildingVariations = BuildingVariations.ToList().ConvertAll(x => x.ToList()), + }, UnkBuildingData = [.. UnkBuildingData], BuildingSizeFlags = BuildingSizeFlags, BuildingWall = BuildingWall?.GetAsModel(), diff --git a/Gui/ViewModels/Graphics/GroupedImageViewModel.cs b/Gui/ViewModels/Graphics/GroupedImageViewModel.cs index ac7dfc91..a8ccc73c 100644 --- a/Gui/ViewModels/Graphics/GroupedImageViewModel.cs +++ b/Gui/ViewModels/Graphics/GroupedImageViewModel.cs @@ -23,21 +23,5 @@ public GroupedImageViewModel(string groupName, IEnumerable image { SingleSelect = false }; - //SelectionModel.SelectionChanged += SelectionChanged; } - - //void SelectionChanged(object sender, SelectionModelSelectionChangedEventArgs e) - //{ - // var sm = (SelectionModel)sender; - - // if (sm.SelectedIndexes.Count > 0) - // { - // //SelectedImageIndex = sm.SelectedIndex; - // } - - // if (sm.SelectedItems.Count == 0) - // { - // return; - // } - //} } diff --git a/Gui/ViewModels/Graphics/ImageTableViewModel.cs b/Gui/ViewModels/Graphics/ImageTableViewModel.cs index ea9d0013..976d12c0 100644 --- a/Gui/ViewModels/Graphics/ImageTableViewModel.cs +++ b/Gui/ViewModels/Graphics/ImageTableViewModel.cs @@ -4,7 +4,9 @@ using Common.Json; using Common.Logging; using Definitions.ObjectModels; +using Definitions.ObjectModels.Objects.Common; using Definitions.ObjectModels.Types; +using DynamicData; using ReactiveUI; using ReactiveUI.Fody.Helpers; using SixLabors.ImageSharp; @@ -70,7 +72,14 @@ public class ImageTableViewModel : ReactiveObject, IExtraContentViewModel [Reactive] public ObservableCollection GroupedImageViewModels { get; set; } = []; - public ImageTableViewModel(ImageTable imageTable, PaletteMap paletteMap, ILogger logger) + [Reactive] + public ObservableCollection LayeredImages { get; set; } = []; + + [Reactive] + public int OffsetSpacing { get; set; } + int prevOffset; + + public ImageTableViewModel(ImageTable imageTable, PaletteMap paletteMap, ILogger logger, BuildingComponents buildingComponents = null) { ArgumentNullException.ThrowIfNull(paletteMap); @@ -80,8 +89,55 @@ public ImageTableViewModel(ImageTable imageTable, PaletteMap paletteMap, ILogger var givm = new GroupedImageViewModel(group.Name, imageViewModels); givm.SelectionModel.SelectionChanged += SelectionChanged; GroupedImageViewModels.Add(givm); + } + // building components + if (buildingComponents != null) + { + var allGEs = imageTable.GraphicsElements; + var baseY = 128; + + foreach (var variation in buildingComponents.BuildingVariations) + { + var yOffset = 0; + foreach (var variationItem in variation) + { + var group = imageTable.Groups[variationItem]; + var numDirections = 4; + for (var i = 0; i < numDirections; ++i) + { + var x = new ImageViewModel(group.GraphicsElements[i], paletteMap); + x.XOffset += (i + 1) * 128; + x.YOffset += baseY + yOffset; + LayeredImages.Add(x); + } + + yOffset -= buildingComponents.BuildingHeights[variationItem]; + } + + baseY += 128; + } + } + + _ = this.WhenAnyValue(x => x.OffsetSpacing) + .Where(_ => LayeredImages.Count > 0) + .Subscribe(spacing => + { + var index = 0; + foreach (var img in LayeredImages) + { + var diff = OffsetSpacing - prevOffset; + img.YOffset -= diff * index; + img.RaisePropertyChanged(nameof(img.YOffset)); + img.RaisePropertyChanged(nameof(img)); + index++; + } + + prevOffset = OffsetSpacing; + this.RaisePropertyChanged(nameof(LayeredImages)); + }); + PaletteMap = paletteMap; Logger = logger; @@ -89,15 +145,6 @@ public ImageTableViewModel(ImageTable imageTable, PaletteMap paletteMap, ILogger .Subscribe(_ => RecolourImages(SelectedPrimarySwatch, SelectedSecondarySwatch)); _ = this.WhenAnyValue(o => o.SelectedSecondarySwatch).Skip(1) .Subscribe(_ => RecolourImages(SelectedPrimarySwatch, SelectedSecondarySwatch)); - - //_ = this.WhenAnyValue(o => o.SelectedImageIndex) - // .Subscribe(index => - // { - // SelectedImage = SelectedImageIndexIsValid() - // ? ImageViewModels[SelectedImageIndex] - // : null; - // }); - _ = this.WhenAnyValue(o => o.AnimationSpeed) .Where(_ => animationTimer != null) .Subscribe(_ => animationTimer!.Interval = TimeSpan.FromMilliseconds(1000 / AnimationSpeed)); @@ -110,20 +157,20 @@ public ImageTableViewModel(ImageTable imageTable, PaletteMap paletteMap, ILogger ZeroOffsetAllImagesCommand = ReactiveCommand.Create(() => { - //foreach (var ivm in ImageViewModels) - //{ - // ivm.XOffset = 0; - // ivm.YOffset = 0; - //} + foreach (var ivm in GroupedImageViewModels.SelectMany(x => x.Images)) + { + ivm.XOffset = 0; + ivm.YOffset = 0; + } }); CenterOffsetAllImagesCommand = ReactiveCommand.Create(() => { - //foreach (var ivm in ImageViewModels) - //{ - // ivm.XOffset = (short)(-ivm.Width / 2); - // ivm.YOffset = (short)(-ivm.Height / 2); - //} + foreach (var ivm in GroupedImageViewModels.SelectMany(x => x.Images)) + { + ivm.XOffset = (short)(-ivm.Width / 2); + ivm.YOffset = (short)(-ivm.Height / 2); + } }); SelectionModel = new SelectionModel @@ -223,39 +270,29 @@ public async Task ReplaceImage() return; } - //ImageViewModels[SelectedImageIndex].UnderlyingImage = Image.Load(filename); + _ = SelectedImage?.UnderlyingImage = Image.Load(filename); } // model stuff public void RecolourImages(ColourRemapSwatch primary, ColourRemapSwatch secondary) { - //if (SelectedImageIndexIsValid()) - //{ - // return; - //} - - //foreach (var ivm in ImageViewModels) - //{ - // ivm.RecolourImage(primary, secondary); - //} + foreach (var ivm in GroupedImageViewModels.SelectMany(x => x.Images)) + { + ivm.RecolourImage(primary, secondary); + } } public void CropImage() { - //if (!SelectedImageIndexIsValid()) - //{ - // return; - //} - - //ImageViewModels[SelectedImageIndex].CropImage(); + SelectedImage?.CropImage(); } public void CropAllImages() { - //foreach (var ivm in ImageViewModels) - //{ - // ivm.CropImage(); - //} + foreach (var ivm in GroupedImageViewModels.SelectMany(x => x.Images)) + { + ivm.CropImage(); + } } public static string TrimZeroes(string str) @@ -266,68 +303,75 @@ public static string TrimZeroes(string str) public async Task ImportImages(string directory) { - //if (string.IsNullOrEmpty(directory)) - //{ - // Logger.Error($"Directory is invalid: \"{directory}\""); - // return; - //} - - //if (!Directory.Exists(directory)) - //{ - // Logger.Error($"Directory does not exist: \"{directory}\""); - // return; - //} - - //Logger.Info($"Importing images from {directory}"); - - //ClearSelectionModel(); - - //try - //{ - // Logger.Debug($"{ImageViewModels.Count} images in current object"); - // ICollection offsets; - - // // check for offsets file - // var offsetsFile = Path.Combine(directory, "sprites.json"); - // if (File.Exists(offsetsFile)) - // { - // offsets = await JsonFile.DeserializeFromFileAsync>(offsetsFile); // sprites.json is an unnamed array so we need ICollection here, not IEnumerable - // ArgumentNullException.ThrowIfNull(offsets); - // Logger.Debug($"Found sprites.json file with {offsets.Count} images"); - // } - // else - // { - // var files = Directory.GetFiles(directory, "*.png", SearchOption.AllDirectories); - // var sanitised = files.Select(TrimZeroes).ToList(); - - // offsets = [.. ImageViewModels - // .Select((x, i) => new GraphicsElementJson(sanitised[i], (short)x.XOffset, (short)x.YOffset, string.Empty)) - // .Fill(files.Length, GraphicsElementJson.Zero)]; - - // Logger.Debug($"Didn't find sprites.json file, using existing GraphicsElement offsets with {offsets.Count} images"); - // } - - // // clear existing images - // Logger.Debug("Clearing current images"); - // ImageViewModels.Clear(); - - // // load files - // foreach (var (offset, i) in offsets.Select((x, i) => (x, i))) - // { - // var is1Pixel = string.IsNullOrEmpty(offset.Path); - // var img = is1Pixel ? OnePixelTransparent : Image.Load(Path.Combine(directory, offset.Path)); - // var newOffset = is1Pixel ? offset with { Flags = GraphicsElementFlags.HasTransparency } : offset; - // var graphicsElement = GraphicsElementFromImage(newOffset, img, PaletteMap); - // graphicsElement.Name = string.IsNullOrEmpty(graphicsElement.Name) ? GetImageName(i) : graphicsElement.Name; - // ImageViewModels.Add(new ImageViewModel(graphicsElement, PaletteMap)); - // } - - // Logger.Debug($"Imported {ImageViewModels.Count} images successfully"); - //} - //catch (Exception ex) - //{ - // Logger.Error(ex); - //} + if (string.IsNullOrEmpty(directory)) + { + Logger.Error($"Directory is invalid: \"{directory}\""); + return; + } + + if (!Directory.Exists(directory)) + { + Logger.Error($"Directory does not exist: \"{directory}\""); + return; + } + + Logger.Info($"Importing images from {directory}"); + + ClearSelectionModel(); + + try + { + ICollection offsets; + + // check for offsets file + var offsetsFile = Path.Combine(directory, "sprites.json"); + if (File.Exists(offsetsFile)) + { + offsets = await JsonFile.DeserializeFromFileAsync>(offsetsFile); // sprites.json is an unnamed array so we need ICollection here, not IEnumerable + ArgumentNullException.ThrowIfNull(offsets); + Logger.Debug($"Found sprites.json file with {offsets.Count} images"); + } + else + { + var files = Directory.GetFiles(directory, "*.png", SearchOption.AllDirectories); + var sanitised = files.Select(TrimZeroes).ToList(); + + offsets = [.. GroupedImageViewModels.SelectMany(x => x.Images) + .Select((x, i) => new GraphicsElementJson(sanitised[i], (short)x.XOffset, (short)x.YOffset, x.Name)) + .Fill(files.Length, GraphicsElementJson.Zero)]; + + Logger.Debug($"Didn't find sprites.json file, using existing GraphicsElement offsets with {offsets.Count} images"); + } + + // clear existing images + Logger.Debug("Clearing current images"); + GroupedImageViewModels.Clear(); + + // load files + var currentGroup = 0; + var currentGroupIndex = 0; + foreach (var (offset, i) in offsets.Select((x, i) => (x, i))) + { + var is1Pixel = string.IsNullOrEmpty(offset.Path); + var img = is1Pixel ? OnePixelTransparent : Image.Load(Path.Combine(directory, offset.Path)); + var newOffset = is1Pixel ? offset with { Flags = GraphicsElementFlags.HasTransparency } : offset; + var graphicsElement = GraphicsElementFromImage(newOffset, img, PaletteMap); + graphicsElement.Name = string.IsNullOrEmpty(graphicsElement.Name) ? DefaultImageTableNameProvider.GetImageName(i) : graphicsElement.Name; + + GroupedImageViewModels[currentGroup].Images.Add(new ImageViewModel(graphicsElement, PaletteMap)); + currentGroupIndex++; + if (currentGroupIndex >= GroupedImageViewModels[currentGroup].Images.Count) + { + currentGroup++; + currentGroupIndex = 0; + } + + } + } + catch (Exception ex) + { + Logger.Error(ex); + } } static GraphicsElement GraphicsElementFromImage(GraphicsElementJson ele, Image img, PaletteMap paletteMap) @@ -350,37 +394,39 @@ static GraphicsElement GraphicsElementFromImage(GraphicsElementJson ele, Image(); - - //foreach (var image in ImageViewModels) - //{ - // var imageName = counter.ToString(); // todo: maybe use image name provider below (but number must still exist) - // counter++; - - // var fileName = $"{imageName}.png"; - // var path = Path.Combine(directory, fileName); - // await image.UnderlyingImage.SaveAsPngAsync(path); - - // offsets.Add(new GraphicsElementJson(fileName, image.ToGraphicsElement())); - //} - - //var offsetsFile = Path.Combine(directory, "sprites.json"); - //Logger.Info($"Saving sprite offsets to {offsetsFile}"); - //await JsonFile.SerializeToFileAsync(offsets, offsetsFile); + if (string.IsNullOrEmpty(directory)) + { + Logger.Error($"Directory is invalid: \"{directory}\""); + return; + } + + if (!Directory.Exists(directory)) + { + Logger.Error($"Directory does not exist: \"{directory}\""); + return; + } + + Logger.Info($"Exporting images to {directory}"); + + var counter = 0; + var offsets = new List(); + + foreach (var group in GroupedImageViewModels) + { + foreach (var image in group.Images) + { + var imageName = image.Name.Trim().ToLower().Replace(' ', '-'); + var groupName = group.GroupName.Trim().ToLower().Replace(' ', '-'); + var fileName = $"{groupName}_{imageName}.png"; + var path = Path.Combine(directory, fileName); + await image.UnderlyingImage.SaveAsPngAsync(path); + + offsets.Add(new GraphicsElementJson(fileName, image.ToGraphicsElement())); + } + } + + var offsetsFile = Path.Combine(directory, "sprites.json"); + Logger.Info($"Saving sprite offsets to {offsetsFile}"); + await JsonFile.SerializeToFileAsync(offsets, offsetsFile); } } diff --git a/Gui/Views/ImageTableView.axaml b/Gui/Views/ImageTableView.axaml index bafd274c..b1de18dc 100644 --- a/Gui/Views/ImageTableView.axaml +++ b/Gui/Views/ImageTableView.axaml @@ -140,15 +140,46 @@ - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + --> + + + + --> + + diff --git a/Gui/Views/WindowInteractionExtensions.cs b/Gui/Views/WindowInteractionExtensions.cs index 030c75eb..98609326 100644 --- a/Gui/Views/WindowInteractionExtensions.cs +++ b/Gui/Views/WindowInteractionExtensions.cs @@ -7,7 +7,7 @@ namespace Gui.Views; public static class WindowInteractionExtensions { public static async Task DoShowDialogAsync(this Window owner, IInteractionContext interaction) - where TWindow : Window, new() + where TWindow : Window, new() { var dialog = new TWindow { diff --git a/ImageConversion/ImageConversion.csproj b/ImageConversion/ImageConversion.csproj new file mode 100644 index 00000000..752e02a9 --- /dev/null +++ b/ImageConversion/ImageConversion.csproj @@ -0,0 +1,15 @@ + + + + Exe + net10.0 + enable + enable + win-x64 + + + + + + + diff --git a/ImageConversion/Program.cs b/ImageConversion/Program.cs new file mode 100644 index 00000000..cd8287b3 --- /dev/null +++ b/ImageConversion/Program.cs @@ -0,0 +1,40 @@ +using System.Drawing; +using System.Drawing.Imaging; + +foreach (var file in Directory.EnumerateFiles(AppContext.BaseDirectory, "*.png", SearchOption.AllDirectories)) +{ + try + { + Bitmap newBitmap; + + using (var originalBitmap = new Bitmap(file)) + { + newBitmap = new Bitmap(originalBitmap.Width, originalBitmap.Height, PixelFormat.Format32bppArgb); + + for (var y = 0; y < originalBitmap.Height; y++) + { + for (var x = 0; x < originalBitmap.Width; x++) + { + var pixelColor = originalBitmap.GetPixel(x, y); + if (pixelColor.R == 0 && pixelColor.G == 0 && pixelColor.B == 255) + { + newBitmap.SetPixel(x, y, Color.Transparent); + } + else + { + newBitmap.SetPixel(x, y, pixelColor); + } + } + } + } + + newBitmap.Save(file, ImageFormat.Png); + + Console.WriteLine($"Processed {Path.GetFileName(file)} and saved successfully!"); + } + catch (Exception ex) + { + Console.WriteLine("An error occurred: " + ex.Message); + Console.WriteLine($"Please make sure {Path.GetFileName(file)} exists and is a valid PNG image file."); + } +} diff --git a/ObjectEditor.sln b/ObjectEditor.sln index b744cb00..827f80bf 100644 --- a/ObjectEditor.sln +++ b/ObjectEditor.sln @@ -43,6 +43,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DatabaseImporter", "Databas EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Index", "Index\Index.csproj", "{26FB082F-DEC4-4501-907F-16E99C4D6442}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageConversion", "ImageConversion\ImageConversion.csproj", "{EA404621-9BBC-48E1-B956-CD9F99939F91}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -185,6 +187,18 @@ Global {26FB082F-DEC4-4501-907F-16E99C4D6442}.Release|x64.Build.0 = Release|Any CPU {26FB082F-DEC4-4501-907F-16E99C4D6442}.Release|x86.ActiveCfg = Release|Any CPU {26FB082F-DEC4-4501-907F-16E99C4D6442}.Release|x86.Build.0 = Release|Any CPU + {EA404621-9BBC-48E1-B956-CD9F99939F91}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EA404621-9BBC-48E1-B956-CD9F99939F91}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EA404621-9BBC-48E1-B956-CD9F99939F91}.Debug|x64.ActiveCfg = Debug|Any CPU + {EA404621-9BBC-48E1-B956-CD9F99939F91}.Debug|x64.Build.0 = Debug|Any CPU + {EA404621-9BBC-48E1-B956-CD9F99939F91}.Debug|x86.ActiveCfg = Debug|Any CPU + {EA404621-9BBC-48E1-B956-CD9F99939F91}.Debug|x86.Build.0 = Debug|Any CPU + {EA404621-9BBC-48E1-B956-CD9F99939F91}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EA404621-9BBC-48E1-B956-CD9F99939F91}.Release|Any CPU.Build.0 = Release|Any CPU + {EA404621-9BBC-48E1-B956-CD9F99939F91}.Release|x64.ActiveCfg = Release|Any CPU + {EA404621-9BBC-48E1-B956-CD9F99939F91}.Release|x64.Build.0 = Release|Any CPU + {EA404621-9BBC-48E1-B956-CD9F99939F91}.Release|x86.ActiveCfg = Release|Any CPU + {EA404621-9BBC-48E1-B956-CD9F99939F91}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Tests/LoadSaveTests.cs b/Tests/LoadSaveTests.cs index 114065af..3950b85e 100644 --- a/Tests/LoadSaveTests.cs +++ b/Tests/LoadSaveTests.cs @@ -118,9 +118,9 @@ void assertFunc(LocoObject obj, AirportObject struc) => Assert.Multiple(() => // Assert.That(struc.Image, Is.Zero, nameof(struc.Image)); //Assert.That(struc.ImageOffset, Is.Zero, nameof(struc.ImageOffset)); Assert.That(struc.AllowedPlaneTypes, Is.EqualTo(24), nameof(struc.AllowedPlaneTypes)); - Assert.That(struc.BuildingHeights.Count, Is.EqualTo(94), nameof(struc.BuildingHeights)); - Assert.That(struc.BuildingAnimations.Count, Is.EqualTo(94), nameof(struc.BuildingAnimations)); - Assert.That(struc.BuildingVariations.Count, Is.EqualTo(23), nameof(struc.BuildingVariations)); + Assert.That(struc.BuildingComponents.BuildingHeights.Count, Is.EqualTo(94), nameof(struc.BuildingComponents.BuildingHeights)); + Assert.That(struc.BuildingComponents.BuildingAnimations.Count, Is.EqualTo(94), nameof(struc.BuildingComponents.BuildingAnimations)); + Assert.That(struc.BuildingComponents.BuildingVariations.Count, Is.EqualTo(23), nameof(struc.BuildingComponents.BuildingVariations)); //Assert.That(struc.var_14, Is.EqualTo(0), nameof(struc.var_14)); //Assert.That(struc.var_18, Is.EqualTo(0), nameof(struc.var_18)); @@ -195,7 +195,7 @@ public void BuildingObject(string objectName) { void assertFunc(LocoObject obj, BuildingObject struc) => Assert.Multiple(() => { - Assert.That(struc.BuildingVariations.Count, Is.EqualTo(5), nameof(struc.BuildingVariations)); + Assert.That(struc.BuildingComponents.BuildingVariations.Count, Is.EqualTo(5), nameof(struc.BuildingComponents.BuildingVariations)); // CollectionAssert.AreEqual(struc.VariationHeights, Array.CreateInstance(typeof(byte), 4), nameof(struc.VariationHeights)); // VariationHeights // VariationAnimations @@ -361,8 +361,8 @@ void assertFunc(LocoObject obj, DockObject struc) => Assert.Multiple(() => Assert.That(struc.var_07, Is.Zero, nameof(struc.var_07)); //Assert.That(struc.UnkImage, Is.Zero, nameof(struc.UnkImage)); Assert.That(struc.Flags, Is.EqualTo(DockObjectFlags.None), nameof(struc.Flags)); - Assert.That(struc.BuildingAnimations.Count, Is.EqualTo(2), nameof(struc.BuildingAnimations)); - Assert.That(struc.BuildingVariations.Count, Is.EqualTo(1), nameof(struc.BuildingVariations)); + Assert.That(struc.BuildingComponents.BuildingAnimations.Count, Is.EqualTo(2), nameof(struc.BuildingComponents.BuildingAnimations)); + Assert.That(struc.BuildingComponents.BuildingVariations.Count, Is.EqualTo(1), nameof(struc.BuildingComponents.BuildingVariations)); //Assert.That(struc.var_14, Is.EqualTo(1), nameof(struc.var_14)); //Assert.That(struc.var_14, Is.EqualTo(1), nameof(struc.var_18)); @@ -402,46 +402,46 @@ void assertFunc(LocoObject obj, IndustryObject struc) => Assert.Multiple(() => // Buildings Assert.That(struc.BuildingSizeFlags, Is.EqualTo(7), nameof(struc.BuildingSizeFlags)); // BuildingPartHeights - Assert.That(struc.BuildingHeights, Is.EqualTo(new List() { 0, 56, 0, 66, 0, 122, 0, 48, 0, 36 })); + Assert.That(struc.BuildingComponents.BuildingHeights, Is.EqualTo(new List() { 0, 56, 0, 66, 0, 122, 0, 48, 0, 36 })); // BuildingPartAnimations - Assert.That(struc.BuildingAnimations, Has.Count.EqualTo(10)); - Assert.That(struc.BuildingAnimations[0].NumFrames, Is.EqualTo(1)); - Assert.That(struc.BuildingAnimations[0].AnimationSpeed, Is.Zero); - Assert.That(struc.BuildingAnimations[1].NumFrames, Is.EqualTo(1)); - Assert.That(struc.BuildingAnimations[1].AnimationSpeed, Is.Zero); - Assert.That(struc.BuildingAnimations[2].NumFrames, Is.EqualTo(1)); - Assert.That(struc.BuildingAnimations[2].AnimationSpeed, Is.Zero); - Assert.That(struc.BuildingAnimations[3].NumFrames, Is.EqualTo(1)); - Assert.That(struc.BuildingAnimations[3].AnimationSpeed, Is.Zero); - Assert.That(struc.BuildingAnimations[4].NumFrames, Is.EqualTo(1)); - Assert.That(struc.BuildingAnimations[4].AnimationSpeed, Is.Zero); - Assert.That(struc.BuildingAnimations[5].NumFrames, Is.EqualTo(1)); - Assert.That(struc.BuildingAnimations[5].AnimationSpeed, Is.Zero); - Assert.That(struc.BuildingAnimations[6].NumFrames, Is.EqualTo(1)); - Assert.That(struc.BuildingAnimations[6].AnimationSpeed, Is.Zero); - Assert.That(struc.BuildingAnimations[7].NumFrames, Is.EqualTo(1)); - Assert.That(struc.BuildingAnimations[7].AnimationSpeed, Is.Zero); - Assert.That(struc.BuildingAnimations[8].NumFrames, Is.EqualTo(1)); - Assert.That(struc.BuildingAnimations[8].AnimationSpeed, Is.Zero); - Assert.That(struc.BuildingAnimations[9].NumFrames, Is.EqualTo(1)); - Assert.That(struc.BuildingAnimations[9].AnimationSpeed, Is.Zero); + Assert.That(struc.BuildingComponents.BuildingAnimations, Has.Count.EqualTo(10)); + Assert.That(struc.BuildingComponents.BuildingAnimations[0].NumFrames, Is.EqualTo(1)); + Assert.That(struc.BuildingComponents.BuildingAnimations[0].AnimationSpeed, Is.Zero); + Assert.That(struc.BuildingComponents.BuildingAnimations[1].NumFrames, Is.EqualTo(1)); + Assert.That(struc.BuildingComponents.BuildingAnimations[1].AnimationSpeed, Is.Zero); + Assert.That(struc.BuildingComponents.BuildingAnimations[2].NumFrames, Is.EqualTo(1)); + Assert.That(struc.BuildingComponents.BuildingAnimations[2].AnimationSpeed, Is.Zero); + Assert.That(struc.BuildingComponents.BuildingAnimations[3].NumFrames, Is.EqualTo(1)); + Assert.That(struc.BuildingComponents.BuildingAnimations[3].AnimationSpeed, Is.Zero); + Assert.That(struc.BuildingComponents.BuildingAnimations[4].NumFrames, Is.EqualTo(1)); + Assert.That(struc.BuildingComponents.BuildingAnimations[4].AnimationSpeed, Is.Zero); + Assert.That(struc.BuildingComponents.BuildingAnimations[5].NumFrames, Is.EqualTo(1)); + Assert.That(struc.BuildingComponents.BuildingAnimations[5].AnimationSpeed, Is.Zero); + Assert.That(struc.BuildingComponents.BuildingAnimations[6].NumFrames, Is.EqualTo(1)); + Assert.That(struc.BuildingComponents.BuildingAnimations[6].AnimationSpeed, Is.Zero); + Assert.That(struc.BuildingComponents.BuildingAnimations[7].NumFrames, Is.EqualTo(1)); + Assert.That(struc.BuildingComponents.BuildingAnimations[7].AnimationSpeed, Is.Zero); + Assert.That(struc.BuildingComponents.BuildingAnimations[8].NumFrames, Is.EqualTo(1)); + Assert.That(struc.BuildingComponents.BuildingAnimations[8].AnimationSpeed, Is.Zero); + Assert.That(struc.BuildingComponents.BuildingAnimations[9].NumFrames, Is.EqualTo(1)); + Assert.That(struc.BuildingComponents.BuildingAnimations[9].AnimationSpeed, Is.Zero); // BuildingParts - Assert.That(struc.BuildingVariations, Has.Count.EqualTo(5)); - Assert.That(struc.BuildingVariations[0], Has.Count.EqualTo(2)); - Assert.That(struc.BuildingVariations[0][0], Is.Zero); - Assert.That(struc.BuildingVariations[0][1], Is.EqualTo(1)); - Assert.That(struc.BuildingVariations[1], Has.Count.EqualTo(2)); - Assert.That(struc.BuildingVariations[1][0], Is.EqualTo(2)); - Assert.That(struc.BuildingVariations[1][1], Is.EqualTo(3)); - Assert.That(struc.BuildingVariations[2], Has.Count.EqualTo(2)); - Assert.That(struc.BuildingVariations[2][0], Is.EqualTo(4)); - Assert.That(struc.BuildingVariations[2][1], Is.EqualTo(5)); - Assert.That(struc.BuildingVariations[3], Has.Count.EqualTo(2)); - Assert.That(struc.BuildingVariations[3][0], Is.EqualTo(6)); - Assert.That(struc.BuildingVariations[3][1], Is.EqualTo(7)); - Assert.That(struc.BuildingVariations[4], Has.Count.EqualTo(2)); - Assert.That(struc.BuildingVariations[4][0], Is.EqualTo(8)); - Assert.That(struc.BuildingVariations[4][1], Is.EqualTo(9)); + Assert.That(struc.BuildingComponents.BuildingVariations, Has.Count.EqualTo(5)); + Assert.That(struc.BuildingComponents.BuildingVariations[0], Has.Count.EqualTo(2)); + Assert.That(struc.BuildingComponents.BuildingVariations[0][0], Is.Zero); + Assert.That(struc.BuildingComponents.BuildingVariations[0][1], Is.EqualTo(1)); + Assert.That(struc.BuildingComponents.BuildingVariations[1], Has.Count.EqualTo(2)); + Assert.That(struc.BuildingComponents.BuildingVariations[1][0], Is.EqualTo(2)); + Assert.That(struc.BuildingComponents.BuildingVariations[1][1], Is.EqualTo(3)); + Assert.That(struc.BuildingComponents.BuildingVariations[2], Has.Count.EqualTo(2)); + Assert.That(struc.BuildingComponents.BuildingVariations[2][0], Is.EqualTo(4)); + Assert.That(struc.BuildingComponents.BuildingVariations[2][1], Is.EqualTo(5)); + Assert.That(struc.BuildingComponents.BuildingVariations[3], Has.Count.EqualTo(2)); + Assert.That(struc.BuildingComponents.BuildingVariations[3][0], Is.EqualTo(6)); + Assert.That(struc.BuildingComponents.BuildingVariations[3][1], Is.EqualTo(7)); + Assert.That(struc.BuildingComponents.BuildingVariations[4], Has.Count.EqualTo(2)); + Assert.That(struc.BuildingComponents.BuildingVariations[4][0], Is.EqualTo(8)); + Assert.That(struc.BuildingComponents.BuildingVariations[4][1], Is.EqualTo(9)); // Rest of object Assert.That(struc.SellCostFactor, Is.EqualTo(240), nameof(struc.SellCostFactor)); Assert.That(struc.BuildCostFactor, Is.EqualTo(400), nameof(struc.BuildCostFactor)); @@ -454,9 +454,9 @@ void assertFunc(LocoObject obj, IndustryObject struc) => Assert.Multiple(() => Assert.That(struc.InitialProductionRate[1].Max, Is.Zero); Assert.That(struc.MaxNumBuildings, Is.EqualTo(11), nameof(struc.MaxNumBuildings)); Assert.That(struc.MinNumBuildings, Is.EqualTo(9), nameof(struc.MinNumBuildings)); - Assert.That(struc.BuildingHeights.Count, Is.EqualTo(10), nameof(struc.BuildingHeights)); - Assert.That(struc.BuildingAnimations.Count, Is.EqualTo(10), nameof(struc.BuildingAnimations)); - Assert.That(struc.BuildingVariations.Count, Is.EqualTo(5), nameof(struc.BuildingVariations)); + Assert.That(struc.BuildingComponents.BuildingHeights.Count, Is.EqualTo(10), nameof(struc.BuildingComponents.BuildingHeights)); + Assert.That(struc.BuildingComponents.BuildingAnimations.Count, Is.EqualTo(10), nameof(struc.BuildingComponents.BuildingAnimations)); + Assert.That(struc.BuildingComponents.BuildingVariations.Count, Is.EqualTo(5), nameof(struc.BuildingComponents.BuildingVariations)); Assert.That(struc.ObsoleteYear, Is.EqualTo(65535), nameof(struc.ObsoleteYear)); Assert.That(struc.MapColour, Is.EqualTo(Colour.Yellow), nameof(struc.MapColour)); // ProducedCargo @@ -504,25 +504,25 @@ void assertFunc(LocoObject obj, IndustryObject struc) => Assert.Multiple(() => //Assert.That(struc._BuildingWall, Is.Zero, nameof(struc._BuildingWall)); //Assert.That(struc._BuildingWallEntrance, Is.Zero, nameof(struc._BuildingWallEntrance)); // BuildingPartHeights - Assert.That(struc.BuildingHeights, Is.EqualTo(new List() { 0, 166, 0, 64, })); + Assert.That(struc.BuildingComponents.BuildingHeights, Is.EqualTo(new List() { 0, 166, 0, 64, })); // BuildingPartAnimations - Assert.That(struc.BuildingAnimations, Has.Count.EqualTo(4)); - Assert.That(struc.BuildingAnimations[0].NumFrames, Is.EqualTo(1)); - Assert.That(struc.BuildingAnimations[0].AnimationSpeed, Is.Zero); - Assert.That(struc.BuildingAnimations[1].NumFrames, Is.EqualTo(1)); - Assert.That(struc.BuildingAnimations[1].AnimationSpeed, Is.Zero); - Assert.That(struc.BuildingAnimations[2].NumFrames, Is.EqualTo(1)); - Assert.That(struc.BuildingAnimations[2].AnimationSpeed, Is.Zero); - Assert.That(struc.BuildingAnimations[3].NumFrames, Is.EqualTo(1)); - Assert.That(struc.BuildingAnimations[3].AnimationSpeed, Is.Zero); + Assert.That(struc.BuildingComponents.BuildingAnimations, Has.Count.EqualTo(4)); + Assert.That(struc.BuildingComponents.BuildingAnimations[0].NumFrames, Is.EqualTo(1)); + Assert.That(struc.BuildingComponents.BuildingAnimations[0].AnimationSpeed, Is.Zero); + Assert.That(struc.BuildingComponents.BuildingAnimations[1].NumFrames, Is.EqualTo(1)); + Assert.That(struc.BuildingComponents.BuildingAnimations[1].AnimationSpeed, Is.Zero); + Assert.That(struc.BuildingComponents.BuildingAnimations[2].NumFrames, Is.EqualTo(1)); + Assert.That(struc.BuildingComponents.BuildingAnimations[2].AnimationSpeed, Is.Zero); + Assert.That(struc.BuildingComponents.BuildingAnimations[3].NumFrames, Is.EqualTo(1)); + Assert.That(struc.BuildingComponents.BuildingAnimations[3].AnimationSpeed, Is.Zero); // BuildingParts - Assert.That(struc.BuildingVariations, Has.Count.EqualTo(2)); - Assert.That(struc.BuildingVariations[0], Has.Count.EqualTo(2)); - Assert.That(struc.BuildingVariations[0][0], Is.Zero); - Assert.That(struc.BuildingVariations[0][1], Is.EqualTo(1)); - Assert.That(struc.BuildingVariations[1], Has.Count.EqualTo(2)); - Assert.That(struc.BuildingVariations[1][0], Is.EqualTo(2)); - Assert.That(struc.BuildingVariations[1][1], Is.EqualTo(3)); + Assert.That(struc.BuildingComponents.BuildingVariations, Has.Count.EqualTo(2)); + Assert.That(struc.BuildingComponents.BuildingVariations[0], Has.Count.EqualTo(2)); + Assert.That(struc.BuildingComponents.BuildingVariations[0][0], Is.Zero); + Assert.That(struc.BuildingComponents.BuildingVariations[0][1], Is.EqualTo(1)); + Assert.That(struc.BuildingComponents.BuildingVariations[1], Has.Count.EqualTo(2)); + Assert.That(struc.BuildingComponents.BuildingVariations[1][0], Is.EqualTo(2)); + Assert.That(struc.BuildingComponents.BuildingVariations[1][1], Is.EqualTo(3)); // Rest of object Assert.That(struc.SellCostFactor, Is.EqualTo(240), nameof(struc.SellCostFactor)); @@ -536,9 +536,9 @@ void assertFunc(LocoObject obj, IndustryObject struc) => Assert.Multiple(() => Assert.That(struc.InitialProductionRate[1].Max, Is.Zero); Assert.That(struc.MaxNumBuildings, Is.EqualTo(8), nameof(struc.MaxNumBuildings)); Assert.That(struc.MinNumBuildings, Is.EqualTo(4), nameof(struc.MinNumBuildings)); - Assert.That(struc.BuildingHeights.Count, Is.EqualTo(4), nameof(struc.BuildingHeights)); - Assert.That(struc.BuildingAnimations.Count, Is.EqualTo(4), nameof(struc.BuildingAnimations)); - Assert.That(struc.BuildingVariations.Count, Is.EqualTo(2), nameof(struc.BuildingVariations)); + Assert.That(struc.BuildingComponents.BuildingHeights.Count, Is.EqualTo(4), nameof(struc.BuildingComponents.BuildingHeights)); + Assert.That(struc.BuildingComponents.BuildingAnimations.Count, Is.EqualTo(4), nameof(struc.BuildingComponents.BuildingAnimations)); + Assert.That(struc.BuildingComponents.BuildingVariations.Count, Is.EqualTo(2), nameof(struc.BuildingComponents.BuildingVariations)); Assert.That(struc.ObsoleteYear, Is.EqualTo(65535), nameof(struc.ObsoleteYear)); Assert.That(struc.MapColour, Is.EqualTo(Colour.MutedPurple), nameof(struc.MapColour)); // ProducedCargo From f2c50408cc4b7c742a3bfc7d4e9b1028cd0d0563 Mon Sep 17 00:00:00 2001 From: Benjamin Sutas Date: Mon, 1 Sep 2025 00:47:44 +1000 Subject: [PATCH 05/21] code cleanup --- .editorconfig | 2 +- Dat/Converters/GraphicsElementConverter.cs | 1 - Dat/FileParsing/LocoBinaryReader.cs | 1 - Dat/Loaders/AirportObjectLoader.cs | 2 + Dat/Loaders/IndustryObjectLoader.cs | 6 +- Dat/Loaders/RegionObjectLoader.cs | 2 + Dat/Loaders/SteamObjectLoader.cs | 2 + .../DtoStringTableDescriptorComparer.cs | 1 + .../ObjectModels/IImageTableNameProvider.cs | 1 - Definitions/ObjectModels/LocoObject.cs | 1 - .../Objects/RoadStation/RoadStationObject.cs | 1 - Gui/Gui.csproj | 20 ++-- Gui/ViewModels/DatTypes/IObjectViewModel.cs | 2 - .../DatTypes/ObjectEditorViewModel.cs | 4 +- .../DatTypes/Objects/BuildingViewModel.cs | 6 +- .../DatTypes/Objects/DockViewModel.cs | 1 - .../DatTypes/Objects/IndustryViewModel.cs | 1 - .../DatTypes/Objects/TrackStationViewModel.cs | 2 - .../Graphics/ImageTableViewModel.cs | 7 +- Gui/ViewModels/MainWindowViewModel.cs | 3 - .../TableHandlers/AuthorRouteHandler.cs | 1 + .../TableHandlers/LicenceRouteHandler.cs | 1 + .../TableHandlers/RoleRouteHandler.cs | 1 + .../TableHandlers/TagRouteHandler.cs | 1 + .../TableHandlers/UserRouteHandler.cs | 1 + Tests/LoadSaveTests.cs | 108 +++++++++--------- 26 files changed, 86 insertions(+), 93 deletions(-) diff --git a/.editorconfig b/.editorconfig index f30d26b5..34c29a22 100644 --- a/.editorconfig +++ b/.editorconfig @@ -394,7 +394,7 @@ dotnet_diagnostic.IDE0078.severity = suggestion dotnet_diagnostic.IDE0080.severity = suggestion dotnet_diagnostic.IDE0082.severity = suggestion dotnet_diagnostic.IDE0083.severity = suggestion -dotnet_diagnostic.IDE0090.severity = suggestion +dotnet_diagnostic.IDE0090.severity = error dotnet_diagnostic.IDE0100.severity = suggestion dotnet_diagnostic.IDE0110.severity = suggestion dotnet_diagnostic.IDE0120.severity = suggestion diff --git a/Dat/Converters/GraphicsElementConverter.cs b/Dat/Converters/GraphicsElementConverter.cs index 162d2d80..267f9e6d 100644 --- a/Dat/Converters/GraphicsElementConverter.cs +++ b/Dat/Converters/GraphicsElementConverter.cs @@ -1,5 +1,4 @@ using Dat.Types; -using Definitions.ObjectModels; using Definitions.ObjectModels.Types; namespace Dat.Converters; diff --git a/Dat/FileParsing/LocoBinaryReader.cs b/Dat/FileParsing/LocoBinaryReader.cs index d7a79dc3..26dc135b 100644 --- a/Dat/FileParsing/LocoBinaryReader.cs +++ b/Dat/FileParsing/LocoBinaryReader.cs @@ -1,6 +1,5 @@ using Dat.Converters; using Dat.Types; -using Definitions.ObjectModels.Objects.Airport; using Definitions.ObjectModels.Objects.Common; using Definitions.ObjectModels.Objects.Sound; using Definitions.ObjectModels.Objects.Vehicle; diff --git a/Dat/Loaders/AirportObjectLoader.cs b/Dat/Loaders/AirportObjectLoader.cs index 4d5e65e3..684b0d48 100644 --- a/Dat/Loaders/AirportObjectLoader.cs +++ b/Dat/Loaders/AirportObjectLoader.cs @@ -92,6 +92,7 @@ private static void LoadVariable(LocoBinaryReader br, AirportObject model, int n var building = ByteReader.ReadLocoStruct(br.ReadBytes(StructSizes.AirportBuilding)); model.BuildingPositions.Add(building); } + br.SkipTerminator(); // movement nodes @@ -171,6 +172,7 @@ private static void SaveVariable(LocoBinaryWriter bw, AirportObject model) bw.Write(x.X); bw.Write(x.Y); } + bw.WriteTerminator(); // movement nodes diff --git a/Dat/Loaders/IndustryObjectLoader.cs b/Dat/Loaders/IndustryObjectLoader.cs index c9dc3884..3d7e8513 100644 --- a/Dat/Loaders/IndustryObjectLoader.cs +++ b/Dat/Loaders/IndustryObjectLoader.cs @@ -70,8 +70,9 @@ public static LocoObject Load(Stream stream) model.ScaffoldingColour = (Colour)br.ReadByte(); for (var i = 0; i < Constants.InitialProductionRateCount; ++i) { - model.InitialProductionRate.Add(new() { Min = br.ReadUInt16(), Max = br.ReadUInt16()}); + model.InitialProductionRate.Add(new() { Min = br.ReadUInt16(), Max = br.ReadUInt16() }); } + br.SkipByte(Constants.MaxProducedCargoType); // ProducedCargo, not part of object definition br.SkipByte(Constants.MaxRequiredCargoType); // RequiredCargo, not part of object definition model.MapColour = (Colour)br.ReadByte(); @@ -123,6 +124,7 @@ private static void LoadVariable(LocoBinaryReader br, IndustryObject model, byte { model.var_38.Add(new() { var_00 = br.ReadByte(), var_01 = br.ReadByte() }); } + br.SkipTerminator(); model.BuildingComponents.BuildingVariations = br.ReadBuildingVariations(numBuildingVariations); @@ -177,6 +179,7 @@ public static void Save(Stream stream, LocoObject obj) bw.Write(rate.Min); bw.Write(rate.Max); } + bw.WriteEmptyBytes(Constants.MaxProducedCargoType); bw.WriteEmptyBytes(Constants.MaxRequiredCargoType); bw.Write((uint8_t)model.MapColour); @@ -223,6 +226,7 @@ private static void SaveVariable(IndustryObject model, LocoBinaryWriter bw) bw.Write(x.var_00); bw.Write(x.var_01); } + bw.WriteTerminator(); bw.Write(model.BuildingComponents.BuildingVariations); diff --git a/Dat/Loaders/RegionObjectLoader.cs b/Dat/Loaders/RegionObjectLoader.cs index c1f95446..f21dc7a4 100644 --- a/Dat/Loaders/RegionObjectLoader.cs +++ b/Dat/Loaders/RegionObjectLoader.cs @@ -38,6 +38,7 @@ public static LocoObject Load(Stream stream) { model.CargoInfluenceTownFilter.Add((CargoInfluenceTownFilterType)br.ReadByte()); // Cargo influence town filter } + br.SkipByte(Constants.MaxCargoInfluenceObjects * StructSizes.CargoInfluenceTownFilterType); // Cargo influence town filter br.SkipByte(); // pad @@ -74,6 +75,7 @@ public static void Save(Stream stream, LocoObject obj) { bw.Write((uint8_t)model.CargoInfluenceTownFilter[i]); // Cargo influence town filter } + bw.Write((uint8_t)0); // pad // sanity check diff --git a/Dat/Loaders/SteamObjectLoader.cs b/Dat/Loaders/SteamObjectLoader.cs index 41c2bbdd..7384bbca 100644 --- a/Dat/Loaders/SteamObjectLoader.cs +++ b/Dat/Loaders/SteamObjectLoader.cs @@ -128,6 +128,7 @@ private static void SaveVariable(SteamObject model, LocoBinaryWriter bw) bw.Write(fit.ImageOffset); bw.Write(fit.Height); } + bw.WriteTerminator(); // end of frame info type 0 foreach (var fit in model.FrameInfoType1) @@ -135,6 +136,7 @@ private static void SaveVariable(SteamObject model, LocoBinaryWriter bw) bw.Write(fit.ImageOffset); bw.Write(fit.Height); } + bw.WriteTerminator(); // end of frame info type 1 bw.WriteS5HeaderList(model.SoundEffects); diff --git a/Definitions/DTO/Comparers/DtoStringTableDescriptorComparer.cs b/Definitions/DTO/Comparers/DtoStringTableDescriptorComparer.cs index cc95a87d..373ae15f 100644 --- a/Definitions/DTO/Comparers/DtoStringTableDescriptorComparer.cs +++ b/Definitions/DTO/Comparers/DtoStringTableDescriptorComparer.cs @@ -64,6 +64,7 @@ public int GetHashCode([DisallowNull] DtoStringTableDescriptor obj) hash = HashCode.Combine(hash, lang, text); } } + return hash; } } diff --git a/Definitions/ObjectModels/IImageTableNameProvider.cs b/Definitions/ObjectModels/IImageTableNameProvider.cs index b6c0c367..2491458d 100644 --- a/Definitions/ObjectModels/IImageTableNameProvider.cs +++ b/Definitions/ObjectModels/IImageTableNameProvider.cs @@ -1,5 +1,4 @@ using Definitions.ObjectModels.Types; -using System; namespace Definitions.ObjectModels; diff --git a/Definitions/ObjectModels/LocoObject.cs b/Definitions/ObjectModels/LocoObject.cs index 1d3c9c2a..2188d6ae 100644 --- a/Definitions/ObjectModels/LocoObject.cs +++ b/Definitions/ObjectModels/LocoObject.cs @@ -1,5 +1,4 @@ using Definitions.ObjectModels.Types; -using System.Text.RegularExpressions; namespace Definitions.ObjectModels; diff --git a/Definitions/ObjectModels/Objects/RoadStation/RoadStationObject.cs b/Definitions/ObjectModels/Objects/RoadStation/RoadStationObject.cs index ae490c30..439ad061 100644 --- a/Definitions/ObjectModels/Objects/RoadStation/RoadStationObject.cs +++ b/Definitions/ObjectModels/Objects/RoadStation/RoadStationObject.cs @@ -1,5 +1,4 @@ using Definitions.ObjectModels.Objects.Road; -using Definitions.ObjectModels.Objects.TrackStation; using Definitions.ObjectModels.Types; namespace Definitions.ObjectModels.Objects.RoadStation; diff --git a/Gui/Gui.csproj b/Gui/Gui.csproj index 4c222343..6e622b54 100644 --- a/Gui/Gui.csproj +++ b/Gui/Gui.csproj @@ -43,23 +43,23 @@ - - - + + + - - - + + + - - - + + + - + diff --git a/Gui/ViewModels/DatTypes/IObjectViewModel.cs b/Gui/ViewModels/DatTypes/IObjectViewModel.cs index 51ef0ab1..35d80186 100644 --- a/Gui/ViewModels/DatTypes/IObjectViewModel.cs +++ b/Gui/ViewModels/DatTypes/IObjectViewModel.cs @@ -1,5 +1,3 @@ -using Definitions.ObjectModels; - namespace Gui.ViewModels; // this is purely to bind to the UI elements since Avalonia XAML doesn't support binding to generic types diff --git a/Gui/ViewModels/DatTypes/ObjectEditorViewModel.cs b/Gui/ViewModels/DatTypes/ObjectEditorViewModel.cs index efadf0e2..693c64e5 100644 --- a/Gui/ViewModels/DatTypes/ObjectEditorViewModel.cs +++ b/Gui/ViewModels/DatTypes/ObjectEditorViewModel.cs @@ -5,7 +5,6 @@ using Dat.Data; using Dat.FileParsing; using Definitions.ObjectModels; -using Definitions.ObjectModels.Objects.Building; using Definitions.ObjectModels.Objects.Common; using Definitions.ObjectModels.Objects.Sound; using Gui.Models; @@ -15,7 +14,6 @@ using ReactiveUI; using ReactiveUI.Fody.Helpers; using System; -using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; using System.Linq; @@ -280,7 +278,7 @@ void SaveCore(string filename, SawyerEncoding? encodingToUse = null) if (ExtraContentViewModel is ImageTableViewModel itvm) { - //CurrentObject.LocoObject.ImageTable.GraphicsElements = itvm.ImageViewModels.Select(x => x.ToGraphicsElement()).ToList(); + CurrentObject.LocoObject.ImageTable.GraphicsElements = itvm.GroupedImageViewModels.SelectMany(x => x.Images).Select(x => x.ToGraphicsElement()).ToList(); } // this is hacky but it should work diff --git a/Gui/ViewModels/DatTypes/Objects/BuildingViewModel.cs b/Gui/ViewModels/DatTypes/Objects/BuildingViewModel.cs index 0fb255c0..42b62674 100644 --- a/Gui/ViewModels/DatTypes/Objects/BuildingViewModel.cs +++ b/Gui/ViewModels/DatTypes/Objects/BuildingViewModel.cs @@ -80,7 +80,7 @@ public BuildingViewModel(BuildingObject bo) // validation: // BuildingVariationHeights.Count MUST equal BuildingVariationAnimations.Count public override BuildingObject GetAsModel() - => new BuildingObject() + => new() { BuildingComponents = new BuildingComponents() { @@ -118,18 +118,22 @@ List GetElevatorSequences() { result.Add(ElevatorSequence1); } + if (ElevatorSequence2 != null) { result.Add(ElevatorSequence2); } + if (ElevatorSequence3 != null) { result.Add(ElevatorSequence3); } + if (ElevatorSequence4 != null) { result.Add(ElevatorSequence4); } + return result; } } diff --git a/Gui/ViewModels/DatTypes/Objects/DockViewModel.cs b/Gui/ViewModels/DatTypes/Objects/DockViewModel.cs index 4b3c72dc..e9a3155b 100644 --- a/Gui/ViewModels/DatTypes/Objects/DockViewModel.cs +++ b/Gui/ViewModels/DatTypes/Objects/DockViewModel.cs @@ -1,5 +1,4 @@ using Dat.Loaders; -using Definitions.ObjectModels.Objects.Airport; using Definitions.ObjectModels.Objects.Common; using Definitions.ObjectModels.Objects.Dock; using Definitions.ObjectModels.Types; diff --git a/Gui/ViewModels/DatTypes/Objects/IndustryViewModel.cs b/Gui/ViewModels/DatTypes/Objects/IndustryViewModel.cs index 942ea229..60c28721 100644 --- a/Gui/ViewModels/DatTypes/Objects/IndustryViewModel.cs +++ b/Gui/ViewModels/DatTypes/Objects/IndustryViewModel.cs @@ -1,5 +1,4 @@ using Dat.Loaders; -using Definitions.ObjectModels.Objects.Airport; using Definitions.ObjectModels.Objects.Common; using Definitions.ObjectModels.Objects.Industry; using Definitions.ObjectModels.Types; diff --git a/Gui/ViewModels/DatTypes/Objects/TrackStationViewModel.cs b/Gui/ViewModels/DatTypes/Objects/TrackStationViewModel.cs index 3c4767f0..ba72c8af 100644 --- a/Gui/ViewModels/DatTypes/Objects/TrackStationViewModel.cs +++ b/Gui/ViewModels/DatTypes/Objects/TrackStationViewModel.cs @@ -1,10 +1,8 @@ using Definitions.ObjectModels.Objects.Track; using Definitions.ObjectModels.Objects.TrackStation; -using Definitions.ObjectModels.Types; using PropertyModels.ComponentModel.DataAnnotations; using System.Collections.Generic; using System.ComponentModel; -using System.Linq; namespace Gui.ViewModels; diff --git a/Gui/ViewModels/Graphics/ImageTableViewModel.cs b/Gui/ViewModels/Graphics/ImageTableViewModel.cs index 976d12c0..86e23019 100644 --- a/Gui/ViewModels/Graphics/ImageTableViewModel.cs +++ b/Gui/ViewModels/Graphics/ImageTableViewModel.cs @@ -6,7 +6,6 @@ using Definitions.ObjectModels; using Definitions.ObjectModels.Objects.Common; using Definitions.ObjectModels.Types; -using DynamicData; using ReactiveUI; using ReactiveUI.Fody.Helpers; using SixLabors.ImageSharp; @@ -283,9 +282,7 @@ public void RecolourImages(ColourRemapSwatch primary, ColourRemapSwatch secondar } public void CropImage() - { - SelectedImage?.CropImage(); - } + => SelectedImage?.CropImage(); public void CropAllImages() { @@ -365,7 +362,6 @@ public async Task ImportImages(string directory) currentGroup++; currentGroupIndex = 0; } - } } catch (Exception ex) @@ -408,7 +404,6 @@ public async Task ExportImages(string directory) Logger.Info($"Exporting images to {directory}"); - var counter = 0; var offsets = new List(); foreach (var group in GroupedImageViewModels) diff --git a/Gui/ViewModels/MainWindowViewModel.cs b/Gui/ViewModels/MainWindowViewModel.cs index 0a013d77..21ead87e 100644 --- a/Gui/ViewModels/MainWindowViewModel.cs +++ b/Gui/ViewModels/MainWindowViewModel.cs @@ -24,9 +24,6 @@ #if !DEBUG using Common; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Text.Json; #endif namespace Gui.ViewModels; diff --git a/ObjectService/RouteHandlers/TableHandlers/AuthorRouteHandler.cs b/ObjectService/RouteHandlers/TableHandlers/AuthorRouteHandler.cs index 9135b182..fcd838a3 100644 --- a/ObjectService/RouteHandlers/TableHandlers/AuthorRouteHandler.cs +++ b/ObjectService/RouteHandlers/TableHandlers/AuthorRouteHandler.cs @@ -36,6 +36,7 @@ public static bool TryValidateCreate(DtoAuthorEntry request, [FromServices] Loco result = Results.BadRequest("Cannot add an empty or whitespace-only name."); return false; } + result = null; return true; } diff --git a/ObjectService/RouteHandlers/TableHandlers/LicenceRouteHandler.cs b/ObjectService/RouteHandlers/TableHandlers/LicenceRouteHandler.cs index 8605b6df..7820e83e 100644 --- a/ObjectService/RouteHandlers/TableHandlers/LicenceRouteHandler.cs +++ b/ObjectService/RouteHandlers/TableHandlers/LicenceRouteHandler.cs @@ -39,6 +39,7 @@ public static bool TryValidateCreate([FromBody] DtoLicenceEntry request, [FromSe result = Results.BadRequest("Cannot add an empty or whitespace-only name."); return false; } + result = null; return true; } diff --git a/ObjectService/RouteHandlers/TableHandlers/RoleRouteHandler.cs b/ObjectService/RouteHandlers/TableHandlers/RoleRouteHandler.cs index e1f12c33..7912ea59 100644 --- a/ObjectService/RouteHandlers/TableHandlers/RoleRouteHandler.cs +++ b/ObjectService/RouteHandlers/TableHandlers/RoleRouteHandler.cs @@ -36,6 +36,7 @@ public static bool TryValidateCreate(DtoRoleEntry request, [FromServices] LocoDb result = Results.BadRequest("Cannot add an empty or whitespace-only name."); return false; } + result = null; return true; } diff --git a/ObjectService/RouteHandlers/TableHandlers/TagRouteHandler.cs b/ObjectService/RouteHandlers/TableHandlers/TagRouteHandler.cs index 68a0fc87..500e68be 100644 --- a/ObjectService/RouteHandlers/TableHandlers/TagRouteHandler.cs +++ b/ObjectService/RouteHandlers/TableHandlers/TagRouteHandler.cs @@ -36,6 +36,7 @@ public static bool TryValidateCreate([FromBody] DtoTagEntry request, [FromServic result = Results.BadRequest("Cannot add an empty or whitespace-only name."); return false; } + result = null; return true; } diff --git a/ObjectService/RouteHandlers/TableHandlers/UserRouteHandler.cs b/ObjectService/RouteHandlers/TableHandlers/UserRouteHandler.cs index 0c4331ef..5c462dc4 100644 --- a/ObjectService/RouteHandlers/TableHandlers/UserRouteHandler.cs +++ b/ObjectService/RouteHandlers/TableHandlers/UserRouteHandler.cs @@ -36,6 +36,7 @@ public static bool TryValidateCreate([FromBody] DtoUserEntry request, [FromServi result = Results.BadRequest("Cannot add an empty or whitespace-only name."); return false; } + result = null; return true; } diff --git a/Tests/LoadSaveTests.cs b/Tests/LoadSaveTests.cs index 3950b85e..b9937321 100644 --- a/Tests/LoadSaveTests.cs +++ b/Tests/LoadSaveTests.cs @@ -78,12 +78,12 @@ static void LoadSaveGenericTest(string filename, Action assert var bytes2 = SawyerStreamWriter.WriteLocoObject(datInfo2.S5Header.Name, obj2.ObjectType, datInfo2.S5Header.ObjectSource.Convert(), datInfo2.ObjectHeader.Encoding, logger, obj2, true).ToArray(); // grab headers first - var s5Header1 = S5Header.Read(bytes1[0..S5Header.StructLength]); - var s5Header2 = S5Header.Read(bytes2[0..S5Header.StructLength]); + var s5Header1 = S5Header.Read(bytes1.AsSpan()[0..S5Header.StructLength]); + var s5Header2 = S5Header.Read(bytes2.AsSpan()[0..S5Header.StructLength]); AssertS5Headers(s5Header1, s5Header2); - var objHeader1 = ObjectHeader.Read(bytes1[S5Header.StructLength..(S5Header.StructLength + ObjectHeader.StructLength)]); - var objHeader2 = ObjectHeader.Read(bytes2[S5Header.StructLength..(S5Header.StructLength + ObjectHeader.StructLength)]); + var objHeader1 = ObjectHeader.Read(bytes1.AsSpan()[S5Header.StructLength..(S5Header.StructLength + ObjectHeader.StructLength)]); + var objHeader2 = ObjectHeader.Read(bytes2.AsSpan()[S5Header.StructLength..(S5Header.StructLength + ObjectHeader.StructLength)]); AssertObjHeaders(objHeader1, objHeader2); // then grab object bytes @@ -118,9 +118,9 @@ void assertFunc(LocoObject obj, AirportObject struc) => Assert.Multiple(() => // Assert.That(struc.Image, Is.Zero, nameof(struc.Image)); //Assert.That(struc.ImageOffset, Is.Zero, nameof(struc.ImageOffset)); Assert.That(struc.AllowedPlaneTypes, Is.EqualTo(24), nameof(struc.AllowedPlaneTypes)); - Assert.That(struc.BuildingComponents.BuildingHeights.Count, Is.EqualTo(94), nameof(struc.BuildingComponents.BuildingHeights)); - Assert.That(struc.BuildingComponents.BuildingAnimations.Count, Is.EqualTo(94), nameof(struc.BuildingComponents.BuildingAnimations)); - Assert.That(struc.BuildingComponents.BuildingVariations.Count, Is.EqualTo(23), nameof(struc.BuildingComponents.BuildingVariations)); + Assert.That(struc.BuildingComponents.BuildingHeights, Has.Count.EqualTo(94), nameof(struc.BuildingComponents.BuildingHeights)); + Assert.That(struc.BuildingComponents.BuildingAnimations, Has.Count.EqualTo(94), nameof(struc.BuildingComponents.BuildingAnimations)); + Assert.That(struc.BuildingComponents.BuildingVariations, Has.Count.EqualTo(23), nameof(struc.BuildingComponents.BuildingVariations)); //Assert.That(struc.var_14, Is.EqualTo(0), nameof(struc.var_14)); //Assert.That(struc.var_18, Is.EqualTo(0), nameof(struc.var_18)); @@ -134,8 +134,8 @@ void assertFunc(LocoObject obj, AirportObject struc) => Assert.Multiple(() => Assert.That(struc.MaxY, Is.EqualTo(5), nameof(struc.MaxY)); Assert.That(struc.DesignedYear, Is.EqualTo(1970), nameof(struc.DesignedYear)); Assert.That(struc.ObsoleteYear, Is.EqualTo(65535), nameof(struc.ObsoleteYear)); - Assert.That(struc.MovementNodes.Count, Is.EqualTo(26), nameof(struc.MovementNodes)); - Assert.That(struc.MovementEdges.Count, Is.EqualTo(30), nameof(struc.MovementEdges)); + Assert.That(struc.MovementNodes, Has.Count.EqualTo(26), nameof(struc.MovementNodes)); + Assert.That(struc.MovementEdges, Has.Count.EqualTo(30), nameof(struc.MovementEdges)); //Assert.That(struc.MovementNodes, Is.EqualTo(0), nameof(struc.MovementNodes)); //Assert.That(struc.MovementEdges, Is.EqualTo(0), nameof(struc.MovementEdges)); @@ -195,7 +195,7 @@ public void BuildingObject(string objectName) { void assertFunc(LocoObject obj, BuildingObject struc) => Assert.Multiple(() => { - Assert.That(struc.BuildingComponents.BuildingVariations.Count, Is.EqualTo(5), nameof(struc.BuildingComponents.BuildingVariations)); + Assert.That(struc.BuildingComponents.BuildingVariations, Has.Count.EqualTo(5), nameof(struc.BuildingComponents.BuildingVariations)); // CollectionAssert.AreEqual(struc.VariationHeights, Array.CreateInstance(typeof(byte), 4), nameof(struc.VariationHeights)); // VariationHeights // VariationAnimations @@ -218,7 +218,7 @@ void assertFunc(LocoObject obj, BuildingObject struc) => Assert.Multiple(() => // CollectionAssert.AreEqual(struc.var_A4, Array.CreateInstance(typeof(byte), 2), nameof(struc.var_A4)); Assert.That(struc.DemolishRatingReduction, Is.Zero, nameof(struc.DemolishRatingReduction)); Assert.That(struc.var_AC, Is.EqualTo(255), nameof(struc.var_AC)); - Assert.That(struc.ElevatorHeightSequences.Count, Is.Zero, nameof(struc.ElevatorHeightSequences)); + Assert.That(struc.ElevatorHeightSequences, Is.Empty, nameof(struc.ElevatorHeightSequences)); //Assert.That(struc.ElevatorHeightSequences[0].Count, Is.Zero, nameof(struc.ElevatorHeightSequences) + "[0]"); //Assert.That(struc.ElevatorHeightSequences[1].Count, Is.Zero, nameof(struc.ElevatorHeightSequences) + "[1]"); //Assert.That(struc.ElevatorHeightSequences[2].Count, Is.Zero, nameof(struc.ElevatorHeightSequences) + "[2]"); @@ -361,8 +361,8 @@ void assertFunc(LocoObject obj, DockObject struc) => Assert.Multiple(() => Assert.That(struc.var_07, Is.Zero, nameof(struc.var_07)); //Assert.That(struc.UnkImage, Is.Zero, nameof(struc.UnkImage)); Assert.That(struc.Flags, Is.EqualTo(DockObjectFlags.None), nameof(struc.Flags)); - Assert.That(struc.BuildingComponents.BuildingAnimations.Count, Is.EqualTo(2), nameof(struc.BuildingComponents.BuildingAnimations)); - Assert.That(struc.BuildingComponents.BuildingVariations.Count, Is.EqualTo(1), nameof(struc.BuildingComponents.BuildingVariations)); + Assert.That(struc.BuildingComponents.BuildingAnimations, Has.Count.EqualTo(2), nameof(struc.BuildingComponents.BuildingAnimations)); + Assert.That(struc.BuildingComponents.BuildingVariations, Has.Count.EqualTo(1), nameof(struc.BuildingComponents.BuildingVariations)); //Assert.That(struc.var_14, Is.EqualTo(1), nameof(struc.var_14)); //Assert.That(struc.var_14, Is.EqualTo(1), nameof(struc.var_18)); @@ -385,7 +385,7 @@ void assertFunc(LocoObject obj, HillShapesObject struc) => Assert.Multiple(() => Assert.That(struc.HillHeightMapCount, Is.EqualTo(2), nameof(struc.HillHeightMapCount)); Assert.That(struc.MountainHeightMapCount, Is.EqualTo(2), nameof(struc.MountainHeightMapCount)); //Assert.That(struc.var_08, Is.EqualTo(0), nameof(struc.var_08)); - Assert.That(struc.IsHeightMap, Is.EqualTo(false), nameof(struc.IsHeightMap)); + Assert.That(struc.IsHeightMap, Is.False, nameof(struc.IsHeightMap)); Assert.That(obj.ImageTable.GraphicsElements, Has.Count.EqualTo(5)); }); @@ -454,9 +454,9 @@ void assertFunc(LocoObject obj, IndustryObject struc) => Assert.Multiple(() => Assert.That(struc.InitialProductionRate[1].Max, Is.Zero); Assert.That(struc.MaxNumBuildings, Is.EqualTo(11), nameof(struc.MaxNumBuildings)); Assert.That(struc.MinNumBuildings, Is.EqualTo(9), nameof(struc.MinNumBuildings)); - Assert.That(struc.BuildingComponents.BuildingHeights.Count, Is.EqualTo(10), nameof(struc.BuildingComponents.BuildingHeights)); - Assert.That(struc.BuildingComponents.BuildingAnimations.Count, Is.EqualTo(10), nameof(struc.BuildingComponents.BuildingAnimations)); - Assert.That(struc.BuildingComponents.BuildingVariations.Count, Is.EqualTo(5), nameof(struc.BuildingComponents.BuildingVariations)); + Assert.That(struc.BuildingComponents.BuildingHeights, Has.Count.EqualTo(10), nameof(struc.BuildingComponents.BuildingHeights)); + Assert.That(struc.BuildingComponents.BuildingAnimations, Has.Count.EqualTo(10), nameof(struc.BuildingComponents.BuildingAnimations)); + Assert.That(struc.BuildingComponents.BuildingVariations, Has.Count.EqualTo(5), nameof(struc.BuildingComponents.BuildingVariations)); Assert.That(struc.ObsoleteYear, Is.EqualTo(65535), nameof(struc.ObsoleteYear)); Assert.That(struc.MapColour, Is.EqualTo(Colour.Yellow), nameof(struc.MapColour)); // ProducedCargo @@ -536,9 +536,9 @@ void assertFunc(LocoObject obj, IndustryObject struc) => Assert.Multiple(() => Assert.That(struc.InitialProductionRate[1].Max, Is.Zero); Assert.That(struc.MaxNumBuildings, Is.EqualTo(8), nameof(struc.MaxNumBuildings)); Assert.That(struc.MinNumBuildings, Is.EqualTo(4), nameof(struc.MinNumBuildings)); - Assert.That(struc.BuildingComponents.BuildingHeights.Count, Is.EqualTo(4), nameof(struc.BuildingComponents.BuildingHeights)); - Assert.That(struc.BuildingComponents.BuildingAnimations.Count, Is.EqualTo(4), nameof(struc.BuildingComponents.BuildingAnimations)); - Assert.That(struc.BuildingComponents.BuildingVariations.Count, Is.EqualTo(2), nameof(struc.BuildingComponents.BuildingVariations)); + Assert.That(struc.BuildingComponents.BuildingHeights, Has.Count.EqualTo(4), nameof(struc.BuildingComponents.BuildingHeights)); + Assert.That(struc.BuildingComponents.BuildingAnimations, Has.Count.EqualTo(4), nameof(struc.BuildingComponents.BuildingAnimations)); + Assert.That(struc.BuildingComponents.BuildingVariations, Has.Count.EqualTo(2), nameof(struc.BuildingComponents.BuildingVariations)); Assert.That(struc.ObsoleteYear, Is.EqualTo(65535), nameof(struc.ObsoleteYear)); Assert.That(struc.MapColour, Is.EqualTo(Colour.MutedPurple), nameof(struc.MapColour)); // ProducedCargo @@ -619,9 +619,9 @@ void assertFunc(LocoObject obj, LandObject struc) => Assert.Multiple(() => Assert.That(struc.VariationLikelihood, Is.EqualTo(10), nameof(struc.VariationLikelihood)); Assert.That(struc.CliffEdgeHeader.Name, Is.EqualTo("LSBROWN"), nameof(struc.CliffEdgeHeader)); - Assert.That(struc.CliffEdgeHeader.Checksum, Is.EqualTo(0), nameof(struc.CliffEdgeHeader)); + Assert.That(struc.CliffEdgeHeader.Checksum, Is.Zero, nameof(struc.CliffEdgeHeader)); Assert.That(struc.CliffEdgeHeader.ObjectType, Is.EqualTo(ObjectType.CliffEdge), nameof(struc.CliffEdgeHeader)); - Assert.That(struc.CliffEdgeHeader.ObjectSource, Is.EqualTo((ObjectSource)0), nameof(struc.CliffEdgeHeader)); + Assert.That(struc.CliffEdgeHeader.ObjectSource, Is.Zero, nameof(struc.CliffEdgeHeader)); Assert.That(struc.UnkObjectHeader, Is.Null, nameof(struc.UnkObjectHeader)); @@ -659,7 +659,7 @@ public void RegionObject(string objectName) void assertFunc(LocoObject obj, RegionObject struc) => Assert.Multiple(() => { //Assert.That(struc.pad_06, Is.EquivalentTo(Array.CreateInstance(typeof(byte), 2)), nameof(struc.pad_06)); - Assert.That(struc.CargoInfluenceObjects.Count, Is.EqualTo(1), nameof(struc.CargoInfluenceObjects)); + Assert.That(struc.CargoInfluenceObjects, Has.Count.EqualTo(1), nameof(struc.CargoInfluenceObjects)); Assert.That(struc.DependentObjects, Has.Count.EqualTo(239), nameof(struc.DependentObjects)); Assert.That(struc.CargoInfluenceTownFilter, Is.EquivalentTo(Enumerable.Repeat(CargoInfluenceTownFilterType.AllTowns, 4)), nameof(struc.CargoInfluenceTownFilter)); @@ -715,44 +715,44 @@ void assertFunc(LocoObject obj, RoadObject struc) => Assert.Multiple(() => | RoadTraitFlags.unk_04 | RoadTraitFlags.unk_06), nameof(struc.RoadPieces)); - Assert.That(struc.Bridges.Count, Is.EqualTo(5), nameof(struc.Bridges)); + Assert.That(struc.Bridges, Has.Count.EqualTo(5), nameof(struc.Bridges)); Assert.That(struc.Bridges[0].Name, Is.EqualTo("BRDGBRCK"), nameof(struc.Bridges)); - Assert.That(struc.Bridges[0].Checksum, Is.EqualTo(0), nameof(struc.Bridges)); + Assert.That(struc.Bridges[0].Checksum, Is.Zero, nameof(struc.Bridges)); Assert.That(struc.Bridges[0].ObjectSource, Is.EqualTo(ObjectSource.Custom), nameof(struc.Bridges)); Assert.That(struc.Bridges[0].ObjectType, Is.EqualTo(ObjectType.Bridge), nameof(struc.Bridges)); Assert.That(struc.Bridges[1].Name, Is.EqualTo("BRDGSTAR"), nameof(struc.Bridges)); - Assert.That(struc.Bridges[1].Checksum, Is.EqualTo(0), nameof(struc.Bridges)); + Assert.That(struc.Bridges[1].Checksum, Is.Zero, nameof(struc.Bridges)); Assert.That(struc.Bridges[1].ObjectSource, Is.EqualTo(ObjectSource.Custom), nameof(struc.Bridges)); Assert.That(struc.Bridges[1].ObjectType, Is.EqualTo(ObjectType.Bridge), nameof(struc.Bridges)); Assert.That(struc.Bridges[2].Name, Is.EqualTo("BRDGGIRD"), nameof(struc.Bridges)); - Assert.That(struc.Bridges[2].Checksum, Is.EqualTo(0), nameof(struc.Bridges)); + Assert.That(struc.Bridges[2].Checksum, Is.Zero, nameof(struc.Bridges)); Assert.That(struc.Bridges[2].ObjectSource, Is.EqualTo(ObjectSource.Custom), nameof(struc.Bridges)); Assert.That(struc.Bridges[2].ObjectType, Is.EqualTo(ObjectType.Bridge), nameof(struc.Bridges)); Assert.That(struc.Bridges[3].Name, Is.EqualTo("BRDGSUSP"), nameof(struc.Bridges)); - Assert.That(struc.Bridges[3].Checksum, Is.EqualTo(0), nameof(struc.Bridges)); + Assert.That(struc.Bridges[3].Checksum, Is.Zero, nameof(struc.Bridges)); Assert.That(struc.Bridges[3].ObjectSource, Is.EqualTo(ObjectSource.Custom), nameof(struc.Bridges)); Assert.That(struc.Bridges[3].ObjectType, Is.EqualTo(ObjectType.Bridge), nameof(struc.Bridges)); Assert.That(struc.Bridges[4].Name, Is.EqualTo("BRDGWOOD"), nameof(struc.Bridges)); - Assert.That(struc.Bridges[4].Checksum, Is.EqualTo(0), nameof(struc.Bridges)); + Assert.That(struc.Bridges[4].Checksum, Is.Zero, nameof(struc.Bridges)); Assert.That(struc.Bridges[4].ObjectSource, Is.EqualTo(ObjectSource.Custom), nameof(struc.Bridges)); Assert.That(struc.Bridges[4].ObjectType, Is.EqualTo(ObjectType.Bridge), nameof(struc.Bridges)); - Assert.That(struc.CompatibleTracksAndRoads.Count, Is.EqualTo(1), nameof(struc.CompatibleTracksAndRoads)); + Assert.That(struc.CompatibleTracksAndRoads, Has.Count.EqualTo(1), nameof(struc.CompatibleTracksAndRoads)); Assert.That(struc.CompatibleTracksAndRoads[0].Name, Is.EqualTo("ROADTRAM"), nameof(struc.CompatibleTracksAndRoads)); - Assert.That(struc.CompatibleTracksAndRoads[0].Checksum, Is.EqualTo(0), nameof(struc.CompatibleTracksAndRoads)); + Assert.That(struc.CompatibleTracksAndRoads[0].Checksum, Is.Zero, nameof(struc.CompatibleTracksAndRoads)); Assert.That(struc.CompatibleTracksAndRoads[0].ObjectSource, Is.EqualTo(ObjectSource.Custom), nameof(struc.CompatibleTracksAndRoads)); Assert.That(struc.CompatibleTracksAndRoads[0].ObjectType, Is.EqualTo(ObjectType.Road), nameof(struc.CompatibleTracksAndRoads)); - Assert.That(struc.RoadMods.Count, Is.Zero, nameof(struc.RoadMods)); + Assert.That(struc.RoadMods, Is.Empty, nameof(struc.RoadMods)); - Assert.That(struc.Stations.Count, Is.EqualTo(1), nameof(struc.Stations)); + Assert.That(struc.Stations, Has.Count.EqualTo(1), nameof(struc.Stations)); Assert.That(struc.Stations[0].Name, Is.EqualTo("BUSSTOP"), nameof(struc.Stations)); - Assert.That(struc.Stations[0].Checksum, Is.EqualTo(0), nameof(struc.Stations)); + Assert.That(struc.Stations[0].Checksum, Is.Zero, nameof(struc.Stations)); Assert.That(struc.Stations[0].ObjectSource, Is.EqualTo(ObjectSource.Custom), nameof(struc.Stations)); Assert.That(struc.Stations[0].ObjectType, Is.EqualTo(ObjectType.RoadStation), nameof(struc.Stations)); Assert.That(struc.Tunnel.Name, Is.EqualTo("TUNNEL2"), nameof(struc.Tunnel)); - Assert.That(struc.Tunnel.Checksum, Is.EqualTo(0), nameof(struc.Tunnel.Checksum)); + Assert.That(struc.Tunnel.Checksum, Is.Zero, nameof(struc.Tunnel.Checksum)); Assert.That(struc.Tunnel.ObjectSource, Is.EqualTo(ObjectSource.Custom), nameof(struc.Tunnel.ObjectSource)); Assert.That(struc.Tunnel.ObjectType, Is.EqualTo(ObjectType.Tunnel), nameof(struc.Tunnel.ObjectType)); @@ -773,7 +773,7 @@ void assertFunc(LocoObject obj, RoadStationObject struc) => Assert.Multiple(() = Assert.That(struc.CostIndex, Is.EqualTo(1), nameof(struc.CostIndex)); Assert.That(struc.DesignedYear, Is.Zero, nameof(struc.DesignedYear)); Assert.That(struc.Flags, Is.EqualTo(RoadStationObjectFlags.Passenger | RoadStationObjectFlags.RoadEnd), nameof(struc.Flags)); - Assert.That(struc.CompatibleRoadObjects.Count, Is.Zero, nameof(struc.CompatibleRoadObjects)); + Assert.That(struc.CompatibleRoadObjects, Is.Empty, nameof(struc.CompatibleRoadObjects)); Assert.That(struc.ObsoleteYear, Is.EqualTo(1945), nameof(struc.ObsoleteYear)); Assert.That(struc.PaintStyle, Is.Zero, nameof(struc.PaintStyle)); Assert.That(struc.RoadPieces, Is.EqualTo(RoadTraitFlags.None), nameof(struc.RoadPieces)); @@ -805,10 +805,7 @@ void assertFunc(LocoObject obj, ScaffoldingObject struc) => Assert.Multiple(() = [TestCase("STEX000.DAT")] public void ScenarioTextObject(string objectName) { - void assertFunc(LocoObject obj, ScenarioTextObject struc) => Assert.Multiple(() => - { - Assert.That(obj.ImageTable.GraphicsElements, Is.Empty); - }); + void assertFunc(LocoObject obj, ScenarioTextObject struc) => Assert.Multiple(() => Assert.That(obj.ImageTable.GraphicsElements, Is.Empty)); LoadSaveGenericTest(objectName, assertFunc); } @@ -861,16 +858,16 @@ void assertFunc(LocoObject obj, SteamObject struc) => Assert.Multiple(() => // FrameInfoType0 contents // FrameInfoType1 contents //Assert.That(struc.NumImages, Is.EqualTo(57), nameof(struc.NumImages)); - Assert.That(struc.SoundEffects.Count, Is.EqualTo(8), nameof(struc.SoundEffects)); + Assert.That(struc.SoundEffects, Has.Count.EqualTo(8), nameof(struc.SoundEffects)); Assert.That(struc.NumStationaryTicks, Is.EqualTo(2), nameof(struc.NumStationaryTicks)); // these aren't currently calculated in this tool - Assert.That(struc.SpriteWidth, Is.EqualTo(0), nameof(struc.SpriteWidth)); - Assert.That(struc.SpriteHeightNegative, Is.EqualTo(0), nameof(struc.SpriteHeightNegative)); - Assert.That(struc.SpriteHeightPositive, Is.EqualTo(0), nameof(struc.SpriteHeightPositive)); + Assert.That(struc.SpriteWidth, Is.Zero, nameof(struc.SpriteWidth)); + Assert.That(struc.SpriteHeightNegative, Is.Zero, nameof(struc.SpriteHeightNegative)); + Assert.That(struc.SpriteHeightPositive, Is.Zero, nameof(struc.SpriteHeightPositive)); - Assert.That(struc.FrameInfoType0.Count, Is.EqualTo(47), nameof(struc.FrameInfoType0)); - Assert.That(struc.FrameInfoType1.Count, Is.EqualTo(30), nameof(struc.FrameInfoType1)); + Assert.That(struc.FrameInfoType0, Has.Count.EqualTo(47), nameof(struc.FrameInfoType0)); + Assert.That(struc.FrameInfoType1, Has.Count.EqualTo(30), nameof(struc.FrameInfoType1)); Assert.That(struc.var_0A, Is.Zero, nameof(struc.var_0A)); // SoundEffects @@ -964,11 +961,11 @@ void assertFunc(LocoObject obj, TrackObject struc) => Assert.Multiple(() => Assert.That(struc.DisplayOffset, Is.EqualTo(3), nameof(struc.DisplayOffset)); Assert.That(struc.Flags, Is.EqualTo(TrackObjectFlags.unk_00), nameof(struc.Flags)); // Mods - Assert.That(struc.Bridges.Count, Is.EqualTo(5), nameof(struc.Bridges)); - Assert.That(struc.CompatibleTracksAndRoads.Count, Is.EqualTo(7), nameof(struc.CompatibleTracksAndRoads)); - Assert.That(struc.TrackMods.Count, Is.EqualTo(2), nameof(struc.TrackMods)); - Assert.That(struc.Signals.Count, Is.EqualTo(10), nameof(struc.Signals)); - Assert.That(struc.Stations.Count, Is.EqualTo(5), nameof(struc.Stations)); + Assert.That(struc.Bridges, Has.Count.EqualTo(5), nameof(struc.Bridges)); + Assert.That(struc.CompatibleTracksAndRoads, Has.Count.EqualTo(7), nameof(struc.CompatibleTracksAndRoads)); + Assert.That(struc.TrackMods, Has.Count.EqualTo(2), nameof(struc.TrackMods)); + Assert.That(struc.Signals, Has.Count.EqualTo(10), nameof(struc.Signals)); + Assert.That(struc.Stations, Has.Count.EqualTo(5), nameof(struc.Stations)); Assert.That(struc.SellCostFactor, Is.EqualTo(-10), nameof(struc.SellCostFactor)); // Signals // Stations @@ -997,7 +994,7 @@ void assertFunc(LocoObject obj, TrackSignalObject struc) => Assert.Multiple(() = Assert.That(struc.DesignedYear, Is.Zero, nameof(struc.DesignedYear)); Assert.That(struc.Flags, Is.EqualTo(TrackSignalObjectFlags.IsLeft), nameof(struc.Flags)); // Mods - Assert.That(struc.CompatibleTrackObjects.Count, Is.Zero, nameof(struc.CompatibleTrackObjects)); + Assert.That(struc.CompatibleTrackObjects, Is.Empty, nameof(struc.CompatibleTrackObjects)); Assert.That(struc.NumFrames, Is.EqualTo(7), nameof(struc.NumFrames)); Assert.That(struc.ObsoleteYear, Is.EqualTo(1955), nameof(struc.ObsoleteYear)); Assert.That(struc.SellCostFactor, Is.EqualTo(-3), nameof(struc.SellCostFactor)); @@ -1022,7 +1019,7 @@ void assertFunc(LocoObject obj, TrackStationObject struc) => Assert.Multiple(() Assert.That(struc.Flags, Is.EqualTo(TrackStationObjectFlags.None), nameof(struc.Flags)); // ManualPower Assert.That(struc.Height, Is.Zero, nameof(struc.Height)); - Assert.That(struc.CompatibleTrackObjects.Count, Is.Zero, nameof(struc.CompatibleTrackObjects)); + Assert.That(struc.CompatibleTrackObjects, Is.Empty, nameof(struc.CompatibleTrackObjects)); Assert.That(struc.ObsoleteYear, Is.EqualTo(65535), nameof(struc.ObsoleteYear)); Assert.That(struc.SellCostFactor, Is.EqualTo(-7), nameof(struc.SellCostFactor)); Assert.That(struc.TrackPieces, Is.EqualTo(TrackTraitFlags.None), nameof(struc.TrackPieces)); @@ -1067,10 +1064,7 @@ void assertFunc(LocoObject obj, TreeObject struc) => Assert.Multiple(() => [TestCase("TUNNEL1.DAT")] public void TunnelObject(string objectName) { - void assertFunc(LocoObject obj, TunnelObject struc) => Assert.Multiple(() => - { - Assert.That(obj.ImageTable.GraphicsElements, Has.Count.EqualTo(4)); - }); + void assertFunc(LocoObject obj, TunnelObject struc) => Assert.Multiple(() => Assert.That(obj.ImageTable.GraphicsElements, Has.Count.EqualTo(4))); LoadSaveGenericTest(objectName, assertFunc); } From 6b129c773256280d5d9aeb6e170e04d38c57aab0 Mon Sep 17 00:00:00 2001 From: Benjamin Sutas Date: Wed, 3 Sep 2025 10:40:23 +1000 Subject: [PATCH 06/21] work on buildingcomponents --- .../Objects/Airport/AirportObject.cs | 2 +- .../Objects/Building/BuildingObject.cs | 2 +- ...mponents.cs => BuildingComponentsModel.cs} | 4 +-- .../ObjectModels/Objects/Dock/DockObject.cs | 2 +- .../Objects/Industry/IndustryObject.cs | 2 +- .../Graphics/ImageTableViewModel.cs | 2 +- .../BaseLocoFileViewModel.cs | 0 .../LocoTypes/BuildingComponentsViewModel.cs | 19 +++++++++++++ .../{DatTypes => LocoTypes}/G1ViewModel.cs | 0 .../ILocoFileViewModel.cs | 0 .../IObjectViewModel.cs | 0 .../LocoObjectViewModel.cs | 0 .../{DatTypes => LocoTypes}/MusicViewModel.cs | 0 .../ObjectEditorViewModel.cs | 0 .../ObjectHeaderViewModel.cs | 0 .../ObjectModelHeaderViewModel.cs | 0 .../Objects/AirportViewModel.cs | 24 ++++++++--------- .../Objects/BridgeViewModel.cs | 0 .../Objects/BuildingViewModel.cs | 27 ++++++++++--------- .../Objects/CargoViewModel.cs | 0 .../Objects/CliffEdgeViewModel.cs | 0 .../Objects/ClimateViewModel.cs | 0 .../Objects/CompetitorViewModel.cs | 0 .../Objects/CurrencyViewModel.cs | 0 .../Objects/DockViewModel.cs | 24 ++++++++--------- .../Objects/HillShapesViewModel.cs | 0 .../Objects/IndustryViewModel.cs | 24 ++++++++--------- .../Objects/InterfaceSkinViewModel.cs | 0 .../Objects/LandViewModel.cs | 0 .../Objects/LevelCrossingViewModel.cs | 0 .../Objects/RegionViewModel.cs | 0 .../Objects/RoadExtraViewModel.cs | 0 .../Objects/RoadStationViewModel.cs | 0 .../Objects/RoadViewModel.cs | 0 .../Objects/ScaffoldingViewModel.cs | 0 .../Objects/ScenarioTextViewModel.cs | 0 .../Objects/SnowViewModel.cs | 0 .../Objects/SoundViewModel.cs | 0 .../Objects/SteamViewModel.cs | 0 .../Objects/StreetLightViewModel.cs | 0 .../Objects/TownNamesViewModel.cs | 0 .../Objects/TrackExtraViewModel.cs | 0 .../Objects/TrackSignalViewModel.cs | 0 .../Objects/TrackStationViewModel.cs | 0 .../Objects/TrackViewModel.cs | 0 .../Objects/TreeViewModel.cs | 0 .../Objects/TunnelViewModel.cs | 0 .../Objects/VehicleViewModel.cs | 0 .../Objects/WallViewModel.cs | 0 .../Objects/WaterViewModel.cs | 0 .../{DatTypes => LocoTypes}/SCV5ViewModel.cs | 0 .../SoundEffectsViewModel.cs | 0 .../TutorialViewModel.cs | 0 Gui/Views/BuildingComponentsView.axaml | 18 +++++++++++++ Gui/Views/BuildingComponentsView.axaml.cs | 13 +++++++++ 55 files changed, 108 insertions(+), 55 deletions(-) rename Definitions/ObjectModels/Objects/Common/{BuildingComponents.cs => BuildingComponentsModel.cs} (86%) rename Gui/ViewModels/{DatTypes => LocoTypes}/BaseLocoFileViewModel.cs (100%) create mode 100644 Gui/ViewModels/LocoTypes/BuildingComponentsViewModel.cs rename Gui/ViewModels/{DatTypes => LocoTypes}/G1ViewModel.cs (100%) rename Gui/ViewModels/{DatTypes => LocoTypes}/ILocoFileViewModel.cs (100%) rename Gui/ViewModels/{DatTypes => LocoTypes}/IObjectViewModel.cs (100%) rename Gui/ViewModels/{DatTypes => LocoTypes}/LocoObjectViewModel.cs (100%) rename Gui/ViewModels/{DatTypes => LocoTypes}/MusicViewModel.cs (100%) rename Gui/ViewModels/{DatTypes => LocoTypes}/ObjectEditorViewModel.cs (100%) rename Gui/ViewModels/{DatTypes => LocoTypes}/ObjectHeaderViewModel.cs (100%) rename Gui/ViewModels/{DatTypes => LocoTypes}/ObjectModelHeaderViewModel.cs (100%) rename Gui/ViewModels/{DatTypes => LocoTypes}/Objects/AirportViewModel.cs (71%) rename Gui/ViewModels/{DatTypes => LocoTypes}/Objects/BridgeViewModel.cs (100%) rename Gui/ViewModels/{DatTypes => LocoTypes}/Objects/BuildingViewModel.cs (82%) rename Gui/ViewModels/{DatTypes => LocoTypes}/Objects/CargoViewModel.cs (100%) rename Gui/ViewModels/{DatTypes => LocoTypes}/Objects/CliffEdgeViewModel.cs (100%) rename Gui/ViewModels/{DatTypes => LocoTypes}/Objects/ClimateViewModel.cs (100%) rename Gui/ViewModels/{DatTypes => LocoTypes}/Objects/CompetitorViewModel.cs (100%) rename Gui/ViewModels/{DatTypes => LocoTypes}/Objects/CurrencyViewModel.cs (100%) rename Gui/ViewModels/{DatTypes => LocoTypes}/Objects/DockViewModel.cs (61%) rename Gui/ViewModels/{DatTypes => LocoTypes}/Objects/HillShapesViewModel.cs (100%) rename Gui/ViewModels/{DatTypes => LocoTypes}/Objects/IndustryViewModel.cs (85%) rename Gui/ViewModels/{DatTypes => LocoTypes}/Objects/InterfaceSkinViewModel.cs (100%) rename Gui/ViewModels/{DatTypes => LocoTypes}/Objects/LandViewModel.cs (100%) rename Gui/ViewModels/{DatTypes => LocoTypes}/Objects/LevelCrossingViewModel.cs (100%) rename Gui/ViewModels/{DatTypes => LocoTypes}/Objects/RegionViewModel.cs (100%) rename Gui/ViewModels/{DatTypes => LocoTypes}/Objects/RoadExtraViewModel.cs (100%) rename Gui/ViewModels/{DatTypes => LocoTypes}/Objects/RoadStationViewModel.cs (100%) rename Gui/ViewModels/{DatTypes => LocoTypes}/Objects/RoadViewModel.cs (100%) rename Gui/ViewModels/{DatTypes => LocoTypes}/Objects/ScaffoldingViewModel.cs (100%) rename Gui/ViewModels/{DatTypes => LocoTypes}/Objects/ScenarioTextViewModel.cs (100%) rename Gui/ViewModels/{DatTypes => LocoTypes}/Objects/SnowViewModel.cs (100%) rename Gui/ViewModels/{DatTypes => LocoTypes}/Objects/SoundViewModel.cs (100%) rename Gui/ViewModels/{DatTypes => LocoTypes}/Objects/SteamViewModel.cs (100%) rename Gui/ViewModels/{DatTypes => LocoTypes}/Objects/StreetLightViewModel.cs (100%) rename Gui/ViewModels/{DatTypes => LocoTypes}/Objects/TownNamesViewModel.cs (100%) rename Gui/ViewModels/{DatTypes => LocoTypes}/Objects/TrackExtraViewModel.cs (100%) rename Gui/ViewModels/{DatTypes => LocoTypes}/Objects/TrackSignalViewModel.cs (100%) rename Gui/ViewModels/{DatTypes => LocoTypes}/Objects/TrackStationViewModel.cs (100%) rename Gui/ViewModels/{DatTypes => LocoTypes}/Objects/TrackViewModel.cs (100%) rename Gui/ViewModels/{DatTypes => LocoTypes}/Objects/TreeViewModel.cs (100%) rename Gui/ViewModels/{DatTypes => LocoTypes}/Objects/TunnelViewModel.cs (100%) rename Gui/ViewModels/{DatTypes => LocoTypes}/Objects/VehicleViewModel.cs (100%) rename Gui/ViewModels/{DatTypes => LocoTypes}/Objects/WallViewModel.cs (100%) rename Gui/ViewModels/{DatTypes => LocoTypes}/Objects/WaterViewModel.cs (100%) rename Gui/ViewModels/{DatTypes => LocoTypes}/SCV5ViewModel.cs (100%) rename Gui/ViewModels/{DatTypes => LocoTypes}/SoundEffectsViewModel.cs (100%) rename Gui/ViewModels/{DatTypes => LocoTypes}/TutorialViewModel.cs (100%) create mode 100644 Gui/Views/BuildingComponentsView.axaml create mode 100644 Gui/Views/BuildingComponentsView.axaml.cs diff --git a/Definitions/ObjectModels/Objects/Airport/AirportObject.cs b/Definitions/ObjectModels/Objects/Airport/AirportObject.cs index dc0694cd..c97df4f9 100644 --- a/Definitions/ObjectModels/Objects/Airport/AirportObject.cs +++ b/Definitions/ObjectModels/Objects/Airport/AirportObject.cs @@ -17,7 +17,7 @@ public class AirportObject : ILocoStruct, IHasBuildingComponents public uint16_t DesignedYear { get; set; } public uint16_t ObsoleteYear { get; set; } - public BuildingComponents BuildingComponents { get; set; } = new(); + public BuildingComponentsModel BuildingComponents { get; set; } = new(); public List BuildingPositions { get; set; } = []; public List MovementNodes { get; set; } = []; diff --git a/Definitions/ObjectModels/Objects/Building/BuildingObject.cs b/Definitions/ObjectModels/Objects/Building/BuildingObject.cs index 11249f73..4f99a8d0 100644 --- a/Definitions/ObjectModels/Objects/Building/BuildingObject.cs +++ b/Definitions/ObjectModels/Objects/Building/BuildingObject.cs @@ -24,7 +24,7 @@ public class BuildingObject : ILocoStruct, IHasBuildingComponents public uint8_t var_AC { get; set; } - public BuildingComponents BuildingComponents { get; set; } = new(); + public BuildingComponentsModel BuildingComponents { get; set; } = new(); public List ProducedQuantity { get; set; } = []; public List ProducedCargo { get; set; } = []; diff --git a/Definitions/ObjectModels/Objects/Common/BuildingComponents.cs b/Definitions/ObjectModels/Objects/Common/BuildingComponentsModel.cs similarity index 86% rename from Definitions/ObjectModels/Objects/Common/BuildingComponents.cs rename to Definitions/ObjectModels/Objects/Common/BuildingComponentsModel.cs index a7c3cfd4..739f1027 100644 --- a/Definitions/ObjectModels/Objects/Common/BuildingComponents.cs +++ b/Definitions/ObjectModels/Objects/Common/BuildingComponentsModel.cs @@ -4,11 +4,11 @@ namespace Definitions.ObjectModels.Objects.Common; public interface IHasBuildingComponents { - BuildingComponents BuildingComponents { get; set; } + BuildingComponentsModel BuildingComponents { get; set; } } [TypeConverter(typeof(ExpandableObjectConverter))] -public class BuildingComponents +public class BuildingComponentsModel { public List BuildingHeights { get; set; } = []; public List BuildingAnimations { get; set; } = []; diff --git a/Definitions/ObjectModels/Objects/Dock/DockObject.cs b/Definitions/ObjectModels/Objects/Dock/DockObject.cs index e919569a..2fd5d9fc 100644 --- a/Definitions/ObjectModels/Objects/Dock/DockObject.cs +++ b/Definitions/ObjectModels/Objects/Dock/DockObject.cs @@ -14,7 +14,7 @@ public class DockObject : ILocoStruct, IHasBuildingComponents public uint16_t ObsoleteYear { get; set; } public Pos2 BoatPosition { get; set; } - public BuildingComponents BuildingComponents { get; set; } = new(); + public BuildingComponentsModel BuildingComponents { get; set; } = new(); public bool Validate() { diff --git a/Definitions/ObjectModels/Objects/Industry/IndustryObject.cs b/Definitions/ObjectModels/Objects/Industry/IndustryObject.cs index 322035d8..aa9da922 100644 --- a/Definitions/ObjectModels/Objects/Industry/IndustryObject.cs +++ b/Definitions/ObjectModels/Objects/Industry/IndustryObject.cs @@ -34,7 +34,7 @@ public class IndustryObject : ILocoStruct, IHasBuildingComponents public ObjectModelHeader? BuildingWall { get; set; } // Wall types that can be built around this industry public ObjectModelHeader? BuildingWallEntrance { get; set; } // Wall types that can be built around this industry - public BuildingComponents BuildingComponents { get; set; } = new(); + public BuildingComponentsModel BuildingComponents { get; set; } = new(); public List> AnimationSequences { get; set; } = []; // Access with getAnimationSequence helper method public List UnkBuildingData { get; set; } = []; diff --git a/Gui/ViewModels/Graphics/ImageTableViewModel.cs b/Gui/ViewModels/Graphics/ImageTableViewModel.cs index 86e23019..109d9b83 100644 --- a/Gui/ViewModels/Graphics/ImageTableViewModel.cs +++ b/Gui/ViewModels/Graphics/ImageTableViewModel.cs @@ -78,7 +78,7 @@ public class ImageTableViewModel : ReactiveObject, IExtraContentViewModel public int OffsetSpacing { get; set; } int prevOffset; - public ImageTableViewModel(ImageTable imageTable, PaletteMap paletteMap, ILogger logger, BuildingComponents buildingComponents = null) + public ImageTableViewModel(ImageTable imageTable, PaletteMap paletteMap, ILogger logger, BuildingComponentsModel buildingComponents = null) { ArgumentNullException.ThrowIfNull(paletteMap); diff --git a/Gui/ViewModels/DatTypes/BaseLocoFileViewModel.cs b/Gui/ViewModels/LocoTypes/BaseLocoFileViewModel.cs similarity index 100% rename from Gui/ViewModels/DatTypes/BaseLocoFileViewModel.cs rename to Gui/ViewModels/LocoTypes/BaseLocoFileViewModel.cs diff --git a/Gui/ViewModels/LocoTypes/BuildingComponentsViewModel.cs b/Gui/ViewModels/LocoTypes/BuildingComponentsViewModel.cs new file mode 100644 index 00000000..5bb114a3 --- /dev/null +++ b/Gui/ViewModels/LocoTypes/BuildingComponentsViewModel.cs @@ -0,0 +1,19 @@ +using Definitions.ObjectModels.Objects.Common; +using Definitions.ObjectModels.Types; +using ReactiveUI; +using System.Collections.Generic; + +namespace Gui.ViewModels.LocoTypes; + +public class BuildingComponentsViewModel : ReactiveObject +{ + public BuildingComponentsModel BuildingComponents { get; } + + List GraphicsElements { get; } + + public BuildingComponentsViewModel(BuildingComponentsModel buildingComponents, List graphicsElements) + { + BuildingComponents = buildingComponents; + GraphicsElements = graphicsElements; + } +} diff --git a/Gui/ViewModels/DatTypes/G1ViewModel.cs b/Gui/ViewModels/LocoTypes/G1ViewModel.cs similarity index 100% rename from Gui/ViewModels/DatTypes/G1ViewModel.cs rename to Gui/ViewModels/LocoTypes/G1ViewModel.cs diff --git a/Gui/ViewModels/DatTypes/ILocoFileViewModel.cs b/Gui/ViewModels/LocoTypes/ILocoFileViewModel.cs similarity index 100% rename from Gui/ViewModels/DatTypes/ILocoFileViewModel.cs rename to Gui/ViewModels/LocoTypes/ILocoFileViewModel.cs diff --git a/Gui/ViewModels/DatTypes/IObjectViewModel.cs b/Gui/ViewModels/LocoTypes/IObjectViewModel.cs similarity index 100% rename from Gui/ViewModels/DatTypes/IObjectViewModel.cs rename to Gui/ViewModels/LocoTypes/IObjectViewModel.cs diff --git a/Gui/ViewModels/DatTypes/LocoObjectViewModel.cs b/Gui/ViewModels/LocoTypes/LocoObjectViewModel.cs similarity index 100% rename from Gui/ViewModels/DatTypes/LocoObjectViewModel.cs rename to Gui/ViewModels/LocoTypes/LocoObjectViewModel.cs diff --git a/Gui/ViewModels/DatTypes/MusicViewModel.cs b/Gui/ViewModels/LocoTypes/MusicViewModel.cs similarity index 100% rename from Gui/ViewModels/DatTypes/MusicViewModel.cs rename to Gui/ViewModels/LocoTypes/MusicViewModel.cs diff --git a/Gui/ViewModels/DatTypes/ObjectEditorViewModel.cs b/Gui/ViewModels/LocoTypes/ObjectEditorViewModel.cs similarity index 100% rename from Gui/ViewModels/DatTypes/ObjectEditorViewModel.cs rename to Gui/ViewModels/LocoTypes/ObjectEditorViewModel.cs diff --git a/Gui/ViewModels/DatTypes/ObjectHeaderViewModel.cs b/Gui/ViewModels/LocoTypes/ObjectHeaderViewModel.cs similarity index 100% rename from Gui/ViewModels/DatTypes/ObjectHeaderViewModel.cs rename to Gui/ViewModels/LocoTypes/ObjectHeaderViewModel.cs diff --git a/Gui/ViewModels/DatTypes/ObjectModelHeaderViewModel.cs b/Gui/ViewModels/LocoTypes/ObjectModelHeaderViewModel.cs similarity index 100% rename from Gui/ViewModels/DatTypes/ObjectModelHeaderViewModel.cs rename to Gui/ViewModels/LocoTypes/ObjectModelHeaderViewModel.cs diff --git a/Gui/ViewModels/DatTypes/Objects/AirportViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/AirportViewModel.cs similarity index 71% rename from Gui/ViewModels/DatTypes/Objects/AirportViewModel.cs rename to Gui/ViewModels/LocoTypes/Objects/AirportViewModel.cs index db82c914..5d0b59e1 100644 --- a/Gui/ViewModels/DatTypes/Objects/AirportViewModel.cs +++ b/Gui/ViewModels/LocoTypes/Objects/AirportViewModel.cs @@ -22,9 +22,9 @@ public class AirportViewModel : LocoObjectViewModel [Category("Cost")] public uint8_t CostIndex { get; set; } [Category("Cost")] public int16_t BuildCostFactor { get; set; } [Category("Cost")] public int16_t SellCostFactor { get; set; } - [Category("Building"), Length(1, AirportObjectLoader.Constants.BuildingVariationCount)] public List> BuildingVariations { get; set; } // NumBuildingVariations - [Category("Building"), Length(1, AirportObjectLoader.Constants.BuildingHeightCount)] public List BuildingHeights { get; set; } // NumBuildingParts - [Category("Building"), Length(1, AirportObjectLoader.Constants.BuildingAnimationCount)] public List BuildingAnimations { get; set; } // NumBuildingParts + //[Category("Building"), Length(1, AirportObjectLoader.Constants.BuildingVariationCount)] public List> BuildingVariations { get; set; } // NumBuildingVariations + //[Category("Building"), Length(1, AirportObjectLoader.Constants.BuildingHeightCount)] public List BuildingHeights { get; set; } // NumBuildingParts + //[Category("Building"), Length(1, AirportObjectLoader.Constants.BuildingAnimationCount)] public List BuildingAnimations { get; set; } // NumBuildingParts [Category("Building")] public List BuildingPositions { get; set; } [Category("Movement")] public List MovementNodes { get; set; } // NumMovementNodes [Category("Movement")] public List MovementEdges { get; set; } // NumMovementEdges @@ -38,9 +38,9 @@ public AirportViewModel(AirportObject ao) SellCostFactor = ao.SellCostFactor; var_07 = ao.var_07; AllowedPlaneTypes = ao.AllowedPlaneTypes; - BuildingAnimations = [.. ao.BuildingComponents.BuildingAnimations]; - BuildingHeights = [.. ao.BuildingComponents.BuildingHeights]; - BuildingVariations = [.. ao.BuildingComponents.BuildingVariations.Select(x => new List(x))]; + //BuildingAnimations = [.. ao.BuildingComponents.BuildingAnimations]; + //BuildingHeights = [.. ao.BuildingComponents.BuildingHeights]; + //BuildingVariations = [.. ao.BuildingComponents.BuildingVariations.Select(x => new List(x))]; BuildingPositions = [.. ao.BuildingPositions]; LargeTiles = ao.LargeTiles; MinX = ao.MinX; @@ -72,12 +72,12 @@ public override AirportObject GetAsModel() DesignedYear = DesignedYear, ObsoleteYear = ObsoleteYear, var_B6 = [.. var_B6], - BuildingComponents = new() - { - BuildingHeights = [.. BuildingHeights], - BuildingAnimations = [.. BuildingAnimations], - BuildingVariations = BuildingVariations.ToList().ConvertAll(x => x.ToList()), - }, + //BuildingComponents = new() + //{ + // BuildingHeights = [.. BuildingHeights], + // BuildingAnimations = [.. BuildingAnimations], + // BuildingVariations = BuildingVariations.ToList().ConvertAll(x => x.ToList()), + //}, BuildingPositions = [.. BuildingPositions], MovementNodes = [.. MovementNodes], MovementEdges = [.. MovementEdges], diff --git a/Gui/ViewModels/DatTypes/Objects/BridgeViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/BridgeViewModel.cs similarity index 100% rename from Gui/ViewModels/DatTypes/Objects/BridgeViewModel.cs rename to Gui/ViewModels/LocoTypes/Objects/BridgeViewModel.cs diff --git a/Gui/ViewModels/DatTypes/Objects/BuildingViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/BuildingViewModel.cs similarity index 82% rename from Gui/ViewModels/DatTypes/Objects/BuildingViewModel.cs rename to Gui/ViewModels/LocoTypes/Objects/BuildingViewModel.cs index 42b62674..6058c852 100644 --- a/Gui/ViewModels/DatTypes/Objects/BuildingViewModel.cs +++ b/Gui/ViewModels/LocoTypes/Objects/BuildingViewModel.cs @@ -2,6 +2,7 @@ using Definitions.ObjectModels.Objects.Building; using Definitions.ObjectModels.Objects.Common; using Definitions.ObjectModels.Types; +using Gui.ViewModels.LocoTypes; using PropertyModels.ComponentModel.DataAnnotations; using PropertyModels.Extensions; using ReactiveUI.Fody.Helpers; @@ -28,9 +29,11 @@ public class BuildingViewModel : LocoObjectViewModel [Category("Production"), Length(0, BuildingObjectLoader.Constants.MaxProducedCargoType)] public List ProducedCargo { get; set; } [Category("Production"), Length(0, BuildingObjectLoader.Constants.MaxProducedCargoType)] public List RequiredCargo { get; set; } [Category("Production"), Length(1, BuildingObjectLoader.Constants.MaxProducedCargoType)] public List ProducedQuantity { get; set; } - [Category("Building"), Length(1, BuildingObjectLoader.Constants.BuildingVariationCount)] public List> BuildingVariations { get; set; } // NumBuildingVariations - [Category("Building"), Length(1, BuildingObjectLoader.Constants.BuildingHeightCount)] public List BuildingHeights { get; set; } // NumBuildingParts - [Category("Building"), Length(1, BuildingObjectLoader.Constants.BuildingAnimationCount)] public List BuildingAnimations { get; set; } // NumBuildingParts + + //[Category("Building"), Length(1, BuildingObjectLoader.Constants.BuildingVariationCount)] public List> BuildingVariations { get; set; } // NumBuildingVariations + //[Category("Building"), Length(1, BuildingObjectLoader.Constants.BuildingHeightCount)] public List BuildingHeights { get; set; } // NumBuildingParts + //[Category("Building"), Length(1, BuildingObjectLoader.Constants.BuildingAnimationCount)] public List BuildingAnimations { get; set; } // NumBuildingParts + //[Browsable(false)] public BuildingComponentsViewModel BuildingComponents { get; set; } // note: these height sequences are massive. BLDCTY28 has 2 sequences, 512 in length and 1024 in length. Avalonia PropertyGrid takes 30+ seconds to render this. todo: don't use property grid in future //[Reactive, Category("Building"), Length(1, BuildingObject.MaxElevatorHeightSequences), Browsable(false)] public BindingList> ElevatorHeightSequences { get; set; } // NumElevatorSequences @@ -62,9 +65,9 @@ public BuildingViewModel(BuildingObject bo) ProducedCargo = [.. bo.ProducedCargo.ConvertAll(x => new ObjectModelHeaderViewModel(x))]; RequiredCargo = [.. bo.RequiredCargo.ConvertAll(x => new ObjectModelHeaderViewModel(x))]; ProducedQuantity = [.. bo.ProducedQuantity]; - BuildingAnimations = [.. bo.BuildingComponents.BuildingAnimations]; - BuildingHeights = [.. bo.BuildingComponents.BuildingHeights]; - BuildingVariations = [.. bo.BuildingComponents.BuildingVariations.Select(x => new List(x))]; + //BuildingAnimations = [.. bo.BuildingComponents.BuildingAnimations]; + //BuildingHeights = [.. bo.BuildingComponents.BuildingHeights]; + //BuildingVariations = [.. bo.BuildingComponents.BuildingVariations.Select(x => new List(x))]; ElevatorSequence1 = bo.ElevatorHeightSequences.Count > 0 ? bo.ElevatorHeightSequences[0] : null; ElevatorSequence2 = bo.ElevatorHeightSequences.Count > 1 ? bo.ElevatorHeightSequences[1] : null; ElevatorSequence3 = bo.ElevatorHeightSequences.Count > 2 ? bo.ElevatorHeightSequences[2] : null; @@ -82,12 +85,12 @@ public BuildingViewModel(BuildingObject bo) public override BuildingObject GetAsModel() => new() { - BuildingComponents = new BuildingComponents() - { - BuildingHeights = [.. BuildingHeights], - BuildingAnimations = [.. BuildingAnimations], - BuildingVariations = BuildingVariations.ToList().ConvertAll(x => x.ToList()), - }, + //BuildingComponents = new BuildingComponentsModel() + //{ + // BuildingHeights = [.. BuildingHeights], + // BuildingAnimations = [.. BuildingAnimations], + // BuildingVariations = BuildingVariations.ToList().ConvertAll(x => x.ToList()), + //}, Flags = Flags, Colours = Colours, ScaffoldingColour = ScaffoldingColour, diff --git a/Gui/ViewModels/DatTypes/Objects/CargoViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/CargoViewModel.cs similarity index 100% rename from Gui/ViewModels/DatTypes/Objects/CargoViewModel.cs rename to Gui/ViewModels/LocoTypes/Objects/CargoViewModel.cs diff --git a/Gui/ViewModels/DatTypes/Objects/CliffEdgeViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/CliffEdgeViewModel.cs similarity index 100% rename from Gui/ViewModels/DatTypes/Objects/CliffEdgeViewModel.cs rename to Gui/ViewModels/LocoTypes/Objects/CliffEdgeViewModel.cs diff --git a/Gui/ViewModels/DatTypes/Objects/ClimateViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/ClimateViewModel.cs similarity index 100% rename from Gui/ViewModels/DatTypes/Objects/ClimateViewModel.cs rename to Gui/ViewModels/LocoTypes/Objects/ClimateViewModel.cs diff --git a/Gui/ViewModels/DatTypes/Objects/CompetitorViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/CompetitorViewModel.cs similarity index 100% rename from Gui/ViewModels/DatTypes/Objects/CompetitorViewModel.cs rename to Gui/ViewModels/LocoTypes/Objects/CompetitorViewModel.cs diff --git a/Gui/ViewModels/DatTypes/Objects/CurrencyViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/CurrencyViewModel.cs similarity index 100% rename from Gui/ViewModels/DatTypes/Objects/CurrencyViewModel.cs rename to Gui/ViewModels/LocoTypes/Objects/CurrencyViewModel.cs diff --git a/Gui/ViewModels/DatTypes/Objects/DockViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/DockViewModel.cs similarity index 61% rename from Gui/ViewModels/DatTypes/Objects/DockViewModel.cs rename to Gui/ViewModels/LocoTypes/Objects/DockViewModel.cs index e9a3155b..4936bb4c 100644 --- a/Gui/ViewModels/DatTypes/Objects/DockViewModel.cs +++ b/Gui/ViewModels/LocoTypes/Objects/DockViewModel.cs @@ -20,9 +20,9 @@ public class DockViewModel : LocoObjectViewModel [Category("Cost")] public uint8_t CostIndex { get; set; } [Category("Cost")] public int16_t BuildCostFactor { get; set; } [Category("Cost")] public int16_t SellCostFactor { get; set; } - [Category("Building"), Length(1, DockObjectLoader.Constants.BuildingVariationCount)] public List> BuildingVariations { get; set; } // NumBuildingVariations - [Category("Building"), Length(1, DockObjectLoader.Constants.BuildingHeightCount)] public List BuildingHeights { get; set; } // NumBuildingParts - [Category("Building"), Length(1, DockObjectLoader.Constants.BuildingAnimationCount)] public List BuildingAnimations { get; set; } // NumBuildingParts + //[Category("Building"), Length(1, DockObjectLoader.Constants.BuildingVariationCount)] public List> BuildingVariations { get; set; } // NumBuildingVariations + //[Category("Building"), Length(1, DockObjectLoader.Constants.BuildingHeightCount)] public List BuildingHeights { get; set; } // NumBuildingParts + //[Category("Building"), Length(1, DockObjectLoader.Constants.BuildingAnimationCount)] public List BuildingAnimations { get; set; } // NumBuildingParts [Category("")] public uint8_t var_07 { get; set; } public DockViewModel(DockObject @do) @@ -35,9 +35,9 @@ public DockViewModel(DockObject @do) SellCostFactor = @do.SellCostFactor; BoatPosition = @do.BoatPosition; var_07 = @do.var_07; - BuildingAnimations = [.. @do.BuildingComponents.BuildingAnimations]; - BuildingHeights = [.. @do.BuildingComponents.BuildingHeights]; - BuildingVariations = [.. @do.BuildingComponents.BuildingVariations.Select(x => new List(x))]; + //BuildingAnimations = [.. @do.BuildingComponents.BuildingAnimations]; + //BuildingHeights = [.. @do.BuildingComponents.BuildingHeights]; + //BuildingVariations = [.. @do.BuildingComponents.BuildingVariations.Select(x => new List(x))]; } public override DockObject GetAsModel() @@ -52,12 +52,12 @@ public override DockObject GetAsModel() SellCostFactor = SellCostFactor, BoatPosition = BoatPosition, var_07 = var_07, - BuildingComponents = new BuildingComponents() - { - BuildingHeights = [.. BuildingHeights], - BuildingAnimations = [.. BuildingAnimations], - BuildingVariations = BuildingVariations.ToList().ConvertAll(x => x.ToList()), - }, + //BuildingComponents = new BuildingComponentsModel() + //{ + // BuildingHeights = [.. BuildingHeights], + // BuildingAnimations = [.. BuildingAnimations], + // BuildingVariations = BuildingVariations.ToList().ConvertAll(x => x.ToList()), + //}, }; return dockObject; } diff --git a/Gui/ViewModels/DatTypes/Objects/HillShapesViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/HillShapesViewModel.cs similarity index 100% rename from Gui/ViewModels/DatTypes/Objects/HillShapesViewModel.cs rename to Gui/ViewModels/LocoTypes/Objects/HillShapesViewModel.cs diff --git a/Gui/ViewModels/DatTypes/Objects/IndustryViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/IndustryViewModel.cs similarity index 85% rename from Gui/ViewModels/DatTypes/Objects/IndustryViewModel.cs rename to Gui/ViewModels/LocoTypes/Objects/IndustryViewModel.cs index 60c28721..1359f198 100644 --- a/Gui/ViewModels/DatTypes/Objects/IndustryViewModel.cs +++ b/Gui/ViewModels/LocoTypes/Objects/IndustryViewModel.cs @@ -27,9 +27,9 @@ public class IndustryViewModel : LocoObjectViewModel [Category("Cost")] public int16_t BuildCostFactor { get; set; } [Category("Cost")] public int16_t SellCostFactor { get; set; } [Category("Building"), Length(IndustryObjectLoader.Constants.AnimationSequencesCount, IndustryObjectLoader.Constants.AnimationSequencesCount)] public List> AnimationSequences { get; set; } - [Category("Building"), Length(1, IndustryObjectLoader.Constants.BuildingVariationCount)] public List> BuildingVariations { get; set; } // NumBuildingVariations - [Category("Building"), Length(1, IndustryObjectLoader.Constants.BuildingHeightCount)] public List BuildingHeights { get; set; } // NumBuildingParts - [Category("Building"), Length(1, IndustryObjectLoader.Constants.BuildingAnimationCount)] public List BuildingAnimations { get; set; } // NumBuildingParts + //[Category("Building"), Length(1, IndustryObjectLoader.Constants.BuildingVariationCount)] public List> BuildingVariations { get; set; } // NumBuildingVariations + //[Category("Building"), Length(1, IndustryObjectLoader.Constants.BuildingHeightCount)] public List BuildingHeights { get; set; } // NumBuildingParts + //[Category("Building"), Length(1, IndustryObjectLoader.Constants.BuildingAnimationCount)] public List BuildingAnimations { get; set; } // NumBuildingParts [Category("Building")] public uint8_t MinNumBuildings { get; set; } [Category("Building")] public uint8_t MaxNumBuildings { get; set; } [Category("Building")] public BindingList UnkBuildingData { get; set; } @@ -49,9 +49,9 @@ public class IndustryViewModel : LocoObjectViewModel public IndustryViewModel(IndustryObject io) { AnimationSequences = [.. io.AnimationSequences.Select(x => new List(x))]; - BuildingAnimations = [.. io.BuildingComponents.BuildingAnimations]; - BuildingHeights = [.. io.BuildingComponents.BuildingHeights]; - BuildingVariations = [.. io.BuildingComponents.BuildingVariations.Select(x => new List(x))]; + //BuildingAnimations = [.. io.BuildingComponents.BuildingAnimations]; + //BuildingHeights = [.. io.BuildingComponents.BuildingHeights]; + //BuildingVariations = [.. io.BuildingComponents.BuildingVariations.Select(x => new List(x))]; UnkBuildingData = new(io.UnkBuildingData); BuildingSizeFlags = io.BuildingSizeFlags; BuildingWall = io.BuildingWall == null ? null : new(io.BuildingWall); @@ -88,12 +88,12 @@ public override IndustryObject GetAsModel() => new() { AnimationSequences = AnimationSequences.ToList().ConvertAll(x => x.ToList()), - BuildingComponents = new BuildingComponents() - { - BuildingHeights = [.. BuildingHeights], - BuildingAnimations = [.. BuildingAnimations], - BuildingVariations = BuildingVariations.ToList().ConvertAll(x => x.ToList()), - }, + //BuildingComponents = new BuildingComponentsModel() + //{ + // BuildingHeights = [.. BuildingHeights], + // BuildingAnimations = [.. BuildingAnimations], + // BuildingVariations = BuildingVariations.ToList().ConvertAll(x => x.ToList()), + //}, UnkBuildingData = [.. UnkBuildingData], BuildingSizeFlags = BuildingSizeFlags, BuildingWall = BuildingWall?.GetAsModel(), diff --git a/Gui/ViewModels/DatTypes/Objects/InterfaceSkinViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/InterfaceSkinViewModel.cs similarity index 100% rename from Gui/ViewModels/DatTypes/Objects/InterfaceSkinViewModel.cs rename to Gui/ViewModels/LocoTypes/Objects/InterfaceSkinViewModel.cs diff --git a/Gui/ViewModels/DatTypes/Objects/LandViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/LandViewModel.cs similarity index 100% rename from Gui/ViewModels/DatTypes/Objects/LandViewModel.cs rename to Gui/ViewModels/LocoTypes/Objects/LandViewModel.cs diff --git a/Gui/ViewModels/DatTypes/Objects/LevelCrossingViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/LevelCrossingViewModel.cs similarity index 100% rename from Gui/ViewModels/DatTypes/Objects/LevelCrossingViewModel.cs rename to Gui/ViewModels/LocoTypes/Objects/LevelCrossingViewModel.cs diff --git a/Gui/ViewModels/DatTypes/Objects/RegionViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/RegionViewModel.cs similarity index 100% rename from Gui/ViewModels/DatTypes/Objects/RegionViewModel.cs rename to Gui/ViewModels/LocoTypes/Objects/RegionViewModel.cs diff --git a/Gui/ViewModels/DatTypes/Objects/RoadExtraViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/RoadExtraViewModel.cs similarity index 100% rename from Gui/ViewModels/DatTypes/Objects/RoadExtraViewModel.cs rename to Gui/ViewModels/LocoTypes/Objects/RoadExtraViewModel.cs diff --git a/Gui/ViewModels/DatTypes/Objects/RoadStationViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/RoadStationViewModel.cs similarity index 100% rename from Gui/ViewModels/DatTypes/Objects/RoadStationViewModel.cs rename to Gui/ViewModels/LocoTypes/Objects/RoadStationViewModel.cs diff --git a/Gui/ViewModels/DatTypes/Objects/RoadViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/RoadViewModel.cs similarity index 100% rename from Gui/ViewModels/DatTypes/Objects/RoadViewModel.cs rename to Gui/ViewModels/LocoTypes/Objects/RoadViewModel.cs diff --git a/Gui/ViewModels/DatTypes/Objects/ScaffoldingViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/ScaffoldingViewModel.cs similarity index 100% rename from Gui/ViewModels/DatTypes/Objects/ScaffoldingViewModel.cs rename to Gui/ViewModels/LocoTypes/Objects/ScaffoldingViewModel.cs diff --git a/Gui/ViewModels/DatTypes/Objects/ScenarioTextViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/ScenarioTextViewModel.cs similarity index 100% rename from Gui/ViewModels/DatTypes/Objects/ScenarioTextViewModel.cs rename to Gui/ViewModels/LocoTypes/Objects/ScenarioTextViewModel.cs diff --git a/Gui/ViewModels/DatTypes/Objects/SnowViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/SnowViewModel.cs similarity index 100% rename from Gui/ViewModels/DatTypes/Objects/SnowViewModel.cs rename to Gui/ViewModels/LocoTypes/Objects/SnowViewModel.cs diff --git a/Gui/ViewModels/DatTypes/Objects/SoundViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/SoundViewModel.cs similarity index 100% rename from Gui/ViewModels/DatTypes/Objects/SoundViewModel.cs rename to Gui/ViewModels/LocoTypes/Objects/SoundViewModel.cs diff --git a/Gui/ViewModels/DatTypes/Objects/SteamViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/SteamViewModel.cs similarity index 100% rename from Gui/ViewModels/DatTypes/Objects/SteamViewModel.cs rename to Gui/ViewModels/LocoTypes/Objects/SteamViewModel.cs diff --git a/Gui/ViewModels/DatTypes/Objects/StreetLightViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/StreetLightViewModel.cs similarity index 100% rename from Gui/ViewModels/DatTypes/Objects/StreetLightViewModel.cs rename to Gui/ViewModels/LocoTypes/Objects/StreetLightViewModel.cs diff --git a/Gui/ViewModels/DatTypes/Objects/TownNamesViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/TownNamesViewModel.cs similarity index 100% rename from Gui/ViewModels/DatTypes/Objects/TownNamesViewModel.cs rename to Gui/ViewModels/LocoTypes/Objects/TownNamesViewModel.cs diff --git a/Gui/ViewModels/DatTypes/Objects/TrackExtraViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/TrackExtraViewModel.cs similarity index 100% rename from Gui/ViewModels/DatTypes/Objects/TrackExtraViewModel.cs rename to Gui/ViewModels/LocoTypes/Objects/TrackExtraViewModel.cs diff --git a/Gui/ViewModels/DatTypes/Objects/TrackSignalViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/TrackSignalViewModel.cs similarity index 100% rename from Gui/ViewModels/DatTypes/Objects/TrackSignalViewModel.cs rename to Gui/ViewModels/LocoTypes/Objects/TrackSignalViewModel.cs diff --git a/Gui/ViewModels/DatTypes/Objects/TrackStationViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/TrackStationViewModel.cs similarity index 100% rename from Gui/ViewModels/DatTypes/Objects/TrackStationViewModel.cs rename to Gui/ViewModels/LocoTypes/Objects/TrackStationViewModel.cs diff --git a/Gui/ViewModels/DatTypes/Objects/TrackViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/TrackViewModel.cs similarity index 100% rename from Gui/ViewModels/DatTypes/Objects/TrackViewModel.cs rename to Gui/ViewModels/LocoTypes/Objects/TrackViewModel.cs diff --git a/Gui/ViewModels/DatTypes/Objects/TreeViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/TreeViewModel.cs similarity index 100% rename from Gui/ViewModels/DatTypes/Objects/TreeViewModel.cs rename to Gui/ViewModels/LocoTypes/Objects/TreeViewModel.cs diff --git a/Gui/ViewModels/DatTypes/Objects/TunnelViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/TunnelViewModel.cs similarity index 100% rename from Gui/ViewModels/DatTypes/Objects/TunnelViewModel.cs rename to Gui/ViewModels/LocoTypes/Objects/TunnelViewModel.cs diff --git a/Gui/ViewModels/DatTypes/Objects/VehicleViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/VehicleViewModel.cs similarity index 100% rename from Gui/ViewModels/DatTypes/Objects/VehicleViewModel.cs rename to Gui/ViewModels/LocoTypes/Objects/VehicleViewModel.cs diff --git a/Gui/ViewModels/DatTypes/Objects/WallViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/WallViewModel.cs similarity index 100% rename from Gui/ViewModels/DatTypes/Objects/WallViewModel.cs rename to Gui/ViewModels/LocoTypes/Objects/WallViewModel.cs diff --git a/Gui/ViewModels/DatTypes/Objects/WaterViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/WaterViewModel.cs similarity index 100% rename from Gui/ViewModels/DatTypes/Objects/WaterViewModel.cs rename to Gui/ViewModels/LocoTypes/Objects/WaterViewModel.cs diff --git a/Gui/ViewModels/DatTypes/SCV5ViewModel.cs b/Gui/ViewModels/LocoTypes/SCV5ViewModel.cs similarity index 100% rename from Gui/ViewModels/DatTypes/SCV5ViewModel.cs rename to Gui/ViewModels/LocoTypes/SCV5ViewModel.cs diff --git a/Gui/ViewModels/DatTypes/SoundEffectsViewModel.cs b/Gui/ViewModels/LocoTypes/SoundEffectsViewModel.cs similarity index 100% rename from Gui/ViewModels/DatTypes/SoundEffectsViewModel.cs rename to Gui/ViewModels/LocoTypes/SoundEffectsViewModel.cs diff --git a/Gui/ViewModels/DatTypes/TutorialViewModel.cs b/Gui/ViewModels/LocoTypes/TutorialViewModel.cs similarity index 100% rename from Gui/ViewModels/DatTypes/TutorialViewModel.cs rename to Gui/ViewModels/LocoTypes/TutorialViewModel.cs diff --git a/Gui/Views/BuildingComponentsView.axaml b/Gui/Views/BuildingComponentsView.axaml new file mode 100644 index 00000000..7b64b49c --- /dev/null +++ b/Gui/Views/BuildingComponentsView.axaml @@ -0,0 +1,18 @@ + + Welcome to Avalonia! + diff --git a/Gui/Views/BuildingComponentsView.axaml.cs b/Gui/Views/BuildingComponentsView.axaml.cs new file mode 100644 index 00000000..8142cd21 --- /dev/null +++ b/Gui/Views/BuildingComponentsView.axaml.cs @@ -0,0 +1,13 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; + +namespace Gui.Views; + +public partial class BuildingComponentsView : Window +{ + public BuildingComponentsView() + { + InitializeComponent(); + } +} From 607f98b32e4d4e3ecbcf3128479d99a58afd27ea Mon Sep 17 00:00:00 2001 From: Benjamin Sutas Date: Thu, 4 Sep 2025 17:44:39 +1000 Subject: [PATCH 07/21] building components view working --- Gui/App.axaml | 4 + .../Graphics/ImageTableViewModel.cs | 51 +------- .../LocoTypes/BuildingComponentsViewModel.cs | 116 +++++++++++++++++- .../LocoTypes/Objects/AirportViewModel.cs | 4 - .../LocoTypes/Objects/BuildingViewModel.cs | 3 - .../LocoTypes/Objects/DockViewModel.cs | 6 - .../LocoTypes/Objects/IndustryViewModel.cs | 1 - Gui/Views/BuildingComponentsView.axaml | 87 +++++++++++-- Gui/Views/BuildingComponentsView.axaml.cs | 4 +- Gui/Views/ImageTableView.axaml | 46 ++----- 10 files changed, 209 insertions(+), 113 deletions(-) diff --git a/Gui/App.axaml b/Gui/App.axaml index b79e9360..fa603be7 100644 --- a/Gui/App.axaml +++ b/Gui/App.axaml @@ -3,6 +3,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:materialIcons="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia" xmlns:local="using:Gui" + xmlns:domc="using:Definitions.ObjectModels.Objects.Common" xmlns:vm="using:Gui.ViewModels" xmlns:vi="using:Gui.Views" xmlns:oldt="using:Dat.Types" @@ -55,6 +56,9 @@ + + + diff --git a/Gui/ViewModels/Graphics/ImageTableViewModel.cs b/Gui/ViewModels/Graphics/ImageTableViewModel.cs index 109d9b83..25f8c9b4 100644 --- a/Gui/ViewModels/Graphics/ImageTableViewModel.cs +++ b/Gui/ViewModels/Graphics/ImageTableViewModel.cs @@ -6,6 +6,7 @@ using Definitions.ObjectModels; using Definitions.ObjectModels.Objects.Common; using Definitions.ObjectModels.Types; +using Gui.ViewModels.LocoTypes; using ReactiveUI; using ReactiveUI.Fody.Helpers; using SixLabors.ImageSharp; @@ -74,9 +75,7 @@ public class ImageTableViewModel : ReactiveObject, IExtraContentViewModel [Reactive] public ObservableCollection LayeredImages { get; set; } = []; - [Reactive] - public int OffsetSpacing { get; set; } - int prevOffset; + public BuildingComponentsViewModel? BuildingComponents { get; set; } public ImageTableViewModel(ImageTable imageTable, PaletteMap paletteMap, ILogger logger, BuildingComponentsModel buildingComponents = null) { @@ -88,54 +87,10 @@ public ImageTableViewModel(ImageTable imageTable, PaletteMap paletteMap, ILogger var givm = new GroupedImageViewModel(group.Name, imageViewModels); givm.SelectionModel.SelectionChanged += SelectionChanged; GroupedImageViewModels.Add(givm); - } // building components - if (buildingComponents != null) - { - var allGEs = imageTable.GraphicsElements; - var baseY = 128; - - foreach (var variation in buildingComponents.BuildingVariations) - { - var yOffset = 0; - foreach (var variationItem in variation) - { - var group = imageTable.Groups[variationItem]; - var numDirections = 4; - for (var i = 0; i < numDirections; ++i) - { - var x = new ImageViewModel(group.GraphicsElements[i], paletteMap); - x.XOffset += (i + 1) * 128; - x.YOffset += baseY + yOffset; - LayeredImages.Add(x); - } - - yOffset -= buildingComponents.BuildingHeights[variationItem]; - } - - baseY += 128; - } - } - - _ = this.WhenAnyValue(x => x.OffsetSpacing) - .Where(_ => LayeredImages.Count > 0) - .Subscribe(spacing => - { - var index = 0; - foreach (var img in LayeredImages) - { - var diff = OffsetSpacing - prevOffset; - img.YOffset -= diff * index; - img.RaisePropertyChanged(nameof(img.YOffset)); - img.RaisePropertyChanged(nameof(img)); - index++; - } - - prevOffset = OffsetSpacing; - this.RaisePropertyChanged(nameof(LayeredImages)); - }); + BuildingComponents = new(buildingComponents, imageTable.GraphicsElements, paletteMap); PaletteMap = paletteMap; Logger = logger; diff --git a/Gui/ViewModels/LocoTypes/BuildingComponentsViewModel.cs b/Gui/ViewModels/LocoTypes/BuildingComponentsViewModel.cs index 5bb114a3..3c62bb58 100644 --- a/Gui/ViewModels/LocoTypes/BuildingComponentsViewModel.cs +++ b/Gui/ViewModels/LocoTypes/BuildingComponentsViewModel.cs @@ -1,19 +1,133 @@ +using Avalonia.Media.Imaging; +using Definitions.ObjectModels; using Definitions.ObjectModels.Objects.Common; using Definitions.ObjectModels.Types; +using DynamicData; +using Gui.Models; +using Gui.ViewModels.Graphics; using ReactiveUI; +using ReactiveUI.Fody.Helpers; +using System; using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Reactive.Linq; namespace Gui.ViewModels.LocoTypes; +//public enum Direction : uint8_t +//{ +// North, +// East, +// South, +// West +//} + +public class BuildingLayerViewModel : ReactiveObject +{ + [Reactive] public Bitmap DisplayedImage { get; set; } + + public double Width => DisplayedImage.Size.Width; + public double Height => DisplayedImage.Size.Height; + + [Reactive] public double X { get; set; } + [Reactive] public double Y { get; set; } + [Reactive] public double XOffset { get; set; } + [Reactive] public double YOffset { get; set; } +} + +public class BuildingStackViewModel : ReactiveObject +{ + public ObservableCollection Layers { get; set; } = []; +} + +public class BuildingVariationViewModel : ReactiveObject +{ + public ObservableCollection Directions { get; set; } = []; +} + public class BuildingComponentsViewModel : ReactiveObject { public BuildingComponentsModel BuildingComponents { get; } List GraphicsElements { get; } - public BuildingComponentsViewModel(BuildingComponentsModel buildingComponents, List graphicsElements) + public ObservableCollection LayeredImages { get; set; } = []; + + public ObservableCollection BuildingVariations { get; set; } = []; + + public int OffsetSpacing { get; set; } + + public BuildingComponentsViewModel(BuildingComponentsModel buildingComponents, List graphicsElements, PaletteMap paletteMap) { BuildingComponents = buildingComponents; GraphicsElements = graphicsElements; + + var layers = graphicsElements.Chunk(4).ToList(); // each building part has 4 directions + + //var allGEs = graphicsElements; + var baseX = 128; + var baseY = 128; + + //int i = 0; + foreach (var variation in buildingComponents.BuildingVariations) + { + var bv = new BuildingVariationViewModel(); + + var numDirections = 4; + for (var i = 0; i < numDirections; ++i) + { + var bs = new BuildingStackViewModel(); + var cumulativeOffset = 0; + foreach (var variationItem in variation) + { + var layer = layers[variationItem]; + var bl = new BuildingLayerViewModel(); + + if (!paletteMap.TryConvertG1ToRgba32Bitmap(layer[i], ColourRemapSwatch.PrimaryRemap, ColourRemapSwatch.SecondaryRemap, out var image)) + { + throw new Exception("Failed to convert image"); + } + + bl.DisplayedImage = image.ToAvaloniaBitmap(); + bl.X = layer[i].XOffset + baseX; + + bl.Y = layer[i].YOffset - cumulativeOffset + baseY; + cumulativeOffset += buildingComponents.BuildingHeights[variationItem]; + + //bl.XOffset = 0; + //bl.YOffset = 128; + bs.Layers.Add(bl); + } + + bv.Directions.Add(bs); // [i] = bs; + + //yOffset -= buildingComponents.BuildingHeights[variationItem]; + } + + //baseY += 128; + BuildingVariations.Add(bv); + + //i++; + } + + //_ = this.WhenAnyValue(x => x.OffsetSpacing) + // .Where(_ => LayeredImages.Count > 0) + // .Subscribe(spacing => + // { + // var index = 0; + // foreach (var img in LayeredImages) + // { + // var diff = OffsetSpacing - prevOffset; + // img.YOffset -= diff * index; + // img.RaisePropertyChanged(nameof(img.YOffset)); + // img.RaisePropertyChanged(nameof(img)); + // index++; + // } + + // prevOffset = OffsetSpacing; + // this.RaisePropertyChanged(nameof(LayeredImages)); + // }); } } + diff --git a/Gui/ViewModels/LocoTypes/Objects/AirportViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/AirportViewModel.cs index 5d0b59e1..0a1133f8 100644 --- a/Gui/ViewModels/LocoTypes/Objects/AirportViewModel.cs +++ b/Gui/ViewModels/LocoTypes/Objects/AirportViewModel.cs @@ -1,11 +1,7 @@ -using Dat.Loaders; using Definitions.ObjectModels.Objects.Airport; -using Definitions.ObjectModels.Objects.Common; -using PropertyModels.Extensions; using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations; -using System.Linq; namespace Gui.ViewModels; diff --git a/Gui/ViewModels/LocoTypes/Objects/BuildingViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/BuildingViewModel.cs index 6058c852..39f91e51 100644 --- a/Gui/ViewModels/LocoTypes/Objects/BuildingViewModel.cs +++ b/Gui/ViewModels/LocoTypes/Objects/BuildingViewModel.cs @@ -1,10 +1,7 @@ using Dat.Loaders; using Definitions.ObjectModels.Objects.Building; -using Definitions.ObjectModels.Objects.Common; using Definitions.ObjectModels.Types; -using Gui.ViewModels.LocoTypes; using PropertyModels.ComponentModel.DataAnnotations; -using PropertyModels.Extensions; using ReactiveUI.Fody.Helpers; using System.Collections.Generic; using System.ComponentModel; diff --git a/Gui/ViewModels/LocoTypes/Objects/DockViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/DockViewModel.cs index 4936bb4c..d233ba7f 100644 --- a/Gui/ViewModels/LocoTypes/Objects/DockViewModel.cs +++ b/Gui/ViewModels/LocoTypes/Objects/DockViewModel.cs @@ -1,13 +1,7 @@ -using Dat.Loaders; -using Definitions.ObjectModels.Objects.Common; using Definitions.ObjectModels.Objects.Dock; using Definitions.ObjectModels.Types; using PropertyModels.ComponentModel.DataAnnotations; -using PropertyModels.Extensions; -using System.Collections.Generic; using System.ComponentModel; -using System.ComponentModel.DataAnnotations; -using System.Linq; namespace Gui.ViewModels; diff --git a/Gui/ViewModels/LocoTypes/Objects/IndustryViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/IndustryViewModel.cs index 1359f198..6369ba3a 100644 --- a/Gui/ViewModels/LocoTypes/Objects/IndustryViewModel.cs +++ b/Gui/ViewModels/LocoTypes/Objects/IndustryViewModel.cs @@ -1,5 +1,4 @@ using Dat.Loaders; -using Definitions.ObjectModels.Objects.Common; using Definitions.ObjectModels.Objects.Industry; using Definitions.ObjectModels.Types; using PropertyModels.ComponentModel.DataAnnotations; diff --git a/Gui/Views/BuildingComponentsView.axaml b/Gui/Views/BuildingComponentsView.axaml index 7b64b49c..4b43b1b3 100644 --- a/Gui/Views/BuildingComponentsView.axaml +++ b/Gui/Views/BuildingComponentsView.axaml @@ -1,18 +1,85 @@ - - Welcome to Avalonia! - + x:DataType="vm:LocoTypes.BuildingComponentsViewModel"> + + + + Vertical sprite spacing + + + + + + + + + + + + + + + + + Variation + + + + + + + + + + + + Direction + + + + + + + + + + Layer + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Gui/Views/BuildingComponentsView.axaml.cs b/Gui/Views/BuildingComponentsView.axaml.cs index 8142cd21..5a11c335 100644 --- a/Gui/Views/BuildingComponentsView.axaml.cs +++ b/Gui/Views/BuildingComponentsView.axaml.cs @@ -1,10 +1,8 @@ -using Avalonia; using Avalonia.Controls; -using Avalonia.Markup.Xaml; namespace Gui.Views; -public partial class BuildingComponentsView : Window +public partial class BuildingComponentsView : UserControl { public BuildingComponentsView() { diff --git a/Gui/Views/ImageTableView.axaml b/Gui/Views/ImageTableView.axaml index b1de18dc..995a4dec 100644 --- a/Gui/Views/ImageTableView.axaml +++ b/Gui/Views/ImageTableView.axaml @@ -4,6 +4,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:vmg="using:Gui.ViewModels.Graphics" + xmlns:vmlt="using:Gui.ViewModels.LocoTypes" xmlns:materialIcons="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia" xmlns:pgc="clr-namespace:Avalonia.PropertyGrid.Controls;assembly=Avalonia.PropertyGrid" xmlns:amxc="using:Avalonia.Markup.Xaml.Converters" @@ -140,50 +141,21 @@ - - - - - - - - - - - - - - - - - - - - - - - + - - - - - --> - - - - --> - - - --> + - + + From a4514e676c4760d9a27fab408149362cefc85825 Mon Sep 17 00:00:00 2001 From: Benjamin Sutas Date: Fri, 5 Sep 2025 14:02:05 +1000 Subject: [PATCH 08/21] flyout-vs-menuflyout --- .../LocoTypes/BuildingComponentsViewModel.cs | 35 +++--- Gui/Views/BuildingComponentsView.axaml | 112 +++++++++--------- Gui/Views/ImageTableView.axaml | 80 +++++++++---- 3 files changed, 132 insertions(+), 95 deletions(-) diff --git a/Gui/ViewModels/LocoTypes/BuildingComponentsViewModel.cs b/Gui/ViewModels/LocoTypes/BuildingComponentsViewModel.cs index 3c62bb58..ff039548 100644 --- a/Gui/ViewModels/LocoTypes/BuildingComponentsViewModel.cs +++ b/Gui/ViewModels/LocoTypes/BuildingComponentsViewModel.cs @@ -2,7 +2,6 @@ using Definitions.ObjectModels; using Definitions.ObjectModels.Objects.Common; using Definitions.ObjectModels.Types; -using DynamicData; using Gui.Models; using Gui.ViewModels.Graphics; using ReactiveUI; @@ -15,13 +14,13 @@ namespace Gui.ViewModels.LocoTypes; -//public enum Direction : uint8_t -//{ -// North, -// East, -// South, -// West -//} +public enum CardinalDirection : uint8_t +{ + South, + West, + North, + East, +} public class BuildingLayerViewModel : ReactiveObject { @@ -38,6 +37,7 @@ public class BuildingLayerViewModel : ReactiveObject public class BuildingStackViewModel : ReactiveObject { + public CardinalDirection Direction { get; init; } public ObservableCollection Layers { get; set; } = []; } @@ -65,11 +65,9 @@ public BuildingComponentsViewModel(BuildingComponentsModel buildingComponents, L var layers = graphicsElements.Chunk(4).ToList(); // each building part has 4 directions - //var allGEs = graphicsElements; var baseX = 128; - var baseY = 128; + var baseY = 192; - //int i = 0; foreach (var variation in buildingComponents.BuildingVariations) { var bv = new BuildingVariationViewModel(); @@ -77,7 +75,11 @@ public BuildingComponentsViewModel(BuildingComponentsModel buildingComponents, L var numDirections = 4; for (var i = 0; i < numDirections; ++i) { - var bs = new BuildingStackViewModel(); + var bs = new BuildingStackViewModel() + { + Direction = (CardinalDirection)i + }; + var cumulativeOffset = 0; foreach (var variationItem in variation) { @@ -91,12 +93,12 @@ public BuildingComponentsViewModel(BuildingComponentsModel buildingComponents, L bl.DisplayedImage = image.ToAvaloniaBitmap(); bl.X = layer[i].XOffset + baseX; - bl.Y = layer[i].YOffset - cumulativeOffset + baseY; cumulativeOffset += buildingComponents.BuildingHeights[variationItem]; - //bl.XOffset = 0; - //bl.YOffset = 128; + bl.XOffset = 0; + bl.YOffset = 0; + bs.Layers.Add(bl); } @@ -105,10 +107,7 @@ public BuildingComponentsViewModel(BuildingComponentsModel buildingComponents, L //yOffset -= buildingComponents.BuildingHeights[variationItem]; } - //baseY += 128; BuildingVariations.Add(bv); - - //i++; } //_ = this.WhenAnyValue(x => x.OffsetSpacing) diff --git a/Gui/Views/BuildingComponentsView.axaml b/Gui/Views/BuildingComponentsView.axaml index 4b43b1b3..4f6557d3 100644 --- a/Gui/Views/BuildingComponentsView.axaml +++ b/Gui/Views/BuildingComponentsView.axaml @@ -22,63 +22,63 @@ - - - - - - - - - - - - Variation - - - - - - - - - - - - Direction - - - - - - - - - - Layer - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + Variation + + + + + + + + + + + + + + + + + + + + + + Layer + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Gui/Views/ImageTableView.axaml b/Gui/Views/ImageTableView.axaml index 995a4dec..9b913e9b 100644 --- a/Gui/Views/ImageTableView.axaml +++ b/Gui/Views/ImageTableView.axaml @@ -36,36 +36,74 @@ Export - + + + - - - - - + + + + From 0fe522970df92d0a3f4eb916dd7285c00571f8df Mon Sep 17 00:00:00 2001 From: Benjamin Sutas Date: Fri, 5 Sep 2025 14:02:05 +1000 Subject: [PATCH 09/21] add colour swatch effect --- Definitions/ObjectModels/PaletteMap.cs | 2 +- .../Graphics/ImageTableViewModel.cs | 88 +++++++++-- .../LocoTypes/BuildingComponentsViewModel.cs | 35 ++--- Gui/Views/BuildingComponentsView.axaml | 112 +++++++------- Gui/Views/ImageTableView.axaml | 145 +++++++++++++++--- 5 files changed, 274 insertions(+), 108 deletions(-) diff --git a/Definitions/ObjectModels/PaletteMap.cs b/Definitions/ObjectModels/PaletteMap.cs index 96a0628e..4650bc00 100644 --- a/Definitions/ObjectModels/PaletteMap.cs +++ b/Definitions/ObjectModels/PaletteMap.cs @@ -125,7 +125,7 @@ public static (Color Color, byte Index) Transparent public (Color Color, byte Index)[] Brown => Palette[214..226]; public (Color Color, byte Index)[] Amber - => [.. Palette[230..240], .. Palette[244..246]]; + => [.. Palette[230..240], .. Palette[243..245]]; #endregion diff --git a/Gui/ViewModels/Graphics/ImageTableViewModel.cs b/Gui/ViewModels/Graphics/ImageTableViewModel.cs index 25f8c9b4..7992d551 100644 --- a/Gui/ViewModels/Graphics/ImageTableViewModel.cs +++ b/Gui/ViewModels/Graphics/ImageTableViewModel.cs @@ -1,4 +1,6 @@ using Avalonia.Controls.Selection; +using Avalonia.Data.Converters; +using Avalonia.Media; using Avalonia.Threading; using Common; using Common.Json; @@ -14,14 +16,67 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Globalization; using System.IO; using System.Linq; using System.Reactive.Linq; using System.Threading.Tasks; using System.Windows.Input; +using SLColour = SixLabors.ImageSharp.Color; +using AvaColour = Avalonia.Media.Color; namespace Gui.ViewModels.Graphics; +public class ColourArrayToGradientStopsConverter : IValueConverter +{ + public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) + { + if (value is AvaColour[] colours) + { + var stops = new GradientStops(); + var segmentWidth = 1.0 / colours.Length; + + for (var i = 0; i < colours.Length; i++) + { + // Add a stop at the beginning of the segment + stops.Add(new GradientStop(colours[i], i * segmentWidth)); + + // Add a second stop at the end of the segment to create a sharp transition + stops.Add(new GradientStop(colours[i], (i + 1) * segmentWidth)); + } + + return stops; + } + + return new GradientStops(); + } + + public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } +} + +public static class SixLaboursAvaloniaColor +{ + public static AvaColour ToAvaloniaColor(this SLColour color) + { + var pixel = color.ToPixel(); + return AvaColour.FromArgb(pixel.A, pixel.R, pixel.G, pixel.B); + } + + public static SLColour ToSixLaborsColor(this AvaColour color) + => SLColour.FromRgba(color.R, color.G, color.B, color.A); +} + +public class ColourRemapSwatchViewModel +{ + [Reactive] public ColourRemapSwatch Swatch { get; init; } + [Reactive] public AvaColour Colour { get; init; } + + [Reactive] public AvaColour[] GradientColours { get; init; } +} + public class ImageTableViewModel : ReactiveObject, IExtraContentViewModel { public string Name => "Image Table"; @@ -30,10 +85,13 @@ public class ImageTableViewModel : ReactiveObject, IExtraContentViewModel public static ColourRemapSwatch[] ColourSwatchesArr { get; } = Enum.GetValues(); [Reactive] - public ColourRemapSwatch SelectedPrimarySwatch { get; set; } = ColourRemapSwatch.PrimaryRemap; + public List ColourSwatches { get; init; } + + [Reactive] + public ColourRemapSwatchViewModel SelectedPrimarySwatch { get; set; } [Reactive] - public ColourRemapSwatch SelectedSecondarySwatch { get; set; } = ColourRemapSwatch.SecondaryRemap; + public ColourRemapSwatchViewModel SelectedSecondarySwatch { get; set; } [Reactive] public ICommand ImportImagesCommand { get; set; } @@ -79,26 +137,38 @@ public class ImageTableViewModel : ReactiveObject, IExtraContentViewModel public ImageTableViewModel(ImageTable imageTable, PaletteMap paletteMap, ILogger logger, BuildingComponentsModel buildingComponents = null) { + ArgumentNullException.ThrowIfNull(imageTable); ArgumentNullException.ThrowIfNull(paletteMap); + PaletteMap = paletteMap; + Logger = logger; + + // swatches/palettes + ColourSwatches = [.. ColourSwatchesArr.Select(x => new ColourRemapSwatchViewModel() + { + Swatch = x, + Colour = paletteMap.GetRemapSwatchFromName(x)[0].Color.ToAvaloniaColor(), + GradientColours = [.. paletteMap.GetRemapSwatchFromName(x).Select(x => x.Color.ToAvaloniaColor())], + })]; + SelectedPrimarySwatch = ColourSwatches.Single(x => x.Swatch == ColourRemapSwatch.PrimaryRemap); + SelectedSecondarySwatch = ColourSwatches.Single(x => x.Swatch == ColourRemapSwatch.SecondaryRemap); + + // image tables foreach (var group in imageTable.Groups) { - var imageViewModels = group.GraphicsElements.Select(ge => new ImageViewModel(ge, paletteMap)); + var imageViewModels = group.GraphicsElements.Select(ge => new ImageViewModel(ge, PaletteMap)); var givm = new GroupedImageViewModel(group.Name, imageViewModels); givm.SelectionModel.SelectionChanged += SelectionChanged; GroupedImageViewModels.Add(givm); } // building components - BuildingComponents = new(buildingComponents, imageTable.GraphicsElements, paletteMap); - - PaletteMap = paletteMap; - Logger = logger; + BuildingComponents = new(buildingComponents, imageTable.GraphicsElements, PaletteMap); _ = this.WhenAnyValue(o => o.SelectedPrimarySwatch).Skip(1) - .Subscribe(_ => RecolourImages(SelectedPrimarySwatch, SelectedSecondarySwatch)); + .Subscribe(_ => RecolourImages(SelectedPrimarySwatch.Swatch, SelectedSecondarySwatch.Swatch)); _ = this.WhenAnyValue(o => o.SelectedSecondarySwatch).Skip(1) - .Subscribe(_ => RecolourImages(SelectedPrimarySwatch, SelectedSecondarySwatch)); + .Subscribe(_ => RecolourImages(SelectedPrimarySwatch.Swatch, SelectedSecondarySwatch.Swatch)); _ = this.WhenAnyValue(o => o.AnimationSpeed) .Where(_ => animationTimer != null) .Subscribe(_ => animationTimer!.Interval = TimeSpan.FromMilliseconds(1000 / AnimationSpeed)); diff --git a/Gui/ViewModels/LocoTypes/BuildingComponentsViewModel.cs b/Gui/ViewModels/LocoTypes/BuildingComponentsViewModel.cs index 3c62bb58..ff039548 100644 --- a/Gui/ViewModels/LocoTypes/BuildingComponentsViewModel.cs +++ b/Gui/ViewModels/LocoTypes/BuildingComponentsViewModel.cs @@ -2,7 +2,6 @@ using Definitions.ObjectModels; using Definitions.ObjectModels.Objects.Common; using Definitions.ObjectModels.Types; -using DynamicData; using Gui.Models; using Gui.ViewModels.Graphics; using ReactiveUI; @@ -15,13 +14,13 @@ namespace Gui.ViewModels.LocoTypes; -//public enum Direction : uint8_t -//{ -// North, -// East, -// South, -// West -//} +public enum CardinalDirection : uint8_t +{ + South, + West, + North, + East, +} public class BuildingLayerViewModel : ReactiveObject { @@ -38,6 +37,7 @@ public class BuildingLayerViewModel : ReactiveObject public class BuildingStackViewModel : ReactiveObject { + public CardinalDirection Direction { get; init; } public ObservableCollection Layers { get; set; } = []; } @@ -65,11 +65,9 @@ public BuildingComponentsViewModel(BuildingComponentsModel buildingComponents, L var layers = graphicsElements.Chunk(4).ToList(); // each building part has 4 directions - //var allGEs = graphicsElements; var baseX = 128; - var baseY = 128; + var baseY = 192; - //int i = 0; foreach (var variation in buildingComponents.BuildingVariations) { var bv = new BuildingVariationViewModel(); @@ -77,7 +75,11 @@ public BuildingComponentsViewModel(BuildingComponentsModel buildingComponents, L var numDirections = 4; for (var i = 0; i < numDirections; ++i) { - var bs = new BuildingStackViewModel(); + var bs = new BuildingStackViewModel() + { + Direction = (CardinalDirection)i + }; + var cumulativeOffset = 0; foreach (var variationItem in variation) { @@ -91,12 +93,12 @@ public BuildingComponentsViewModel(BuildingComponentsModel buildingComponents, L bl.DisplayedImage = image.ToAvaloniaBitmap(); bl.X = layer[i].XOffset + baseX; - bl.Y = layer[i].YOffset - cumulativeOffset + baseY; cumulativeOffset += buildingComponents.BuildingHeights[variationItem]; - //bl.XOffset = 0; - //bl.YOffset = 128; + bl.XOffset = 0; + bl.YOffset = 0; + bs.Layers.Add(bl); } @@ -105,10 +107,7 @@ public BuildingComponentsViewModel(BuildingComponentsModel buildingComponents, L //yOffset -= buildingComponents.BuildingHeights[variationItem]; } - //baseY += 128; BuildingVariations.Add(bv); - - //i++; } //_ = this.WhenAnyValue(x => x.OffsetSpacing) diff --git a/Gui/Views/BuildingComponentsView.axaml b/Gui/Views/BuildingComponentsView.axaml index 4b43b1b3..4f6557d3 100644 --- a/Gui/Views/BuildingComponentsView.axaml +++ b/Gui/Views/BuildingComponentsView.axaml @@ -22,63 +22,63 @@ - - - - - - - - - - - - Variation - - - - - - - - - - - - Direction - - - - - - - - - - Layer - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + Variation + + + + + + + + + + + + + + + + + + + + + + Layer + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Gui/Views/ImageTableView.axaml b/Gui/Views/ImageTableView.axaml index 995a4dec..9b1ec993 100644 --- a/Gui/Views/ImageTableView.axaml +++ b/Gui/Views/ImageTableView.axaml @@ -3,14 +3,14 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:vmg="using:Gui.ViewModels.Graphics" - xmlns:vmlt="using:Gui.ViewModels.LocoTypes" xmlns:materialIcons="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia" xmlns:pgc="clr-namespace:Avalonia.PropertyGrid.Controls;assembly=Avalonia.PropertyGrid" xmlns:amxc="using:Avalonia.Markup.Xaml.Converters" - xmlns:gmc="using:Gui.Models.Converters" xmlns:paz="using:Avalonia.Controls.PanAndZoom" xmlns:vi="using:Gui.Views" + xmlns:gmc="using:Gui.Models.Converters" + xmlns:vmg="using:Gui.ViewModels.Graphics" + xmlns:vmlt="using:Gui.ViewModels.LocoTypes" mc:Ignorable="d" d:DesignWidth="1200" d:DesignHeight="900" @@ -20,6 +20,7 @@ + @@ -36,36 +37,132 @@ Export - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + + + From 91165016753cdfd63ecc95c6a2bb13b3a9577dcb Mon Sep 17 00:00:00 2001 From: Benjamin Sutas Date: Sat, 6 Sep 2025 15:43:39 +1000 Subject: [PATCH 10/21] move around converters --- Dat/Loaders/ImageTableLoader.cs | 13 +-- .../Objects/Building/BuildingObject.cs | 8 ++ .../ColourArrayToGradientStopsConverter.cs | 37 +++++++ .../Converters/EnumDescriptionConverter.cs | 2 +- .../Converters/EnumToBooleanConverter.cs | 2 +- .../Converters/EnumToMaterialIconConverter.cs | 5 +- .../Converters/NegativeValueConverter.cs | 2 +- .../Graphics/ImageTableViewModel.cs | 35 +----- .../LocoTypes/BuildingComponentsViewModel.cs | 102 ++++++++++-------- Gui/Views/BuildingComponentsView.axaml | 48 ++++----- Gui/Views/FolderTreeView.axaml | 6 +- Gui/Views/ImageTableView.axaml | 10 +- Gui/Views/MainWindow.axaml | 11 +- Gui/Views/SCV5View.axaml | 8 +- 14 files changed, 154 insertions(+), 135 deletions(-) create mode 100644 Gui/Converters/ColourArrayToGradientStopsConverter.cs rename Gui/{Models => }/Converters/EnumDescriptionConverter.cs (95%) rename Gui/{Models => }/Converters/EnumToBooleanConverter.cs (94%) rename Gui/{Models => }/Converters/EnumToMaterialIconConverter.cs (96%) rename Gui/{Models => }/Converters/NegativeValueConverter.cs (96%) diff --git a/Dat/Loaders/ImageTableLoader.cs b/Dat/Loaders/ImageTableLoader.cs index a8e1e1e0..e2ff1ce4 100644 --- a/Dat/Loaders/ImageTableLoader.cs +++ b/Dat/Loaders/ImageTableLoader.cs @@ -12,13 +12,8 @@ public static ImageTable CreateImageTable(List imageList) } private static void CreateBasicGroups(List imageList, ImageTable imageTable) - { - var chunks = imageList.Chunk(4); - imageTable.Groups.Add(("Base", chunks.First().ToList())); - var floorCount = 0; - foreach (var chunk in chunks.Skip(1)) - { - imageTable.Groups.Add(($"Floor {floorCount++}", chunk.ToList())); - } - } + => imageTable.Groups = imageList + .Chunk(4) + .Select((x, i) => ($"Part {i}", x.ToList())) + .ToList(); } diff --git a/Definitions/ObjectModels/Objects/Building/BuildingObject.cs b/Definitions/ObjectModels/Objects/Building/BuildingObject.cs index 4f99a8d0..4d9f9a3d 100644 --- a/Definitions/ObjectModels/Objects/Building/BuildingObject.cs +++ b/Definitions/ObjectModels/Objects/Building/BuildingObject.cs @@ -3,6 +3,14 @@ namespace Definitions.ObjectModels.Objects.Building; +public enum CardinalDirection : uint8_t +{ + South, + West, + North, + East, +} + public class BuildingObject : ILocoStruct, IHasBuildingComponents { public uint16_t DesignedYear { get; set; } diff --git a/Gui/Converters/ColourArrayToGradientStopsConverter.cs b/Gui/Converters/ColourArrayToGradientStopsConverter.cs new file mode 100644 index 00000000..41dc1b23 --- /dev/null +++ b/Gui/Converters/ColourArrayToGradientStopsConverter.cs @@ -0,0 +1,37 @@ +using Avalonia.Data.Converters; +using Avalonia.Media; +using System; +using System.Globalization; +using AvaColour = Avalonia.Media.Color; + +namespace Gui.Converters; + +public class ColourArrayToGradientStopsConverter : IValueConverter +{ + public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) + { + if (value is AvaColour[] colours) + { + var stops = new GradientStops(); + var segmentWidth = 1.0 / colours.Length; + + for (var i = 0; i < colours.Length; i++) + { + // Add a stop at the beginning of the segment + stops.Add(new GradientStop(colours[i], i * segmentWidth)); + + // Add a second stop at the end of the segment to create a sharp transition + stops.Add(new GradientStop(colours[i], (i + 1) * segmentWidth)); + } + + return stops; + } + + return new GradientStops(); + } + + public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } +} diff --git a/Gui/Models/Converters/EnumDescriptionConverter.cs b/Gui/Converters/EnumDescriptionConverter.cs similarity index 95% rename from Gui/Models/Converters/EnumDescriptionConverter.cs rename to Gui/Converters/EnumDescriptionConverter.cs index 56b80966..6d17f287 100644 --- a/Gui/Models/Converters/EnumDescriptionConverter.cs +++ b/Gui/Converters/EnumDescriptionConverter.cs @@ -3,7 +3,7 @@ using System; using System.Globalization; -namespace Gui.Models.Converters; +namespace Gui.Converters; public class EnumDescriptionConverter : MarkupExtension, IValueConverter { diff --git a/Gui/Models/Converters/EnumToBooleanConverter.cs b/Gui/Converters/EnumToBooleanConverter.cs similarity index 94% rename from Gui/Models/Converters/EnumToBooleanConverter.cs rename to Gui/Converters/EnumToBooleanConverter.cs index 55b2972d..62178362 100644 --- a/Gui/Models/Converters/EnumToBooleanConverter.cs +++ b/Gui/Converters/EnumToBooleanConverter.cs @@ -2,7 +2,7 @@ using System; using System.Globalization; -namespace Gui.Models.Converters; +namespace Gui.Converters; public class EnumToBooleanConverter : IValueConverter { diff --git a/Gui/Models/Converters/EnumToMaterialIconConverter.cs b/Gui/Converters/EnumToMaterialIconConverter.cs similarity index 96% rename from Gui/Models/Converters/EnumToMaterialIconConverter.cs rename to Gui/Converters/EnumToMaterialIconConverter.cs index 7498de94..c4263916 100644 --- a/Gui/Models/Converters/EnumToMaterialIconConverter.cs +++ b/Gui/Converters/EnumToMaterialIconConverter.cs @@ -1,11 +1,12 @@ using Avalonia.Data.Converters; using Definitions.ObjectModels.Objects.Vehicle; using Definitions.ObjectModels.Types; +using Gui.Models; using System; using System.Collections.Generic; using System.Globalization; -namespace Gui.Models.Converters; +namespace Gui.Converters; public class EnumToMaterialIconConverter : IValueConverter { @@ -23,7 +24,7 @@ public class EnumToMaterialIconConverter : IValueConverter { return icon; } - else if ((value is T source2) && mapping.TryGetValue(source2, out var icon2)) + else if (value is T source2 && mapping.TryGetValue(source2, out var icon2)) { return icon2; } diff --git a/Gui/Models/Converters/NegativeValueConverter.cs b/Gui/Converters/NegativeValueConverter.cs similarity index 96% rename from Gui/Models/Converters/NegativeValueConverter.cs rename to Gui/Converters/NegativeValueConverter.cs index 86eae332..37548ebf 100644 --- a/Gui/Models/Converters/NegativeValueConverter.cs +++ b/Gui/Converters/NegativeValueConverter.cs @@ -1,7 +1,7 @@ using Avalonia.Data.Converters; using System; -namespace Gui.Models.Converters; +namespace Gui.Converters; public class NegativeValueConverter : IValueConverter { diff --git a/Gui/ViewModels/Graphics/ImageTableViewModel.cs b/Gui/ViewModels/Graphics/ImageTableViewModel.cs index 7992d551..63eee6d6 100644 --- a/Gui/ViewModels/Graphics/ImageTableViewModel.cs +++ b/Gui/ViewModels/Graphics/ImageTableViewModel.cs @@ -1,6 +1,4 @@ using Avalonia.Controls.Selection; -using Avalonia.Data.Converters; -using Avalonia.Media; using Avalonia.Threading; using Common; using Common.Json; @@ -16,7 +14,6 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Globalization; using System.IO; using System.Linq; using System.Reactive.Linq; @@ -27,36 +24,6 @@ namespace Gui.ViewModels.Graphics; -public class ColourArrayToGradientStopsConverter : IValueConverter -{ - public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) - { - if (value is AvaColour[] colours) - { - var stops = new GradientStops(); - var segmentWidth = 1.0 / colours.Length; - - for (var i = 0; i < colours.Length; i++) - { - // Add a stop at the beginning of the segment - stops.Add(new GradientStop(colours[i], i * segmentWidth)); - - // Add a second stop at the end of the segment to create a sharp transition - stops.Add(new GradientStop(colours[i], (i + 1) * segmentWidth)); - } - - return stops; - } - - return new GradientStops(); - } - - public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) - { - throw new NotImplementedException(); - } -} - public static class SixLaboursAvaloniaColor { public static AvaColour ToAvaloniaColor(this SLColour color) @@ -163,7 +130,7 @@ public ImageTableViewModel(ImageTable imageTable, PaletteMap paletteMap, ILogger } // building components - BuildingComponents = new(buildingComponents, imageTable.GraphicsElements, PaletteMap); + BuildingComponents = new(buildingComponents, imageTable, PaletteMap); _ = this.WhenAnyValue(o => o.SelectedPrimarySwatch).Skip(1) .Subscribe(_ => RecolourImages(SelectedPrimarySwatch.Swatch, SelectedSecondarySwatch.Swatch)); diff --git a/Gui/ViewModels/LocoTypes/BuildingComponentsViewModel.cs b/Gui/ViewModels/LocoTypes/BuildingComponentsViewModel.cs index ff039548..6b6039aa 100644 --- a/Gui/ViewModels/LocoTypes/BuildingComponentsViewModel.cs +++ b/Gui/ViewModels/LocoTypes/BuildingComponentsViewModel.cs @@ -2,6 +2,7 @@ using Definitions.ObjectModels; using Definitions.ObjectModels.Objects.Common; using Definitions.ObjectModels.Types; +using DynamicData.Binding; using Gui.Models; using Gui.ViewModels.Graphics; using ReactiveUI; @@ -14,14 +15,6 @@ namespace Gui.ViewModels.LocoTypes; -public enum CardinalDirection : uint8_t -{ - South, - West, - North, - East, -} - public class BuildingLayerViewModel : ReactiveObject { [Reactive] public Bitmap DisplayedImage { get; set; } @@ -29,10 +22,27 @@ public class BuildingLayerViewModel : ReactiveObject public double Width => DisplayedImage.Size.Width; public double Height => DisplayedImage.Size.Height; - [Reactive] public double X { get; set; } - [Reactive] public double Y { get; set; } + public double X => XBase + XOffset; + public double Y => YBase + YOffset; + + [Reactive] public double XBase { get; set; } + [Reactive] public double YBase { get; set; } + [Reactive] public double XOffset { get; set; } [Reactive] public double YOffset { get; set; } + + public BuildingLayerViewModel() + { + _ = this.WhenAnyValue(o => o.XBase) + .Subscribe(_ => this.RaisePropertyChanged(nameof(X))); + _ = this.WhenAnyValue(o => o.XOffset) + .Subscribe(_ => this.RaisePropertyChanged(nameof(X))); + + _ = this.WhenAnyValue(o => o.YBase) + .Subscribe(_ => this.RaisePropertyChanged(nameof(Y))); + _ = this.WhenAnyValue(o => o.YOffset) + .Subscribe(_ => this.RaisePropertyChanged(nameof(Y))); + } } public class BuildingStackViewModel : ReactiveObject @@ -50,29 +60,27 @@ public class BuildingComponentsViewModel : ReactiveObject { public BuildingComponentsModel BuildingComponents { get; } - List GraphicsElements { get; } - - public ObservableCollection LayeredImages { get; set; } = []; - public ObservableCollection BuildingVariations { get; set; } = []; - public int OffsetSpacing { get; set; } + [Reactive] public int OffsetSpacing { get; set; } + + [Reactive] public int MaxWidth { get; set; } + [Reactive] public int MaxHeight { get; set; } - public BuildingComponentsViewModel(BuildingComponentsModel buildingComponents, List graphicsElements, PaletteMap paletteMap) + public BuildingComponentsViewModel(BuildingComponentsModel buildingComponents, ImageTable imageTable, PaletteMap paletteMap) { BuildingComponents = buildingComponents; - GraphicsElements = graphicsElements; - var layers = graphicsElements.Chunk(4).ToList(); // each building part has 4 directions + var layers = imageTable.Groups.ConvertAll(x => x.GraphicsElements); // each building part has 4 directions - var baseX = 128; - var baseY = 192; + MaxWidth = layers.Max(x => x.Max(y => y.Width)) + 16; + MaxHeight = (layers.Max(x => x.Max(y => y.Height)) * buildingComponents.BuildingHeights.Count) + buildingComponents.BuildingHeights.Sum(x => x) + (buildingComponents.BuildingVariations.Max(x => x.Count) * OffsetSpacing); foreach (var variation in buildingComponents.BuildingVariations) { var bv = new BuildingVariationViewModel(); - var numDirections = 4; + const int numDirections = 4; for (var i = 0; i < numDirections; ++i) { var bs = new BuildingStackViewModel() @@ -92,8 +100,8 @@ public BuildingComponentsViewModel(BuildingComponentsModel buildingComponents, L } bl.DisplayedImage = image.ToAvaloniaBitmap(); - bl.X = layer[i].XOffset + baseX; - bl.Y = layer[i].YOffset - cumulativeOffset + baseY; + bl.XBase = layer[i].XOffset + MaxWidth / 2; + bl.YBase = layer[i].YOffset - cumulativeOffset + (MaxHeight * 0.80); cumulativeOffset += buildingComponents.BuildingHeights[variationItem]; bl.XOffset = 0; @@ -103,30 +111,40 @@ public BuildingComponentsViewModel(BuildingComponentsModel buildingComponents, L } bv.Directions.Add(bs); // [i] = bs; - - //yOffset -= buildingComponents.BuildingHeights[variationItem]; } BuildingVariations.Add(bv); } - //_ = this.WhenAnyValue(x => x.OffsetSpacing) - // .Where(_ => LayeredImages.Count > 0) - // .Subscribe(spacing => - // { - // var index = 0; - // foreach (var img in LayeredImages) - // { - // var diff = OffsetSpacing - prevOffset; - // img.YOffset -= diff * index; - // img.RaisePropertyChanged(nameof(img.YOffset)); - // img.RaisePropertyChanged(nameof(img)); - // index++; - // } - - // prevOffset = OffsetSpacing; - // this.RaisePropertyChanged(nameof(LayeredImages)); - // }); + // Update all nested layers' YOffset whenever OffsetSpacing changes + _ = this.WhenAnyValue(x => x.OffsetSpacing) + .Subscribe(ApplyOffsetToAllLayers); + } + + private void ApplyOffsetToAllLayers(int offset) + { + foreach (var variation in BuildingVariations) + { + if (variation?.Directions == null) + { + continue; + } + + foreach (var dir in variation.Directions) + { + if (dir?.Layers == null) + { + continue; + } + + var cumulativeOffset = 0; + foreach (var layer in dir.Layers) + { + layer.YOffset = cumulativeOffset; + cumulativeOffset -= OffsetSpacing; + } + } + } } } diff --git a/Gui/Views/BuildingComponentsView.axaml b/Gui/Views/BuildingComponentsView.axaml index 4f6557d3..fec40781 100644 --- a/Gui/Views/BuildingComponentsView.axaml +++ b/Gui/Views/BuildingComponentsView.axaml @@ -10,19 +10,18 @@ xmlns:mo="using:Gui.Models" xmlns:vm="using:Gui.ViewModels" mc:Ignorable="d" - x:Class="Gui.Views.BuildingComponentsView" - x:DataType="vm:LocoTypes.BuildingComponentsViewModel"> + x:DataType="vm:LocoTypes.BuildingComponentsViewModel" + x:Name="Root"> Vertical sprite spacing - + - @@ -43,34 +42,29 @@ - - - - - - - - - - - - - Layer + + + + + + + + + + + + - - - - - - - - - - + + + + + + diff --git a/Gui/Views/FolderTreeView.axaml b/Gui/Views/FolderTreeView.axaml index 6acaf47a..5e0fc8d6 100644 --- a/Gui/Views/FolderTreeView.axaml +++ b/Gui/Views/FolderTreeView.axaml @@ -5,7 +5,7 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:vm="using:Gui.ViewModels" xmlns:mo="using:Gui.Models" - xmlns:moc="using:Gui.Models.Converters" + xmlns:cnv="using:Gui.Converters" xmlns:materialIcons="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia" mc:Ignorable="d" d:DesignWidth="384" d:DesignHeight="768" x:Class="Gui.Views.FolderTreeView" @@ -16,8 +16,8 @@ - - + + diff --git a/Gui/Views/ImageTableView.axaml b/Gui/Views/ImageTableView.axaml index 9b1ec993..e476e430 100644 --- a/Gui/Views/ImageTableView.axaml +++ b/Gui/Views/ImageTableView.axaml @@ -8,7 +8,7 @@ xmlns:amxc="using:Avalonia.Markup.Xaml.Converters" xmlns:paz="using:Avalonia.Controls.PanAndZoom" xmlns:vi="using:Gui.Views" - xmlns:gmc="using:Gui.Models.Converters" + xmlns:cnv="using:Gui.Converters" xmlns:vmg="using:Gui.ViewModels.Graphics" xmlns:vmlt="using:Gui.ViewModels.LocoTypes" mc:Ignorable="d" @@ -19,8 +19,8 @@ - - + + @@ -82,7 +82,7 @@ - + @@ -114,7 +114,7 @@ - + diff --git a/Gui/Views/MainWindow.axaml b/Gui/Views/MainWindow.axaml index 11e62145..9cfb70ab 100644 --- a/Gui/Views/MainWindow.axaml +++ b/Gui/Views/MainWindow.axaml @@ -1,4 +1,3 @@ - - - - + + + @@ -80,7 +79,7 @@ - + diff --git a/Gui/Views/SCV5View.axaml b/Gui/Views/SCV5View.axaml index 4ecb88a9..b1212c6e 100644 --- a/Gui/Views/SCV5View.axaml +++ b/Gui/Views/SCV5View.axaml @@ -4,7 +4,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:vm="using:Gui.ViewModels" - xmlns:moc="using:Gui.Models.Converters" + xmlns:cnv="using:Gui.Converters" xmlns:materialIcons="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia" xmlns:pgc="clr-namespace:Avalonia.PropertyGrid.Controls;assembly=Avalonia.PropertyGrid" xmlns:gui="using:Gui" @@ -13,9 +13,9 @@ x:DataType="vm:SCV5ViewModel"> - - - + + + From cf7ad7591a10a7da558c63d6c75f14edbe05ab50 Mon Sep 17 00:00:00 2001 From: Benjamin Sutas Date: Sat, 6 Sep 2025 15:52:42 +1000 Subject: [PATCH 11/21] fix bad merge --- Dat/FileParsing/LocoBinaryReader.cs | 2 -- Dat/FileParsing/LocoBinaryWriter.cs | 1 - .../ObjectModels/Objects/Building/BuildingObject.cs | 2 -- .../ObjectModels/Objects/Building/CardinalDirection.cs | 9 +++++++++ Gui/ViewModels/LocoTypes/BuildingComponentsViewModel.cs | 5 +---- .../LocoTypes/Objects/TrackStationViewModel.cs | 4 ++-- 6 files changed, 12 insertions(+), 11 deletions(-) create mode 100644 Definitions/ObjectModels/Objects/Building/CardinalDirection.cs diff --git a/Dat/FileParsing/LocoBinaryReader.cs b/Dat/FileParsing/LocoBinaryReader.cs index d74659b5..588eebd9 100644 --- a/Dat/FileParsing/LocoBinaryReader.cs +++ b/Dat/FileParsing/LocoBinaryReader.cs @@ -1,7 +1,6 @@ using Dat.Converters; using Dat.Types; using Definitions.ObjectModels.Objects.Common; -using Definitions.ObjectModels.Objects.Building; using Definitions.ObjectModels.Objects.Shared; using Definitions.ObjectModels.Objects.Sound; using Definitions.ObjectModels.Objects.Vehicle; @@ -332,7 +331,6 @@ public GearboxMotorSound ReadGearboxMotorSound() SpeedFrequencyFactor = ReadByte(), }; - public CargoOffset[][][] ReadCargoOffsets() { const int rotationSize = 4; diff --git a/Dat/FileParsing/LocoBinaryWriter.cs b/Dat/FileParsing/LocoBinaryWriter.cs index 6fe7e2da..2b95fae4 100644 --- a/Dat/FileParsing/LocoBinaryWriter.cs +++ b/Dat/FileParsing/LocoBinaryWriter.cs @@ -1,7 +1,6 @@ using Common; using Dat.Converters; using Dat.Types; -using Definitions.ObjectModels.Objects.Building; using Definitions.ObjectModels.Objects.Shared; using Definitions.ObjectModels.Objects.Common; using Definitions.ObjectModels.Objects.Sound; diff --git a/Definitions/ObjectModels/Objects/Building/BuildingObject.cs b/Definitions/ObjectModels/Objects/Building/BuildingObject.cs index 151fb814..e2c3c838 100644 --- a/Definitions/ObjectModels/Objects/Building/BuildingObject.cs +++ b/Definitions/ObjectModels/Objects/Building/BuildingObject.cs @@ -43,6 +43,4 @@ public bool TryGetImageName(int id, out string? value) value = $"{direction} | Level {level}"; return true; } - - && BuildingComponents.Validate(); } diff --git a/Definitions/ObjectModels/Objects/Building/CardinalDirection.cs b/Definitions/ObjectModels/Objects/Building/CardinalDirection.cs new file mode 100644 index 00000000..d8fd9777 --- /dev/null +++ b/Definitions/ObjectModels/Objects/Building/CardinalDirection.cs @@ -0,0 +1,9 @@ +namespace Definitions.ObjectModels.Objects.Building; + +public enum CardinalDirection : uint8_t +{ + South, + West, + North, + East, +} diff --git a/Gui/ViewModels/LocoTypes/BuildingComponentsViewModel.cs b/Gui/ViewModels/LocoTypes/BuildingComponentsViewModel.cs index 6b6039aa..addad241 100644 --- a/Gui/ViewModels/LocoTypes/BuildingComponentsViewModel.cs +++ b/Gui/ViewModels/LocoTypes/BuildingComponentsViewModel.cs @@ -1,14 +1,11 @@ using Avalonia.Media.Imaging; using Definitions.ObjectModels; +using Definitions.ObjectModels.Objects.Building; using Definitions.ObjectModels.Objects.Common; -using Definitions.ObjectModels.Types; -using DynamicData.Binding; using Gui.Models; -using Gui.ViewModels.Graphics; using ReactiveUI; using ReactiveUI.Fody.Helpers; using System; -using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Reactive.Linq; diff --git a/Gui/ViewModels/LocoTypes/Objects/TrackStationViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/TrackStationViewModel.cs index 96e62e77..69b29b1c 100644 --- a/Gui/ViewModels/LocoTypes/Objects/TrackStationViewModel.cs +++ b/Gui/ViewModels/LocoTypes/Objects/TrackStationViewModel.cs @@ -41,7 +41,7 @@ public TrackStationViewModel(TrackStationObject tso) var_0B = tso.var_0B; var_0D = tso.var_0D; var_6E = tso.var_6E; - //CargoOffsets = tso.CargoOffsets; + CargoOffsets = tso.CargoOffsets; CompatibleTrackObjects = [.. tso.CompatibleTrackObjects.ConvertAll(x => new ObjectModelHeaderViewModel(x))]; } @@ -62,7 +62,7 @@ public override TrackStationObject GetAsModel() var_0B = var_0B, var_0D = var_0D, var_6E = var_6E, - //CargoOffsets = CargoOffsets, + CargoOffsets = CargoOffsets, CompatibleTrackObjects = CompatibleTrackObjects.ConvertAll(x => x.GetAsModel()), }; } From 5a2baa399b293f188a011c01957772418593270a Mon Sep 17 00:00:00 2001 From: Benjamin Sutas Date: Fri, 12 Sep 2025 14:43:45 +1000 Subject: [PATCH 12/21] progress --- .github/copilot-instructions.md | 46 ++++++ Definitions/ObjectModels/ImageTable.cs | 22 +++ .../Objects/Common/BuildingComponentsModel.cs | 3 - .../ObjectModels/Types/GraphicsElement.cs | 5 + Gui/App.axaml | 2 +- Gui/Gui.csproj | 1 + .../Graphics/ImageTableViewModel.cs | 13 +- Gui/ViewModels/Graphics/ImageViewModel.cs | 7 +- .../LocoTypes/BuildingComponentsViewModel.cs | 147 ----------------- Gui/ViewModels/LocoTypes/G1ViewModel.cs | 3 +- .../LocoTypes/ObjectEditorViewModel.cs | 16 +- .../Building/BuildingComponentsViewModel.cs | 149 ++++++++++++++++++ .../Building/BuildingLayerViewModel.cs | 40 +++++ .../Building/BuildingStackViewModel.cs | 14 ++ .../Building/BuildingVariationViewModel.cs | 13 ++ .../{ => Building}/BuildingViewModel.cs | 2 +- .../DesignBuildingComponentsViewModel.cs | 86 ++++++++++ Gui/Views/BuildingComponentsView.axaml | 114 +++++++------- ObjectEditor.sln | 1 + 19 files changed, 454 insertions(+), 230 deletions(-) create mode 100644 .github/copilot-instructions.md delete mode 100644 Gui/ViewModels/LocoTypes/BuildingComponentsViewModel.cs create mode 100644 Gui/ViewModels/LocoTypes/Objects/Building/BuildingComponentsViewModel.cs create mode 100644 Gui/ViewModels/LocoTypes/Objects/Building/BuildingLayerViewModel.cs create mode 100644 Gui/ViewModels/LocoTypes/Objects/Building/BuildingStackViewModel.cs create mode 100644 Gui/ViewModels/LocoTypes/Objects/Building/BuildingVariationViewModel.cs rename Gui/ViewModels/LocoTypes/Objects/{ => Building}/BuildingViewModel.cs (99%) create mode 100644 Gui/ViewModels/LocoTypes/Objects/Building/DesignBuildingComponentsViewModel.cs diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 00000000..ad53b8fb --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,46 @@ +# Project Overview + +This project consists of two main applications: +- A GUI application called "ObjectEditor", located in `/Gui` folder +- A web server called "ObjectService", located in `/ObjectService` folder + +The ObjectEditor is used to edit `.dat` objects from the game "Chris Sawyer's Locomotion". It is built using C# and the latest .NET framework (currently version 10 preview). + +The ObjectService is used to host a SQLite database and a folder of all known custom objects. The ObjectService can be interacted with by clients using a simple REST api, and the primary client is the ObjectEditor. + +## Folder Structure + +- `/Gui`: Contains the source code for the AvaloniaUI-based application called "ObjectEditor". +- `/Definitions`: Countains all data types shared throughout the projects in this solution. +- `/Dat`: Contains the source code for reading, saving, decoding and encoding dat-format objects. +- `/Common`: Contains source code for any generic and shared code. +- `/Index`: Contains source code for creating an 'Object Index File' which is a json file storing a list of all parsed .dat objects for a specific user-selected folder. It is used by the ObjectEditor and ObjectService to manage/reference the on-disk .dat objects. +- `/Tests`: Contains the source code for all unit tests for all projects. +- The other folders contain small sub-projects that have specific purposes, but are not necessary or used in the build of the main ObjectEditor and ObjectService applications. + +## Libraries and Frameworks + +- C# +- .NET 10 +- ASP.NET Core +- SQLite +- System.Text.Json +- Avalonia UI + +## Coding Standards + +- Use the coding rules defined in `.editorconfig` - that's what that file is for. +- If in doubt, use the existing coding conventions in the code - do not use arbitrary conventions from other projects. +- Do not rewrite code to older styles, eg do not convert the collection initializer `[]` into `new()`. Use the modern, existing syntax. + +## ObjectEditor UI guidelines + +- Use AXAML/XAML for all UI layouers, and always use XAML-first (not code-behind) solutions, unless strictly not possible, in which case warn the user before making any code change. +- Separate views and viewmodels as necessary, and as evidenced by the existing `/Gui/Views`, `/Gui/ViewModels` and `/Gui/Models` folders. Note that many 'models' are defined in the `/Definitions` project as well. +- The application should have a modern and clean design utilising FluentUI principles and library. +- It should try to use MVVM style UI design, and where this design doesn't exist in the code, inform the user how to rewrite the code to better adhere to the standard MVVM/Avalonia UI style. +- The UI code uses ReactiveUI, so any UI suggestions must use that as well. We do not use INotifyPropertyChanged, for example, since ReactiveUI handles that automatically. + +## AI Agent Behavior + +Generate Code, Not Boilerplate: You don't need to generate entire classes from scratch unless explicitly asked. The goal is to provide helpful code snippets, method implementations, or test cases. You don't need to excessively add comments to code explaining every line; only add comments when you need to explain "why" the code does something, not "what" it does. diff --git a/Definitions/ObjectModels/ImageTable.cs b/Definitions/ObjectModels/ImageTable.cs index ee86e38e..44d2b9fa 100644 --- a/Definitions/ObjectModels/ImageTable.cs +++ b/Definitions/ObjectModels/ImageTable.cs @@ -4,6 +4,28 @@ namespace Definitions.ObjectModels; public class ImageTable : IHasGraphicsElements { + public PaletteMap PaletteMap + { + get; + set + { + ArgumentNullException.ThrowIfNull(value); + field = value; + + foreach (var g in Groups) + { + foreach (var ge in g.GraphicsElements) + { + if (!field.TryConvertG1ToRgba32Bitmap(ge, ColourRemapSwatch.PrimaryRemap, ColourRemapSwatch.SecondaryRemap, out var image)) + { + throw new Exception("Failed to convert image"); + } + ge.Image = image; + } + } + } + } + // public/old interface public List GraphicsElements { diff --git a/Definitions/ObjectModels/Objects/Common/BuildingComponentsModel.cs b/Definitions/ObjectModels/Objects/Common/BuildingComponentsModel.cs index 739f1027..35df97dc 100644 --- a/Definitions/ObjectModels/Objects/Common/BuildingComponentsModel.cs +++ b/Definitions/ObjectModels/Objects/Common/BuildingComponentsModel.cs @@ -1,5 +1,3 @@ -using System.ComponentModel; - namespace Definitions.ObjectModels.Objects.Common; public interface IHasBuildingComponents @@ -7,7 +5,6 @@ public interface IHasBuildingComponents BuildingComponentsModel BuildingComponents { get; set; } } -[TypeConverter(typeof(ExpandableObjectConverter))] public class BuildingComponentsModel { public List BuildingHeights { get; set; } = []; diff --git a/Definitions/ObjectModels/Types/GraphicsElement.cs b/Definitions/ObjectModels/Types/GraphicsElement.cs index 2a308310..db000928 100644 --- a/Definitions/ObjectModels/Types/GraphicsElement.cs +++ b/Definitions/ObjectModels/Types/GraphicsElement.cs @@ -1,3 +1,6 @@ +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.PixelFormats; + namespace Definitions.ObjectModels.Types; [Flags] @@ -23,5 +26,7 @@ public class GraphicsElement // follows G1Element32, except XOffset and YOffset public short ZoomOffset { get; set; } public byte[] ImageData { get; set; } = []; + public Image? Image { get; set; } + public string Name { get; set; } // taken from IImageNameProvider } diff --git a/Gui/App.axaml b/Gui/App.axaml index fa603be7..31d60f2a 100644 --- a/Gui/App.axaml +++ b/Gui/App.axaml @@ -56,7 +56,7 @@ - + diff --git a/Gui/Gui.csproj b/Gui/Gui.csproj index 6e622b54..69b63b17 100644 --- a/Gui/Gui.csproj +++ b/Gui/Gui.csproj @@ -62,6 +62,7 @@ + diff --git a/Gui/ViewModels/Graphics/ImageTableViewModel.cs b/Gui/ViewModels/Graphics/ImageTableViewModel.cs index 63eee6d6..73a8783e 100644 --- a/Gui/ViewModels/Graphics/ImageTableViewModel.cs +++ b/Gui/ViewModels/Graphics/ImageTableViewModel.cs @@ -6,7 +6,6 @@ using Definitions.ObjectModels; using Definitions.ObjectModels.Objects.Common; using Definitions.ObjectModels.Types; -using Gui.ViewModels.LocoTypes; using ReactiveUI; using ReactiveUI.Fody.Helpers; using SixLabors.ImageSharp; @@ -21,6 +20,7 @@ using System.Windows.Input; using SLColour = SixLabors.ImageSharp.Color; using AvaColour = Avalonia.Media.Color; +using Gui.ViewModels.LocoTypes.Objects.Building; namespace Gui.ViewModels.Graphics; @@ -102,19 +102,18 @@ public class ImageTableViewModel : ReactiveObject, IExtraContentViewModel public BuildingComponentsViewModel? BuildingComponents { get; set; } - public ImageTableViewModel(ImageTable imageTable, PaletteMap paletteMap, ILogger logger, BuildingComponentsModel buildingComponents = null) + public ImageTableViewModel(ImageTable imageTable, ILogger logger, BuildingComponentsModel buildingComponents = null) { ArgumentNullException.ThrowIfNull(imageTable); - ArgumentNullException.ThrowIfNull(paletteMap); - PaletteMap = paletteMap; + PaletteMap = imageTable.PaletteMap; Logger = logger; // swatches/palettes ColourSwatches = [.. ColourSwatchesArr.Select(x => new ColourRemapSwatchViewModel() { Swatch = x, - Colour = paletteMap.GetRemapSwatchFromName(x)[0].Color.ToAvaloniaColor(), - GradientColours = [.. paletteMap.GetRemapSwatchFromName(x).Select(x => x.Color.ToAvaloniaColor())], + Colour = PaletteMap.GetRemapSwatchFromName(x)[0].Color.ToAvaloniaColor(), + GradientColours = [.. PaletteMap.GetRemapSwatchFromName(x).Select(x => x.Color.ToAvaloniaColor())], })]; SelectedPrimarySwatch = ColourSwatches.Single(x => x.Swatch == ColourRemapSwatch.PrimaryRemap); @@ -130,7 +129,7 @@ public ImageTableViewModel(ImageTable imageTable, PaletteMap paletteMap, ILogger } // building components - BuildingComponents = new(buildingComponents, imageTable, PaletteMap); + BuildingComponents = new(buildingComponents, imageTable); _ = this.WhenAnyValue(o => o.SelectedPrimarySwatch).Skip(1) .Subscribe(_ => RecolourImages(SelectedPrimarySwatch.Swatch, SelectedSecondarySwatch.Swatch)); diff --git a/Gui/ViewModels/Graphics/ImageViewModel.cs b/Gui/ViewModels/Graphics/ImageViewModel.cs index 5ce2e8b3..b8c90b50 100644 --- a/Gui/ViewModels/Graphics/ImageViewModel.cs +++ b/Gui/ViewModels/Graphics/ImageViewModel.cs @@ -59,12 +59,7 @@ public ImageViewModel(GraphicsElement graphicsElement, PaletteMap paletteMap) _ = this.WhenAnyValue(o => o.DisplayedImage) .Subscribe(_ => this.RaisePropertyChanged(nameof(SelectedBitmapPreviewBorder))); - if (!PaletteMap.TryConvertG1ToRgba32Bitmap(graphicsElement, ColourRemapSwatch.PrimaryRemap, ColourRemapSwatch.SecondaryRemap, out var image)) - { - throw new Exception("Failed to convert image"); - } - - UnderlyingImage = image!; + UnderlyingImage = graphicsElement.Image!; } public void RecolourImage(ColourRemapSwatch primary, ColourRemapSwatch secondary) diff --git a/Gui/ViewModels/LocoTypes/BuildingComponentsViewModel.cs b/Gui/ViewModels/LocoTypes/BuildingComponentsViewModel.cs deleted file mode 100644 index addad241..00000000 --- a/Gui/ViewModels/LocoTypes/BuildingComponentsViewModel.cs +++ /dev/null @@ -1,147 +0,0 @@ -using Avalonia.Media.Imaging; -using Definitions.ObjectModels; -using Definitions.ObjectModels.Objects.Building; -using Definitions.ObjectModels.Objects.Common; -using Gui.Models; -using ReactiveUI; -using ReactiveUI.Fody.Helpers; -using System; -using System.Collections.ObjectModel; -using System.Linq; -using System.Reactive.Linq; - -namespace Gui.ViewModels.LocoTypes; - -public class BuildingLayerViewModel : ReactiveObject -{ - [Reactive] public Bitmap DisplayedImage { get; set; } - - public double Width => DisplayedImage.Size.Width; - public double Height => DisplayedImage.Size.Height; - - public double X => XBase + XOffset; - public double Y => YBase + YOffset; - - [Reactive] public double XBase { get; set; } - [Reactive] public double YBase { get; set; } - - [Reactive] public double XOffset { get; set; } - [Reactive] public double YOffset { get; set; } - - public BuildingLayerViewModel() - { - _ = this.WhenAnyValue(o => o.XBase) - .Subscribe(_ => this.RaisePropertyChanged(nameof(X))); - _ = this.WhenAnyValue(o => o.XOffset) - .Subscribe(_ => this.RaisePropertyChanged(nameof(X))); - - _ = this.WhenAnyValue(o => o.YBase) - .Subscribe(_ => this.RaisePropertyChanged(nameof(Y))); - _ = this.WhenAnyValue(o => o.YOffset) - .Subscribe(_ => this.RaisePropertyChanged(nameof(Y))); - } -} - -public class BuildingStackViewModel : ReactiveObject -{ - public CardinalDirection Direction { get; init; } - public ObservableCollection Layers { get; set; } = []; -} - -public class BuildingVariationViewModel : ReactiveObject -{ - public ObservableCollection Directions { get; set; } = []; -} - -public class BuildingComponentsViewModel : ReactiveObject -{ - public BuildingComponentsModel BuildingComponents { get; } - - public ObservableCollection BuildingVariations { get; set; } = []; - - [Reactive] public int OffsetSpacing { get; set; } - - [Reactive] public int MaxWidth { get; set; } - [Reactive] public int MaxHeight { get; set; } - - public BuildingComponentsViewModel(BuildingComponentsModel buildingComponents, ImageTable imageTable, PaletteMap paletteMap) - { - BuildingComponents = buildingComponents; - - var layers = imageTable.Groups.ConvertAll(x => x.GraphicsElements); // each building part has 4 directions - - MaxWidth = layers.Max(x => x.Max(y => y.Width)) + 16; - MaxHeight = (layers.Max(x => x.Max(y => y.Height)) * buildingComponents.BuildingHeights.Count) + buildingComponents.BuildingHeights.Sum(x => x) + (buildingComponents.BuildingVariations.Max(x => x.Count) * OffsetSpacing); - - foreach (var variation in buildingComponents.BuildingVariations) - { - var bv = new BuildingVariationViewModel(); - - const int numDirections = 4; - for (var i = 0; i < numDirections; ++i) - { - var bs = new BuildingStackViewModel() - { - Direction = (CardinalDirection)i - }; - - var cumulativeOffset = 0; - foreach (var variationItem in variation) - { - var layer = layers[variationItem]; - var bl = new BuildingLayerViewModel(); - - if (!paletteMap.TryConvertG1ToRgba32Bitmap(layer[i], ColourRemapSwatch.PrimaryRemap, ColourRemapSwatch.SecondaryRemap, out var image)) - { - throw new Exception("Failed to convert image"); - } - - bl.DisplayedImage = image.ToAvaloniaBitmap(); - bl.XBase = layer[i].XOffset + MaxWidth / 2; - bl.YBase = layer[i].YOffset - cumulativeOffset + (MaxHeight * 0.80); - cumulativeOffset += buildingComponents.BuildingHeights[variationItem]; - - bl.XOffset = 0; - bl.YOffset = 0; - - bs.Layers.Add(bl); - } - - bv.Directions.Add(bs); // [i] = bs; - } - - BuildingVariations.Add(bv); - } - - // Update all nested layers' YOffset whenever OffsetSpacing changes - _ = this.WhenAnyValue(x => x.OffsetSpacing) - .Subscribe(ApplyOffsetToAllLayers); - } - - private void ApplyOffsetToAllLayers(int offset) - { - foreach (var variation in BuildingVariations) - { - if (variation?.Directions == null) - { - continue; - } - - foreach (var dir in variation.Directions) - { - if (dir?.Layers == null) - { - continue; - } - - var cumulativeOffset = 0; - foreach (var layer in dir.Layers) - { - layer.YOffset = cumulativeOffset; - cumulativeOffset -= OffsetSpacing; - } - } - } - } -} - diff --git a/Gui/ViewModels/LocoTypes/G1ViewModel.cs b/Gui/ViewModels/LocoTypes/G1ViewModel.cs index 6be42f90..43fc3520 100644 --- a/Gui/ViewModels/LocoTypes/G1ViewModel.cs +++ b/Gui/ViewModels/LocoTypes/G1ViewModel.cs @@ -26,7 +26,8 @@ public override void Load() return; } - ImageTableViewModel = new ImageTableViewModel(Model.G1.ImageTable, Model.PaletteMap, logger); + Model.G1.ImageTable.PaletteMap = Model.PaletteMap; + ImageTableViewModel = new ImageTableViewModel(Model.G1.ImageTable, logger); } public override void Save() diff --git a/Gui/ViewModels/LocoTypes/ObjectEditorViewModel.cs b/Gui/ViewModels/LocoTypes/ObjectEditorViewModel.cs index 693c64e5..817dc0d2 100644 --- a/Gui/ViewModels/LocoTypes/ObjectEditorViewModel.cs +++ b/Gui/ViewModels/LocoTypes/ObjectEditorViewModel.cs @@ -154,13 +154,15 @@ public override void Load() CurrentObjectViewModel = GetViewModelFromStruct(CurrentObject.LocoObject.Object); StringTableViewModel = new(CurrentObject.LocoObject.StringTable); - //var imageNameProvider = (CurrentObject.LocoObject.Object is IImageTableNameProvider itnp) - // ? itnp - // : new DefaultImageTableNameProvider(); - - ExtraContentViewModel = CurrentObject.LocoObject.Object is SoundObject soundObject - ? new AudioViewModel(logger, CurrentObject.DatFileInfo.S5Header.Name, soundObject.SoundObjectData.PcmHeader, soundObject.PcmData) - : new ImageTableViewModel(CurrentObject.LocoObject.ImageTable, Model.PaletteMap, Model.Logger, (CurrentObject.LocoObject.Object as IHasBuildingComponents)?.BuildingComponents); + if (CurrentObject.LocoObject.Object is SoundObject soundObject) + { + ExtraContentViewModel = new AudioViewModel(logger, CurrentObject.DatFileInfo.S5Header.Name, soundObject.SoundObjectData.PcmHeader, soundObject.PcmData); + } + else + { + CurrentObject.LocoObject.ImageTable?.PaletteMap = Model.PaletteMap; + ExtraContentViewModel = new ImageTableViewModel(CurrentObject.LocoObject.ImageTable, Model.Logger, (CurrentObject.LocoObject.Object as IHasBuildingComponents)?.BuildingComponents); + } } else { diff --git a/Gui/ViewModels/LocoTypes/Objects/Building/BuildingComponentsViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/Building/BuildingComponentsViewModel.cs new file mode 100644 index 00000000..1d9aa771 --- /dev/null +++ b/Gui/ViewModels/LocoTypes/Objects/Building/BuildingComponentsViewModel.cs @@ -0,0 +1,149 @@ +using Definitions.ObjectModels; +using Definitions.ObjectModels.Objects.Building; +using Definitions.ObjectModels.Objects.Common; +using Gui.Models; +using PropertyModels.ComponentModel; +using ReactiveUI; +using ReactiveUI.Fody.Helpers; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Linq; +using System.Reactive.Linq; +using ReactiveObject = ReactiveUI.ReactiveObject; + +namespace Gui.ViewModels.LocoTypes.Objects.Building; + +[TypeConverter(typeof(ExpandableObjectConverter))] +public class BuildingComponentsViewModel : ReactiveObject +{ + [Reactive] + [Trackable(0, 64)] + public int VerticalLayerSpacing { get; set; } + + [Reactive] public int MaxWidth { get; set; } + [Reactive] public int MaxHeight { get; set; } + + //[Reactive] + //public BuildingComponentsModel BuildingComponentsModel { get; set; } + + [Reactive, Browsable(false)] + public ObservableCollection BuildingHeights { get; set; } = []; + + [Reactive, Browsable(false)] + public ObservableCollection BuildingAnimations { get; set; } = []; + + //[Reactive] + //public List> BuildingVariations { get; set; } = []; + + //[Browsable(false)] + [Reactive] + public ObservableCollection BuildingVariationViewModels { get; set; } = []; + + protected ImageTable ImageTable { get; set; } + + public BuildingComponentsViewModel() + { + //_ = this.WhenAnyValue(x => x.BuildingVariations) + // .Subscribe(_ => this.RaisePropertyChanged(nameof(BuildingVariationViewModels))); + + _ = this.WhenAnyValue(x => x.VerticalLayerSpacing) + .Subscribe(ApplyOffsetToAllLayers); + } + + public BuildingComponentsViewModel(BuildingComponentsModel buildingComponents, ImageTable imageTable) : this() + { + ArgumentNullException.ThrowIfNull(buildingComponents); + ArgumentNullException.ThrowIfNull(imageTable); + + //_ = this.WhenAnyValue(x => x.BuildingVariationViewModels) + // .Where(x => x != null && ImageTable != null) + // .Subscribe(_ => RecomputeBuildingVariationViewModels(buildingComponents.BuildingVariations)); + + ImageTable = imageTable; + BuildingHeights = new ObservableCollection(buildingComponents.BuildingHeights); + BuildingAnimations = new ObservableCollection(buildingComponents.BuildingAnimations); + + RecomputeBuildingVariationViewModels(buildingComponents.BuildingVariations); + + //BuildingVariations = buildingComponents.BuildingVariations; + //BuildingComponentsModel = buildingComponents; + } + + protected void RecomputeBuildingVariationViewModels(List> buildingVariations) + { + var layers = ImageTable.Groups.ConvertAll(x => x.GraphicsElements); + + BuildingVariationViewModels.Clear(); + + MaxWidth = layers.Max(x => x.Max(y => y.Width)) + 16; + MaxHeight = (layers.Max(x => x.Max(y => y.Height)) * BuildingHeights.Count) + BuildingHeights.Sum(x => x) + buildingVariations.Max(x => x.Count) * VerticalLayerSpacing; + + var x = 0; + foreach (var variation in buildingVariations) + { + var bv = new BuildingVariationViewModel() + { + VariationName = $"Variation {x++}", + }; + + const int numDirections = 4; + for (var i = 0; i < numDirections; ++i) + { + var bs = new BuildingStackViewModel() + { + Direction = (CardinalDirection)i + }; + + var cumulativeOffset = 0; + foreach (var variationItem in variation) + { + var layer = layers[variationItem]; + var bl = new BuildingLayerViewModel + { + XBase = layer[i].XOffset, // + (MaxWidth / 2), + YBase = layer[i].YOffset - cumulativeOffset + MaxHeight * 0.80, + DisplayedImage = layer[i].Image.ToAvaloniaBitmap(), + XOffset = 0, + YOffset = 0, + }; + + cumulativeOffset += BuildingHeights[variationItem]; + bs.Layers.Add(bl); + } + + bv.Directions.Add(bs); // [i] = bs; + } + + BuildingVariationViewModels.Add(bv); + } + } + + private void ApplyOffsetToAllLayers(int offset) + { + foreach (var variation in BuildingVariationViewModels) + { + if (variation?.Directions == null) + { + continue; + } + + foreach (var dir in variation.Directions) + { + if (dir?.Layers == null) + { + continue; + } + + var cumulativeOffset = 0; + foreach (var layer in dir.Layers) + { + layer.YOffset = cumulativeOffset; + cumulativeOffset -= VerticalLayerSpacing; + } + } + } + } +} + diff --git a/Gui/ViewModels/LocoTypes/Objects/Building/BuildingLayerViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/Building/BuildingLayerViewModel.cs new file mode 100644 index 00000000..6266d0ae --- /dev/null +++ b/Gui/ViewModels/LocoTypes/Objects/Building/BuildingLayerViewModel.cs @@ -0,0 +1,40 @@ +using Avalonia.Media.Imaging; +using ReactiveUI; +using ReactiveUI.Fody.Helpers; +using System; +using System.ComponentModel; +using System.Reactive.Linq; + +namespace Gui.ViewModels.LocoTypes.Objects.Building; + +[TypeConverter(typeof(ExpandableObjectConverter))] +public class BuildingLayerViewModel : ReactiveObject +{ + [Reactive] public Bitmap DisplayedImage { get; set; } + + public double Width => DisplayedImage.Size.Width; + public double Height => DisplayedImage.Size.Height; + + public double X => XBase + XOffset; + public double Y => YBase + YOffset; + + [Reactive] public double XBase { get; set; } + [Reactive] public double YBase { get; set; } + + [Reactive] public double XOffset { get; set; } + [Reactive] public double YOffset { get; set; } + + public BuildingLayerViewModel() + { + _ = this.WhenAnyValue(o => o.XBase) + .Subscribe(_ => this.RaisePropertyChanged(nameof(X))); + _ = this.WhenAnyValue(o => o.XOffset) + .Subscribe(_ => this.RaisePropertyChanged(nameof(X))); + + _ = this.WhenAnyValue(o => o.YBase) + .Subscribe(_ => this.RaisePropertyChanged(nameof(Y))); + _ = this.WhenAnyValue(o => o.YOffset) + .Subscribe(_ => this.RaisePropertyChanged(nameof(Y))); + } +} + diff --git a/Gui/ViewModels/LocoTypes/Objects/Building/BuildingStackViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/Building/BuildingStackViewModel.cs new file mode 100644 index 00000000..13d8b6dd --- /dev/null +++ b/Gui/ViewModels/LocoTypes/Objects/Building/BuildingStackViewModel.cs @@ -0,0 +1,14 @@ +using Definitions.ObjectModels.Objects.Building; +using ReactiveUI; +using System.Collections.ObjectModel; +using System.ComponentModel; + +namespace Gui.ViewModels.LocoTypes.Objects.Building; + +[TypeConverter(typeof(ExpandableObjectConverter))] +public class BuildingStackViewModel : ReactiveObject +{ + public CardinalDirection Direction { get; init; } + public ObservableCollection Layers { get; set; } = []; +} + diff --git a/Gui/ViewModels/LocoTypes/Objects/Building/BuildingVariationViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/Building/BuildingVariationViewModel.cs new file mode 100644 index 00000000..2a43dc56 --- /dev/null +++ b/Gui/ViewModels/LocoTypes/Objects/Building/BuildingVariationViewModel.cs @@ -0,0 +1,13 @@ +using ReactiveUI; +using System.Collections.ObjectModel; +using System.ComponentModel; + +namespace Gui.ViewModels.LocoTypes.Objects.Building; + +[TypeConverter(typeof(ExpandableObjectConverter))] +public class BuildingVariationViewModel : ReactiveObject +{ + public string VariationName { get; init; } + public ObservableCollection Directions { get; set; } = []; +} + diff --git a/Gui/ViewModels/LocoTypes/Objects/BuildingViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/Building/BuildingViewModel.cs similarity index 99% rename from Gui/ViewModels/LocoTypes/Objects/BuildingViewModel.cs rename to Gui/ViewModels/LocoTypes/Objects/Building/BuildingViewModel.cs index 39f91e51..1043dcfd 100644 --- a/Gui/ViewModels/LocoTypes/Objects/BuildingViewModel.cs +++ b/Gui/ViewModels/LocoTypes/Objects/Building/BuildingViewModel.cs @@ -8,7 +8,7 @@ using System.ComponentModel.DataAnnotations; using System.Linq; -namespace Gui.ViewModels; +namespace Gui.ViewModels.LocoTypes.Objects.Building; public class BuildingViewModel : LocoObjectViewModel { diff --git a/Gui/ViewModels/LocoTypes/Objects/Building/DesignBuildingComponentsViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/Building/DesignBuildingComponentsViewModel.cs new file mode 100644 index 00000000..7837f26a --- /dev/null +++ b/Gui/ViewModels/LocoTypes/Objects/Building/DesignBuildingComponentsViewModel.cs @@ -0,0 +1,86 @@ +using Definitions.ObjectModels; +using Definitions.ObjectModels.Objects.Common; +using Definitions.ObjectModels.Types; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Drawing.Processing; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; + +using System.Collections.Generic; + +namespace Gui.ViewModels.LocoTypes.Objects.Building; + +public class DesignBuildingComponentsViewModel : BuildingComponentsViewModel +{ + public Image CreateDummyImage(int width, int height) + { + var image = new Image(width, height); + + // Fill the entire image with a background color (e.g., white) + image.Mutate(ctx => + { + ctx.BackgroundColor(Color.White); + + // Draw a red rectangle + var border = 1; + ctx.Fill(Color.Red, new RectangleF(border, border, width - (2 * border), height - (2 * border))); + + // Draw a blue circle + //ctx.Fill(Color.Blue, new EllipsePolygon(400, 300, 100)); + + // Draw text + // You'll need to load a font for this + // Example: Font font = SystemFonts.CreateFont("Arial", 24); + // ctx.DrawText("Hello, ImageSharp!", font, Color.Black, new PointF(50, 450)); + }); + + // Save the image to a file + //image.Save("myGeneratedImage.png"); + + return image; + } + + public DesignBuildingComponentsViewModel() + { + var width = (short)32; + var height = (short)32; + + ImageTable = new ImageTable() + { + Groups = [ + ( + "Layer 0", + [ + new GraphicsElement() { Name = "Layer 0 - South", Width = width, Height = height, Image = CreateDummyImage(width, height) }, + new GraphicsElement() { Name = "Layer 0 - West ", Width = width, Height = height, Image = CreateDummyImage(width, height) }, + new GraphicsElement() { Name = "Layer 0 - North", Width = width, Height = height, Image = CreateDummyImage(width, height) }, + new GraphicsElement() { Name = "Layer 0 - East ", Width = width, Height = height, Image = CreateDummyImage(width, height) }, + ] + ), + ( + "Layer 1", + [ + new GraphicsElement() { Name = "Layer 1 - South", Width = width, Height = height, Image = CreateDummyImage(width, height) }, + new GraphicsElement() { Name = "Layer 1 - West ", Width = width, Height = height, Image = CreateDummyImage(width, height) }, + new GraphicsElement() { Name = "Layer 1 - North", Width = width, Height = height, Image = CreateDummyImage(width, height) }, + new GraphicsElement() { Name = "Layer 1 - East ", Width = width, Height = height, Image = CreateDummyImage(width, height) }, + ] + ) + ] + }; + + BuildingHeights = [16, 16]; + BuildingAnimations = + [ + new BuildingPartAnimation() { AnimationSpeed = 40, NumFrames = 20 }, + new BuildingPartAnimation() { AnimationSpeed = 30, NumFrames = 15 }, + ]; + List> buildingVariations = + [ + [0, 1], + [0, 1, 1], + ]; + + RecomputeBuildingVariationViewModels(buildingVariations); + } +} diff --git a/Gui/Views/BuildingComponentsView.axaml b/Gui/Views/BuildingComponentsView.axaml index fec40781..615a892a 100644 --- a/Gui/Views/BuildingComponentsView.axaml +++ b/Gui/Views/BuildingComponentsView.axaml @@ -3,77 +3,77 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:vmg="using:Gui.ViewModels.Graphics" + xmlns:mo="using:Gui.Models" xmlns:materialIcons="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia" xmlns:pgc="clr-namespace:Avalonia.PropertyGrid.Controls;assembly=Avalonia.PropertyGrid" xmlns:paz="using:Avalonia.Controls.PanAndZoom" - xmlns:mo="using:Gui.Models" - xmlns:vm="using:Gui.ViewModels" mc:Ignorable="d" + xmlns:bvm="using:Gui.ViewModels.LocoTypes.Objects.Building" + x:DataType="bvm:BuildingComponentsViewModel" x:Class="Gui.Views.BuildingComponentsView" - x:DataType="vm:LocoTypes.BuildingComponentsViewModel" x:Name="Root"> - - - - Vertical sprite spacing - - + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - Variation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + diff --git a/ObjectEditor.sln b/ObjectEditor.sln index 827f80bf..66e08b2d 100644 --- a/ObjectEditor.sln +++ b/ObjectEditor.sln @@ -16,6 +16,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution .gitignore = .gitignore build.sh = build.sh build_object_service.sh = build_object_service.sh + .github\copilot-instructions.md = .github\copilot-instructions.md dat-object-layout.md = dat-object-layout.md loco.db.dbml = loco.db.dbml loco_icon.ico = loco_icon.ico From 7d48de2196391b87db5af42ca833d2f8d41024c3 Mon Sep 17 00:00:00 2001 From: Benjamin Sutas Date: Fri, 12 Sep 2025 15:33:57 +1000 Subject: [PATCH 13/21] fix merge --- .../LocoTypes/Objects/AirportViewModel.cs | 19 ++++++++++------ .../LocoTypes/Objects/DockViewModel.cs | 22 +++++++++++-------- .../LocoTypes/Objects/IndustryViewModel.cs | 19 ++++++++-------- 3 files changed, 35 insertions(+), 25 deletions(-) diff --git a/Gui/ViewModels/LocoTypes/Objects/AirportViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/AirportViewModel.cs index 15bd9471..6f18f865 100644 --- a/Gui/ViewModels/LocoTypes/Objects/AirportViewModel.cs +++ b/Gui/ViewModels/LocoTypes/Objects/AirportViewModel.cs @@ -1,9 +1,11 @@ +using Dat.Loaders; using Definitions.ObjectModels.Objects.Airport; -using Definitions.ObjectModels.Objects.Building; +using Definitions.ObjectModels.Objects.Common; using PropertyModels.Extensions; using System.Collections.ObjectModel; using System.ComponentModel; using System.ComponentModel.DataAnnotations; +using System.Linq; namespace Gui.ViewModels; @@ -42,9 +44,9 @@ public AirportViewModel(AirportObject ao) CostIndex = ao.CostIndex; BuildCostFactor = ao.BuildCostFactor; SellCostFactor = ao.SellCostFactor; - BuildingHeights = new(ao.BuildingHeights); - BuildingAnimations = new(ao.BuildingAnimations); - BuildingVariations = new(ao.BuildingVariations.Select(x => new ObservableCollection(x))); + BuildingHeights = new(ao.BuildingComponents.BuildingHeights); + BuildingAnimations = new(ao.BuildingComponents.BuildingAnimations); + BuildingVariations = new(ao.BuildingComponents.BuildingVariations.Select(x => new ObservableCollection(x))); BuildingPositions = new(ao.BuildingPositions); MovementNodes = new(ao.MovementNodes); MovementEdges = new(ao.MovementEdges); @@ -68,9 +70,12 @@ public override AirportObject GetAsModel() CostIndex = CostIndex, BuildCostFactor = BuildCostFactor, SellCostFactor = SellCostFactor, - BuildingHeights = [.. BuildingHeights], - BuildingAnimations = [.. BuildingAnimations], - BuildingVariations = BuildingVariations.ToList().ConvertAll(x => x.ToList()), + BuildingComponents = new BuildingComponentsModel() + { + BuildingHeights = [.. BuildingHeights], + BuildingAnimations = [.. BuildingAnimations], + BuildingVariations = [.. BuildingVariations.Select(x => x.ToList())], + }, BuildingPositions = [.. BuildingPositions], MovementNodes = [.. MovementNodes], MovementEdges = [.. MovementEdges], diff --git a/Gui/ViewModels/LocoTypes/Objects/DockViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/DockViewModel.cs index cdfda0fd..4747037c 100644 --- a/Gui/ViewModels/LocoTypes/Objects/DockViewModel.cs +++ b/Gui/ViewModels/LocoTypes/Objects/DockViewModel.cs @@ -1,9 +1,13 @@ +using Dat.Loaders; +using Definitions.ObjectModels.Objects.Common; using Definitions.ObjectModels.Objects.Dock; using Definitions.ObjectModels.Types; using PropertyModels.ComponentModel.DataAnnotations; using PropertyModels.Extensions; using System.Collections.ObjectModel; using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.Linq; namespace Gui.ViewModels; @@ -31,9 +35,9 @@ public DockViewModel(DockObject @do) SellCostFactor = @do.SellCostFactor; BoatPosition = @do.BoatPosition; var_07 = @do.var_07; - BuildingHeights = new(@do.BuildingHeights); - BuildingAnimations = new(@do.BuildingAnimations); - BuildingVariations = new(@do.BuildingVariations.Select(x => new ObservableCollection(x))); + BuildingHeights = new(@do.BuildingComponents.BuildingHeights); + BuildingAnimations = new(@do.BuildingComponents.BuildingAnimations); + BuildingVariations = new(@do.BuildingComponents.BuildingVariations.Select(x => new ObservableCollection(x))); } public override DockObject GetAsModel() @@ -48,12 +52,12 @@ public override DockObject GetAsModel() SellCostFactor = SellCostFactor, BoatPosition = BoatPosition, var_07 = var_07, - //BuildingComponents = new BuildingComponentsModel() - //{ - // BuildingHeights = [.. BuildingHeights], - // BuildingAnimations = [.. BuildingAnimations], - // BuildingVariations = BuildingVariations.ToList().ConvertAll(x => x.ToList()), - //}, + BuildingComponents = new BuildingComponentsModel() + { + BuildingHeights = [.. BuildingHeights], + BuildingAnimations = [.. BuildingAnimations], + BuildingVariations = [.. BuildingVariations.Select(x => x.ToList())], + }, }; return dockObject; } diff --git a/Gui/ViewModels/LocoTypes/Objects/IndustryViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/IndustryViewModel.cs index a339fd7e..ef16daab 100644 --- a/Gui/ViewModels/LocoTypes/Objects/IndustryViewModel.cs +++ b/Gui/ViewModels/LocoTypes/Objects/IndustryViewModel.cs @@ -1,4 +1,5 @@ using Dat.Loaders; +using Definitions.ObjectModels.Objects.Common; using Definitions.ObjectModels.Objects.Industry; using Definitions.ObjectModels.Types; using PropertyModels.ComponentModel.DataAnnotations; @@ -48,9 +49,9 @@ public class IndustryViewModel : LocoObjectViewModel public IndustryViewModel(IndustryObject io) { AnimationSequences = new(io.AnimationSequences.Select(x => new ObservableCollection(x))); - BuildingAnimations = new(io.BuildingAnimations); - BuildingHeights = new(io.BuildingHeights); - BuildingVariations = new(io.BuildingVariations.Select(x => new ObservableCollection(x))); + BuildingHeights = new(io.BuildingComponents.BuildingHeights); + BuildingAnimations = new(io.BuildingComponents.BuildingAnimations); + BuildingVariations = new(io.BuildingComponents.BuildingVariations.Select(x => new ObservableCollection(x))); UnkBuildingData = new(io.UnkBuildingData); BuildingSizeFlags = io.BuildingSizeFlags; BuildingWall = io.BuildingWall == null ? null : new(io.BuildingWall); @@ -87,12 +88,12 @@ public override IndustryObject GetAsModel() => new() { AnimationSequences = AnimationSequences.ToList().ConvertAll(x => x.ToList()), - //BuildingComponents = new BuildingComponentsModel() - //{ - // BuildingHeights = [.. BuildingHeights], - // BuildingAnimations = [.. BuildingAnimations], - // BuildingVariations = BuildingVariations.ToList().ConvertAll(x => x.ToList()), - //}, + BuildingComponents = new BuildingComponentsModel() + { + BuildingHeights = [.. BuildingHeights], + BuildingAnimations = [.. BuildingAnimations], + BuildingVariations = [.. BuildingVariations.Select(x => x.ToList())], + }, UnkBuildingData = [.. UnkBuildingData], BuildingSizeFlags = BuildingSizeFlags, BuildingWall = BuildingWall?.GetAsModel(), From 8ba96cf56a2b221ecdabf2258e0c3ecc131dac2a Mon Sep 17 00:00:00 2001 From: Benjamin Sutas Date: Tue, 16 Sep 2025 22:41:24 +1000 Subject: [PATCH 14/21] progress --- Dat/FileParsing/SawyerStreamReader.cs | 4 +- Dat/Loaders/AirportObjectLoader.cs | 14 +- Dat/Loaders/BridgeObjectLoader.cs | 22 +- Dat/Loaders/BuildingObjectLoader.cs | 14 +- Dat/Loaders/CargoObjectLoader.cs | 24 +- Dat/Loaders/CliffEdgeObjectLoader.cs | 32 +-- Dat/Loaders/ClimateObjectLoader.cs | 21 +- Dat/Loaders/CompetitorObjectLoader.cs | 40 +-- Dat/Loaders/CurrencyObjectLoader.cs | 21 +- Dat/Loaders/DockObjectLoader.cs | 13 +- Dat/Loaders/HillShapesObjectLoader.cs | 18 +- Dat/Loaders/IDatObjectLoader.cs | 5 + Dat/Loaders/ImageTableLoader.cs | 256 +++++++++++++++++- Dat/Loaders/IndustryObjectLoader.cs | 13 +- Dat/Loaders/InterfaceSkinObjectLoader.cs | 45 +-- Dat/Loaders/LandObjectLoader.cs | 18 +- Dat/Loaders/LevelCrossingObjectLoader.cs | 24 +- Dat/Loaders/RegionObjectLoader.cs | 17 +- Dat/Loaders/RoadExtraObjectLoader.cs | 14 +- Dat/Loaders/RoadObjectLoader.cs | 18 +- Dat/Loaders/RoadStationObjectLoader.cs | 18 +- Dat/Loaders/ScaffoldingObjectLoader.cs | 18 +- Dat/Loaders/ScenarioTextObjectLoader.cs | 16 +- Dat/Loaders/SnowObjectLoader.cs | 18 +- Dat/Loaders/SoundObjectLoader.cs | 13 +- Dat/Loaders/SteamObjectLoader.cs | 18 +- Dat/Loaders/StreetLightObjectLoader.cs | 14 +- Dat/Loaders/TownNamesObjectLoader.cs | 9 +- Dat/Loaders/TrackExtraObjectLoader.cs | 18 +- Dat/Loaders/TrackObjectLoader.cs | 21 +- Dat/Loaders/TrackSignalObjectLoader.cs | 18 +- Dat/Loaders/TrackStationObjectLoader.cs | 18 +- Dat/Loaders/TreeObjectLoader.cs | 42 +-- Dat/Loaders/TunnelObjectLoader.cs | 18 +- Dat/Loaders/Vehicle/VehicleObjectLoader.cs | 20 +- Dat/Loaders/WallObjectLoader.cs | 18 +- Dat/Loaders/WaterObjectLoader.cs | 32 +-- Dat/Types/G1Dat.cs | 3 +- .../ObjectModels/IImageTableNameProvider.cs | 5 +- Definitions/ObjectModels/LocoObject.cs | 21 +- .../Objects/Airport/AirportObject.cs | 6 +- .../Objects/Bridge/BridgeObject.cs | 6 +- .../Objects/Building/BuildingObject.cs | 3 +- .../ObjectModels/Objects/Cargo/CargoObject.cs | 4 +- .../Objects/CliffEdge/CliffEdgeObject.cs | 4 +- .../Objects/Climate/ClimateObject.cs | 6 +- .../Objects/Competitor/CompetitorObject.cs | 4 +- .../Objects/Currency/CurrencyObject.cs | 7 +- .../ObjectModels/Objects/Dock/DockObject.cs | 6 +- .../Objects/HillShapes/HillShapesObject.cs | 4 +- .../Objects/Industry/IndustryObject.cs | 6 +- .../InterfaceSkin/InterfaceSkinObject.cs | 3 +- .../ObjectModels/Objects/Land/LandObject.cs | 3 +- .../LevelCrossing/LevelCrossingObject.cs | 7 +- .../ObjectModels/Objects/Road/RoadObject.cs | 44 +-- .../Objects/RoadExtra/RoadExtraObject.cs | 6 +- .../Objects/RoadStation/RoadStationObject.cs | 3 +- .../Objects/Scaffolding/ScaffoldingObject.cs | 6 +- .../ScenarioText/ScenarioTextObject.cs | 5 +- .../ObjectModels/Objects/Snow/SnowObject.cs | 4 +- .../ObjectModels/Objects/Steam/SteamObject.cs | 6 +- .../Objects/StreetLight/StreetLightObject.cs | 4 +- .../ObjectModels/Objects/Track/TrackObject.cs | 3 +- .../Objects/TrackExtra/TrackExtraObject.cs | 3 +- .../Objects/TrackSignal/TrackSignalObject.cs | 3 +- .../TrackStation/TrackStationObject.cs | 7 +- .../ObjectModels/Objects/Tree/TreeObject.cs | 7 +- .../Objects/Tunnel/TunnelObject.cs | 4 +- .../Objects/Vehicle/VehicleObject.cs | 6 +- .../ObjectModels/Objects/Wall/WallObject.cs | 4 +- .../ObjectModels/Objects/Water/WaterObject.cs | 3 +- .../Graphics/ImageTableViewModel.cs | 11 +- .../LocoTypes/ObjectEditorViewModel.cs | 18 +- Gui/Views/ImageTableView.axaml | 4 +- Tests/LoadSaveTests.cs | 2 +- 75 files changed, 768 insertions(+), 445 deletions(-) diff --git a/Dat/FileParsing/SawyerStreamReader.cs b/Dat/FileParsing/SawyerStreamReader.cs index 1181d384..a0504a5c 100644 --- a/Dat/FileParsing/SawyerStreamReader.cs +++ b/Dat/FileParsing/SawyerStreamReader.cs @@ -274,8 +274,8 @@ public static (G1Header Header, List Table) ReadImageTable(Loco currElement.ImageData = [.. imageData[(int)currElement.Offset..(int)nextOffset]]; } - // if rleCompressed, uncompress it, except if the duplicate-previous flag is also set - by the current code here, the previous - // image (which was also compressed) is now uncompressed, so we don't need do double-uncompress it. + // if rleCompressed, decompress it, except if the duplicate-previous flag is also set - by the current code here, the previous + // image (which was also compressed) is now decompressed, so we don't need do double-decompress it. if (currElement.Flags.HasFlag(DatG1ElementFlags.IsRLECompressed) && !currElement.Flags.HasFlag(DatG1ElementFlags.DuplicatePrevious)) { currElement.ImageData = DecodeRLEImageData(currElement); diff --git a/Dat/Loaders/AirportObjectLoader.cs b/Dat/Loaders/AirportObjectLoader.cs index 2f1b0827..60856ee1 100644 --- a/Dat/Loaders/AirportObjectLoader.cs +++ b/Dat/Loaders/AirportObjectLoader.cs @@ -18,13 +18,15 @@ public static class Constants internal static class StructSizes { - public const int Dat = 0xBA; public const int BuildingPartAnimation = 0x02; public const int AirportBuilding = 0x04; public const int MovementNode = 0x08; public const int MovementEdge = 0x0C; } + public static ObjectType ObjectType => ObjectType.Airport; + public static DatObjectType DatObjectType => DatObjectType.Airport; + public static LocoObject Load(Stream stream) { var initialStreamPosition = stream.Position; @@ -62,10 +64,10 @@ public static LocoObject Load(Stream stream) model.var_B6 = br.ReadBytes(0xBA - 0xB6); // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table - var stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType.Airport), null); + var stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType), null); // variable LoadVariable(br, model, numBuildingParts, numBuildingVariations, numMovementNodes, numMovementEdges); @@ -74,9 +76,9 @@ public static LocoObject Load(Stream stream) var imageList = SawyerStreamReader.ReadImageTable(br).Table; // define groups - var imageTable = ImageTableLoader.CreateImageTable(imageList); + var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); - return new LocoObject(ObjectType.Airport, model, stringTable, imageTable); + return new LocoObject(ObjectType, model, stringTable, imageTable); } } @@ -168,7 +170,7 @@ public static void Save(Stream stream, LocoObject obj) bw.Write(model.var_B6); // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table SawyerStreamWriter.WriteStringTable(stream, obj.StringTable); diff --git a/Dat/Loaders/BridgeObjectLoader.cs b/Dat/Loaders/BridgeObjectLoader.cs index 49470a5c..3f2ba0ec 100644 --- a/Dat/Loaders/BridgeObjectLoader.cs +++ b/Dat/Loaders/BridgeObjectLoader.cs @@ -16,10 +16,8 @@ public static class Constants public const int MaxNumRoadMods = 7; } - public static class StructSizes - { - public const int Dat = 0x2C; - } + public static ObjectType ObjectType => ObjectType.Bridge; + public static DatObjectType DatObjectType => DatObjectType.Bridge; public static LocoObject Load(Stream stream) { @@ -28,8 +26,6 @@ public static LocoObject Load(Stream stream) using (var br = new LocoBinaryReader(stream)) { var model = new BridgeObject(); - var stringTable = new StringTable(); - var imageTable = new List(); // fixed br.SkipStringId(); // Name offset, not part of object definition @@ -54,19 +50,22 @@ public static LocoObject Load(Stream stream) model.DesignedYear = br.ReadUInt16(); // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table - stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType.Bridge), null); + var stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType), null); // variable model.CompatibleTrackObjects = br.ReadS5HeaderList(compatibleTrackCount); model.CompatibleRoadObjects = br.ReadS5HeaderList(compatibleRoadCount); // image table - imageTable = SawyerStreamReader.ReadImageTable(br).Table; + var imageList = SawyerStreamReader.ReadImageTable(br).Table; - return new LocoObject(ObjectType.Bridge, model, stringTable, imageTable); + // define groups + var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); + + return new LocoObject(ObjectType, model, stringTable, imageTable); } } @@ -99,7 +98,7 @@ public static void Save(Stream stream, LocoObject obj) bw.Write(model.DesignedYear); // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table SawyerStreamWriter.WriteStringTable(stream, obj.StringTable); @@ -150,7 +149,6 @@ internal enum DatBridgeObjectFlags : uint8_t None = 0, HasRoof = 1 << 0, } - } static class BridgeFlagsConverter diff --git a/Dat/Loaders/BuildingObjectLoader.cs b/Dat/Loaders/BuildingObjectLoader.cs index 33a2fb5c..f631949d 100644 --- a/Dat/Loaders/BuildingObjectLoader.cs +++ b/Dat/Loaders/BuildingObjectLoader.cs @@ -21,7 +21,6 @@ public static class Constants public static class StructSizes { - public const int Dat = 0xBE; public const int BuildingPartAnimation = 0x02; } @@ -30,6 +29,9 @@ public static class ImageGroups public const int Base = 4; } + public static ObjectType ObjectType => ObjectType.Building; + public static DatObjectType DatObjectType => DatObjectType.Building; + public static LocoObject Load(Stream stream) { var initialStreamPosition = stream.Position; @@ -70,10 +72,10 @@ public static LocoObject Load(Stream stream) br.SkipPointer(Constants.MaxElevatorHeightSequences); // ElevatorHeightSequences, not part of object definition // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table - var stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType.Building), null); + var stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType), null); // variable LoadVariable(br, model, numBuildingParts, numBuildingVariations, numElevatorSequences); @@ -82,9 +84,9 @@ public static LocoObject Load(Stream stream) var imageList = SawyerStreamReader.ReadImageTable(br).Table; // define groups - var imageTable = ImageTableLoader.CreateImageTable(imageList); + var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); - return new LocoObject(ObjectType.Building, model, stringTable, imageTable); + return new LocoObject(ObjectType, model, stringTable, imageTable); } } @@ -143,7 +145,7 @@ public static void Save(Stream stream, LocoObject obj) bw.WriteEmptyPointer(Constants.MaxElevatorHeightSequences); // ElevatorHeightSequences, not part of object definition // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table SawyerStreamWriter.WriteStringTable(stream, obj.StringTable); diff --git a/Dat/Loaders/CargoObjectLoader.cs b/Dat/Loaders/CargoObjectLoader.cs index f10c92ff..6eb29ac7 100644 --- a/Dat/Loaders/CargoObjectLoader.cs +++ b/Dat/Loaders/CargoObjectLoader.cs @@ -8,13 +8,8 @@ namespace Dat.Loaders; public abstract class CargoObjectLoader : IDatObjectLoader { - public static class Constants - { } - - public static class StructSizes - { - public const int Dat = 0x1F; - } + public static ObjectType ObjectType => ObjectType.Cargo; + public static DatObjectType DatObjectType => DatObjectType.Cargo; public static LocoObject Load(Stream stream) { @@ -23,8 +18,6 @@ public static LocoObject Load(Stream stream) using (var br = new LocoBinaryReader(stream)) { var model = new CargoObject(); - var stringTable = new StringTable(); - var imageTable = new List(); // fixed br.SkipStringId(); // Name offset, not part of object definition @@ -47,18 +40,21 @@ public static LocoObject Load(Stream stream) model.UnitSize = br.ReadByte(); // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table - stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType.Cargo), null); + var stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType), null); // variable // N/A // image table - imageTable = SawyerStreamReader.ReadImageTable(br).Table; + var imageList = SawyerStreamReader.ReadImageTable(br).Table; + + // define groups + var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); - return new LocoObject(ObjectType.Cargo, model, stringTable, imageTable); + return new LocoObject(ObjectType, model, stringTable, imageTable); } } @@ -89,7 +85,7 @@ public static void Save(Stream stream, LocoObject obj) bw.Write(model.UnitSize); // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table SawyerStreamWriter.WriteStringTable(stream, obj.StringTable); diff --git a/Dat/Loaders/CliffEdgeObjectLoader.cs b/Dat/Loaders/CliffEdgeObjectLoader.cs index 472e10fe..2dbc90f1 100644 --- a/Dat/Loaders/CliffEdgeObjectLoader.cs +++ b/Dat/Loaders/CliffEdgeObjectLoader.cs @@ -3,42 +3,39 @@ using Definitions.ObjectModels; using Definitions.ObjectModels.Objects.CliffEdge; using Definitions.ObjectModels.Types; -using System.ComponentModel; namespace Dat.Loaders; public abstract class CliffEdgeObjectLoader : IDatObjectLoader { - public static class StructSizes - { - public const int DatStructSize = 0x06; - } + public static ObjectType ObjectType => ObjectType.CliffEdge; + public static DatObjectType DatObjectType => DatObjectType.CliffEdge; public static LocoObject Load(Stream stream) { using (var br = new LocoBinaryReader(stream)) { var model = new CliffEdgeObject(); - var stringTable = new StringTable(); - var imageTable = new List(); // fixed br.SkipStringId(); // Name offset, not part of object definition br.SkipImageId(); // Image offset, not part of object definition // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, StructSizes.DatStructSize, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table - stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType.CliffEdge), null); + var stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType), null); // variable // N/A // image table - imageTable = SawyerStreamReader.ReadImageTable(br).Table; + var imageList = SawyerStreamReader.ReadImageTable(br).Table; + + var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); - return new LocoObject(ObjectType.CliffEdge, model, stringTable, imageTable); + return new LocoObject(ObjectType, model, stringTable, imageTable); } } @@ -50,7 +47,7 @@ public static void Save(Stream stream, LocoObject obj) bw.WriteEmptyImageId(); // Image offset, not part of object definition // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, StructSizes.DatStructSize, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table SawyerStreamWriter.WriteStringTable(stream, obj.StringTable); @@ -63,14 +60,3 @@ public static void Save(Stream stream, LocoObject obj) } } } - -[TypeConverter(typeof(ExpandableObjectConverter))] -[LocoStructSize(0x06)] -[LocoStructType(DatObjectType.CliffEdge)] -internal record DatCliffEdgeObject( - [property: LocoStructOffset(0x00), LocoString, Browsable(false)] string_id Name, - [property: LocoStructOffset(0x02), LocoString, Browsable(false)] image_id Image - ) -{ - public bool Validate() => true; -} diff --git a/Dat/Loaders/ClimateObjectLoader.cs b/Dat/Loaders/ClimateObjectLoader.cs index e4c3671b..ee8d50ce 100644 --- a/Dat/Loaders/ClimateObjectLoader.cs +++ b/Dat/Loaders/ClimateObjectLoader.cs @@ -14,10 +14,8 @@ public static class Constants public const int Seasons = 4; } - public static class StructSizes - { - public const int Dat = 0x0A; - } + public static ObjectType ObjectType => ObjectType.Climate; + public static DatObjectType DatObjectType => DatObjectType.Climate; public static LocoObject Load(Stream stream) { @@ -26,8 +24,6 @@ public static LocoObject Load(Stream stream) using (var br = new LocoBinaryReader(stream)) { var model = new ClimateObject(); - var stringTable = new StringTable(); - var imageTable = new List(); // fixed br.SkipStringId(); // Name offset, not part of object definition @@ -41,18 +37,21 @@ public static LocoObject Load(Stream stream) _ = br.ReadByte(); // pad // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table - stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType.Climate), null); + var stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType), null); // variable // N/A // image table - imageTable = SawyerStreamReader.ReadImageTable(br).Table; + var imageList = SawyerStreamReader.ReadImageTable(br).Table; + + // define groups + var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); - return new LocoObject(ObjectType.Climate, model, stringTable, imageTable); + return new LocoObject(ObjectType, model, stringTable, imageTable); } } @@ -74,7 +73,7 @@ public static void Save(Stream stream, LocoObject obj) bw.Write((uint8_t)0); // pad // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table SawyerStreamWriter.WriteStringTable(stream, obj.StringTable); diff --git a/Dat/Loaders/CompetitorObjectLoader.cs b/Dat/Loaders/CompetitorObjectLoader.cs index 491bb9fd..b689d9e7 100644 --- a/Dat/Loaders/CompetitorObjectLoader.cs +++ b/Dat/Loaders/CompetitorObjectLoader.cs @@ -3,8 +3,6 @@ using Definitions.ObjectModels; using Definitions.ObjectModels.Objects.Competitor; using Definitions.ObjectModels.Types; -using System.ComponentModel; -using static Dat.Loaders.CompetitorObjectLoader; namespace Dat.Loaders; @@ -15,10 +13,8 @@ public static class Constants public const int ImagesLength = 9; } - public static class StructSizes - { - public const int Dat = 0x38; - } + public static ObjectType ObjectType => ObjectType.Competitor; + public static DatObjectType DatObjectType => DatObjectType.Competitor; public static LocoObject Load(Stream stream) { @@ -27,8 +23,6 @@ public static LocoObject Load(Stream stream) using (var br = new LocoBinaryReader(stream)) { var model = new CompetitorObject(); - var stringTable = new StringTable(); - var imageTable = new List(); // fixed br.SkipStringId(); // First name offset, not part of object definition @@ -43,18 +37,21 @@ public static LocoObject Load(Stream stream) model.var_37 = br.ReadByte(); // unused // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table - stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType.Competitor), null); + var stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType), null); // variable // N/A // image table - imageTable = SawyerStreamReader.ReadImageTable(br).Table; + var imageList = SawyerStreamReader.ReadImageTable(br).Table; - return new LocoObject(ObjectType.Competitor, model, stringTable, imageTable); + // define groups + var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); + + return new LocoObject(ObjectType, model, stringTable, imageTable); } } @@ -77,7 +74,7 @@ public static void Save(Stream stream, LocoObject obj) bw.Write(model.var_37); // unused // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table SawyerStreamWriter.WriteStringTable(stream, obj.StringTable); @@ -140,20 +137,3 @@ internal enum DatCompetitorPlaystyle : uint32_t unk12 = 1 << 12, } } - -[TypeConverter(typeof(ExpandableObjectConverter))] -[LocoStructSize(0x38)] -[LocoStructType(DatObjectType.Competitor)] -internal record DatCompetitorObject( - [property: LocoStructOffset(0x00), LocoString, Browsable(false)] string_id FullName, - [property: LocoStructOffset(0x00), LocoString, Browsable(false)] string_id LastName, - [property: LocoStructOffset(0x04)] DatCompetitorNamePrefix AvailableNamePrefixes, // bitset - [property: LocoStructOffset(0x08)] DatCompetitorPlaystyle AvailablePlaystyles, // bitset - [property: LocoStructOffset(0x0C)] DatCompetitorEmotion Emotions, // bitset - [property: LocoStructOffset(0x10), Browsable(false), LocoArrayLength(CompetitorObjectLoader.Constants.ImagesLength)] image_id[] Images, - [property: LocoStructOffset(0x34)] uint8_t Intelligence, - [property: LocoStructOffset(0x35)] uint8_t Aggressiveness, - [property: LocoStructOffset(0x36)] uint8_t Competitiveness, - [property: LocoStructOffset(0x37), LocoPropertyMaybeUnused] uint8_t var_37 - ) -{ } diff --git a/Dat/Loaders/CurrencyObjectLoader.cs b/Dat/Loaders/CurrencyObjectLoader.cs index 3b0ceb47..33b76549 100644 --- a/Dat/Loaders/CurrencyObjectLoader.cs +++ b/Dat/Loaders/CurrencyObjectLoader.cs @@ -4,6 +4,7 @@ using Definitions.ObjectModels.Objects.Currency; using Definitions.ObjectModels.Types; using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; namespace Dat.Loaders; @@ -17,6 +18,9 @@ public static class StructSizes public const int Dat = 0x0C; } + public static ObjectType ObjectType => ObjectType.Currency; + public static DatObjectType DatObjectType => DatObjectType.Currency; + public static LocoObject Load(Stream stream) { var initialStreamPosition = stream.Position; @@ -24,8 +28,6 @@ public static LocoObject Load(Stream stream) using (var br = new LocoBinaryReader(stream)) { var model = new CurrencyObject(); - var stringTable = new StringTable(); - var imageTable = new List(); // fixed br.SkipStringId(); // Name offset, not part of object definition @@ -36,18 +38,21 @@ public static LocoObject Load(Stream stream) model.Factor = br.ReadByte(); // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table - stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType.Currency), null); + var stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType), null); // variable // N/A // image table - imageTable = SawyerStreamReader.ReadImageTable(br).Table; + var imageList = SawyerStreamReader.ReadImageTable(br).Table; + + // define groups + var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); - return new LocoObject(ObjectType.Currency, model, stringTable, imageTable); + return new LocoObject(ObjectType, model, stringTable, imageTable); } } @@ -66,7 +71,7 @@ public static void Save(Stream stream, LocoObject obj) bw.Write(model.Factor); // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table SawyerStreamWriter.WriteStringTable(stream, obj.StringTable); @@ -78,6 +83,8 @@ public static void Save(Stream stream, LocoObject obj) SawyerStreamWriter.WriteImageTable(stream, obj.ImageTable.GraphicsElements); } } + + public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) => throw new NotImplementedException(); } [TypeConverter(typeof(ExpandableObjectConverter))] diff --git a/Dat/Loaders/DockObjectLoader.cs b/Dat/Loaders/DockObjectLoader.cs index f28be5a7..05ba1997 100644 --- a/Dat/Loaders/DockObjectLoader.cs +++ b/Dat/Loaders/DockObjectLoader.cs @@ -21,6 +21,9 @@ public static class StructSizes public const int Dat = 0x28; } + public static ObjectType ObjectType => ObjectType.Dock; + public static DatObjectType DatObjectType => DatObjectType.Dock; + public static LocoObject Load(Stream stream) { var initialStreamPosition = stream.Position; @@ -52,10 +55,10 @@ public static LocoObject Load(Stream stream) }; // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table - var stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType.Dock), null); + var stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType), null); // variable model.BuildingComponents.BuildingHeights = br.ReadBuildingHeights(numBuildingParts); @@ -66,9 +69,9 @@ public static LocoObject Load(Stream stream) var imageList = SawyerStreamReader.ReadImageTable(br).Table; // define groups - var imageTable = ImageTableLoader.CreateImageTable(imageList); + var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); - return new LocoObject(ObjectType.Dock, model, stringTable, imageTable); + return new LocoObject(ObjectType, model, stringTable, imageTable); } } @@ -98,7 +101,7 @@ public static void Save(Stream stream, LocoObject obj) bw.Write(model.BoatPosition.Y); // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table SawyerStreamWriter.WriteStringTable(stream, obj.StringTable); diff --git a/Dat/Loaders/HillShapesObjectLoader.cs b/Dat/Loaders/HillShapesObjectLoader.cs index 36949c61..7e093062 100644 --- a/Dat/Loaders/HillShapesObjectLoader.cs +++ b/Dat/Loaders/HillShapesObjectLoader.cs @@ -16,6 +16,9 @@ public static class StructSizes public const int Dat = 0x0E; } + public static ObjectType ObjectType => ObjectType.HillShapes; + public static DatObjectType DatObjectType => DatObjectType.HillShapes; + public static LocoObject Load(Stream stream) { var initialStreamPosition = stream.Position; @@ -23,8 +26,6 @@ public static LocoObject Load(Stream stream) using (var br = new LocoBinaryReader(stream)) { var model = new HillShapesObject(); - var stringTable = new StringTable(); - var imageTable = new List(); // fixed br.SkipStringId(); // Name offset, not part of object definition @@ -35,18 +36,21 @@ public static LocoObject Load(Stream stream) model.IsHeightMap = ((DatHillShapeFlags)br.ReadUInt16()).HasFlag(DatHillShapeFlags.IsHeightMap); // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table - stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType.HillShapes), null); + var stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType), null); // variable // N/A // image table - imageTable = SawyerStreamReader.ReadImageTable(br).Table; + var imageList = SawyerStreamReader.ReadImageTable(br).Table; + + // define groups + var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); - return new LocoObject(ObjectType.HillShapes, model, stringTable, imageTable); + return new LocoObject(ObjectType, model, stringTable, imageTable); } } @@ -65,7 +69,7 @@ public static void Save(Stream stream, LocoObject obj) bw.Write((uint16_t)(model.IsHeightMap ? DatHillShapeFlags.IsHeightMap : DatHillShapeFlags.None)); // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table SawyerStreamWriter.WriteStringTable(stream, obj.StringTable); diff --git a/Dat/Loaders/IDatObjectLoader.cs b/Dat/Loaders/IDatObjectLoader.cs index 79f53233..fa35bd40 100644 --- a/Dat/Loaders/IDatObjectLoader.cs +++ b/Dat/Loaders/IDatObjectLoader.cs @@ -1,4 +1,6 @@ +using Dat.Data; using Definitions.ObjectModels; +using Definitions.ObjectModels.Types; namespace Dat.Loaders; @@ -13,6 +15,9 @@ public interface IDatObjectLoader // where TDetails : IDatDetails { //public static abstract TDetails DatDetails { get; } + public static abstract ObjectType ObjectType { get; } + public static abstract DatObjectType DatObjectType { get; } + public static abstract LocoObject Load(Stream stream); public static abstract void Save(Stream stream, LocoObject obj); } diff --git a/Dat/Loaders/ImageTableLoader.cs b/Dat/Loaders/ImageTableLoader.cs index e2ff1ce4..7bf97cdb 100644 --- a/Dat/Loaders/ImageTableLoader.cs +++ b/Dat/Loaders/ImageTableLoader.cs @@ -1,19 +1,269 @@ using Definitions.ObjectModels; +using Definitions.ObjectModels.Objects.Airport; using Definitions.ObjectModels.Types; namespace Dat.Loaders; public static class ImageTableLoader { - public static ImageTable CreateImageTable(List imageList) + public static ImageTable CreateImageTable(IImageTableNameProvider imageNamer, ObjectType objectType, List imageList) + { + NameImages(imageNamer, imageList); + + var imageTable = new ImageTable(); + + switch (objectType) + { + case ObjectType.InterfaceSkin: + CreateInterfaceGroups(imageList, imageTable); + break; + case ObjectType.Sound: + imageTable.Groups.Add(("", imageList.ToList())); + break; + case ObjectType.Currency: + imageTable.Groups.Add(("", imageList.ToList())); + break; + case ObjectType.Steam: + imageTable.Groups.Add(("", imageList.ToList())); + break; + case ObjectType.CliffEdge: + CreateCliffEdgeGroups(imageList, imageTable); + break; + case ObjectType.Water: + imageTable.Groups.Add(("", imageList.ToList())); + break; + case ObjectType.Land: + imageTable.Groups.Add(("", imageList.ToList())); + break; + case ObjectType.TownNames: + imageTable.Groups.Add(("", imageList.ToList())); + break; + case ObjectType.Cargo: + CreateCargoGroups(imageList, imageTable); + break; + case ObjectType.Wall: + imageTable.Groups.Add(("", imageList.ToList())); + break; + case ObjectType.TrackSignal: + imageTable.Groups.Add(("", imageList.ToList())); + break; + case ObjectType.LevelCrossing: + imageTable.Groups.Add(("", imageList.ToList())); + break; + case ObjectType.StreetLight: + imageTable.Groups.Add(("", imageList.ToList())); + break; + case ObjectType.Tunnel: + imageTable.Groups.Add(("", imageList.ToList())); + break; + case ObjectType.Bridge: + CreateBridgeGroups(imageList, imageTable); + break; + case ObjectType.TrackStation: + imageTable.Groups.Add(("", imageList.ToList())); + break; + case ObjectType.TrackExtra: + imageTable.Groups.Add(("", imageList.ToList())); + break; + case ObjectType.Track: + imageTable.Groups.Add(("", imageList.ToList())); + break; + case ObjectType.RoadStation: + imageTable.Groups.Add(("", imageList.ToList())); + break; + case ObjectType.RoadExtra: + imageTable.Groups.Add(("", imageList.ToList())); + break; + case ObjectType.Road: + imageTable.Groups.Add(("", imageList.ToList())); + break; + case ObjectType.Airport: + CreateAirportGroups(imageList, imageTable); + break; + case ObjectType.Dock: + CreateDockGroups(imageList, imageTable); + break; + case ObjectType.Vehicle: + imageTable.Groups.Add(("", imageList.ToList())); + break; + case ObjectType.Tree: + imageTable.Groups.Add(("", imageList.ToList())); + break; + case ObjectType.Snow: + imageTable.Groups.Add(("", imageList.ToList())); + break; + case ObjectType.Climate: + imageTable.Groups.Add(("", imageList.ToList())); + break; + case ObjectType.HillShapes: + imageTable.Groups.Add(("", imageList.ToList())); + break; + case ObjectType.Building: + CreateBuildingGroups(imageList, imageTable); + break; + case ObjectType.Scaffolding: + CreateScaffoldingGroups(imageList, imageTable); + break; + case ObjectType.Industry: + CreateBuildingGroups(imageList, imageTable); + break; + case ObjectType.Region: + imageTable.Groups.Add(("", imageList.ToList())); + break; + case ObjectType.Competitor: + CreateCompetitorGroups(imageList, imageTable); + break; + case ObjectType.ScenarioText: + imageTable.Groups.Add(("", imageList.ToList())); + break; + default: + break; + } + + return imageTable; + } + + #region NewImpl + + private static void NameImages(IImageTableNameProvider imageNamer, List imageList) + { + for (var i = 0; i < imageList.Count; ++i) + { + imageList[i].Name = imageNamer.TryGetImageName(i, out var name) + ? name + : DefaultImageTableNameProvider.GetImageName(i); + } + } + + private static ImageTable CreateImageTable(AirportObject airportObject, List imageList) { var imageTable = new ImageTable(); - CreateBasicGroups(imageList, imageTable); + + imageTable.Groups.Add(("preview", imageList[0..1])); + + imageTable.Groups.AddRange(imageList + .Skip(1) + .Chunk(4) + .Select((x, i) => ($"Part {i}", x.ToList())) + .ToList()); + return imageTable; } - private static void CreateBasicGroups(List imageList, ImageTable imageTable) + #endregion + + private static void CreateAirportGroups(List imageList, ImageTable imageTable) + { + imageTable.Groups.Add(("preview", imageList[0..1])); + + imageTable.Groups.AddRange(imageList + .Skip(1) + .Chunk(4) + .Select((x, i) => ($"Part {i}", x.ToList())) + .ToList()); + } + + private static void CreateBridgeGroups(List imageList, ImageTable imageTable) + { + imageTable.Groups.Add(("preview", imageList[0..1])); + imageTable.Groups.Add(("base plates", imageList[1..5])); + imageTable.Groups.Add(("unk", imageList[6..11])); + imageTable.Groups.Add(("", imageList[12..])); + } + + private static void CreateBuildingGroups(List imageList, ImageTable imageTable) => imageTable.Groups = imageList .Chunk(4) .Select((x, i) => ($"Part {i}", x.ToList())) .ToList(); + + private static void CreateCargoGroups(List imageList, ImageTable imageTable) + { + imageTable.Groups.Add(("preview", imageList[0..1])); + imageTable.Groups.Add(("station variations", imageList[1..])); + } + + private static void CreateCliffEdgeGroups(List imageList, ImageTable imageTable) + { + imageTable.Groups.Add(("left west", imageList[0..16])); + imageTable.Groups.Add(("right east", imageList[16..32])); + imageTable.Groups.Add(("right west", imageList[32..48])); + imageTable.Groups.Add(("left east", imageList[48..64])); + imageTable.Groups.Add(("far-side slopes", imageList[64..])); + } + + private static void CreateCompetitorGroups(List imageList, ImageTable imageTable) + { + imageTable.Groups.Add(("neutral", imageList[0..2])); + imageTable.Groups.Add(("happy", imageList[2..4])); + imageTable.Groups.Add(("worried", imageList[4..6])); + imageTable.Groups.Add(("thinking", imageList[6..8])); + imageTable.Groups.Add(("dejected", imageList[8..10])); + imageTable.Groups.Add(("surprised", imageList[10..12])); + imageTable.Groups.Add(("scared", imageList[12..14])); + imageTable.Groups.Add(("angry", imageList[14..16])); + imageTable.Groups.Add(("disgusted", imageList[16..18])); + } + + private static void CreateDockGroups(List imageList, ImageTable imageTable) + { + imageTable.Groups.Add(("preview", [imageList[0]])); + + imageTable.Groups.AddRange(imageList + .Skip(1) + .Chunk(4) + .Select((x, i) => ($"Part {i}", x.ToList())) + .ToList()); + } + + private static void CreateInterfaceGroups(List imageList, ImageTable imageTable) + { + imageTable.Groups.Add(("preview", imageList[0..1])); + imageTable.Groups.Add(("toolbar", imageList[1..31])); + imageTable.Groups.Add(("build-vehicle", imageList[31..43])); + imageTable.Groups.Add(("toolbar", imageList[43..49])); + imageTable.Groups.Add(("paint", imageList[49..55])); + imageTable.Groups.Add(("population", imageList[57..65])); + imageTable.Groups.Add(("performance-index", imageList[65..73])); + imageTable.Groups.Add(("cargo-units", imageList[73..81])); + imageTable.Groups.Add(("cargo-distance", imageList[81..89])); + imageTable.Groups.Add(("production", imageList[89..97])); + imageTable.Groups.Add(("wrench", imageList[97..113])); + imageTable.Groups.Add(("finances", imageList[113..129])); + imageTable.Groups.Add(("cup", imageList[129..145])); + imageTable.Groups.Add(("ratings", imageList[145..161])); + imageTable.Groups.Add(("transported", imageList[161..168])); + imageTable.Groups.Add(("cogs", imageList[168..172])); + imageTable.Groups.Add(("toolbar", imageList[172..203])); + imageTable.Groups.Add(("tab-train", imageList[203..211])); + imageTable.Groups.Add(("tab-aircraft", imageList[211..219])); + imageTable.Groups.Add(("tab-bus", imageList[219..227])); + imageTable.Groups.Add(("tab-tram", imageList[227..235])); + imageTable.Groups.Add(("tab-truck", imageList[235..243])); + imageTable.Groups.Add(("tab-ship", imageList[243..251])); + imageTable.Groups.Add(("build-train", imageList[251..267])); + imageTable.Groups.Add(("build-aircraft", imageList[267..283])); + imageTable.Groups.Add(("build-bus", imageList[283..299])); + imageTable.Groups.Add(("build-tram", imageList[299..315])); + imageTable.Groups.Add(("build-truck", imageList[315..331])); + imageTable.Groups.Add(("build-ship", imageList[331..347])); + imageTable.Groups.Add(("build-industry", imageList[347..363])); + imageTable.Groups.Add(("build-town", imageList[363..379])); + imageTable.Groups.Add(("build-buildings", imageList[379..395])); + imageTable.Groups.Add(("build-misc-buildings", imageList[395..411])); + imageTable.Groups.Add(("build-extra", imageList[411..418])); + imageTable.Groups.Add(("train", imageList[418..426])); + imageTable.Groups.Add(("aircraft", imageList[426..434])); + imageTable.Groups.Add(("bus", imageList[434..442])); + imageTable.Groups.Add(("tram", imageList[442..450])); + imageTable.Groups.Add(("truck", imageList[450..458])); + imageTable.Groups.Add(("ship", imageList[458..466])); + imageTable.Groups.Add(("toolbar-map", imageList[466..470])); + } + + private static void CreateScaffoldingGroups(List imageList, ImageTable imageTable) + { + imageTable.Groups.Add(("type 0", imageList[0..10])); + imageTable.Groups.Add(("type 1", imageList[10..24])); + imageTable.Groups.Add(("type 2", imageList[24..36])); + } } diff --git a/Dat/Loaders/IndustryObjectLoader.cs b/Dat/Loaders/IndustryObjectLoader.cs index 3d7e8513..95d9a525 100644 --- a/Dat/Loaders/IndustryObjectLoader.cs +++ b/Dat/Loaders/IndustryObjectLoader.cs @@ -28,6 +28,9 @@ public static class StructSizes public const int IndustryObjectUnk38 = 0x02; } + public static ObjectType ObjectType => ObjectType.Industry; + public static DatObjectType DatObjectType => DatObjectType.Industry; + public static LocoObject Load(Stream stream) { var initialStreamPosition = stream.Position; @@ -88,10 +91,10 @@ public static LocoObject Load(Stream stream) model.MonthlyClosureChance = br.ReadByte(); // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table - var stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType.Industry), null); + var stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType), null); // variable LoadVariable(br, model, numBuildingParts, numBuildingVariations); @@ -100,9 +103,9 @@ public static LocoObject Load(Stream stream) var imageList = SawyerStreamReader.ReadImageTable(br).Table; // define groups - var imageTable = ImageTableLoader.CreateImageTable(imageList); + var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); - return new LocoObject(ObjectType.Industry, model, stringTable, imageTable); + return new LocoObject(ObjectType, model, stringTable, imageTable); } } @@ -195,7 +198,7 @@ public static void Save(Stream stream, LocoObject obj) bw.Write(model.MonthlyClosureChance); // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table SawyerStreamWriter.WriteStringTable(stream, obj.StringTable); diff --git a/Dat/Loaders/InterfaceSkinObjectLoader.cs b/Dat/Loaders/InterfaceSkinObjectLoader.cs index 02aef33b..3454652b 100644 --- a/Dat/Loaders/InterfaceSkinObjectLoader.cs +++ b/Dat/Loaders/InterfaceSkinObjectLoader.cs @@ -3,7 +3,6 @@ using Definitions.ObjectModels; using Definitions.ObjectModels.Objects.InterfaceSkin; using Definitions.ObjectModels.Types; -using System.ComponentModel; namespace Dat.Loaders; @@ -17,6 +16,9 @@ public static class StructSizes public const int Dat = 0x18; } + public static ObjectType ObjectType => ObjectType.InterfaceSkin; + public static DatObjectType DatObjectType => DatObjectType.InterfaceSkin; + public static LocoObject Load(Stream stream) { var initialStreamPosition = stream.Position; @@ -24,8 +26,6 @@ public static LocoObject Load(Stream stream) using (var br = new LocoBinaryReader(stream)) { var model = new InterfaceSkinObject(); - var stringTable = new StringTable(); - var imageTable = new List(); // fixed br.SkipStringId(); // Name offset, not part of object definition @@ -50,18 +50,21 @@ public static LocoObject Load(Stream stream) model.TimeToolbarColour = (Colour)br.ReadByte(); // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table - stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType.InterfaceSkin), null); + var stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType), null); // variable // N/A // image table - imageTable = SawyerStreamReader.ReadImageTable(br).Table; + var imageList = SawyerStreamReader.ReadImageTable(br).Table; + + // define groups + var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); - return new LocoObject(ObjectType.InterfaceSkin, model, stringTable, imageTable); + return new LocoObject(ObjectType, model, stringTable, imageTable); } } @@ -94,7 +97,7 @@ public static void Save(Stream stream, LocoObject obj) bw.Write((uint8_t)model.TimeToolbarColour); // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table SawyerStreamWriter.WriteStringTable(stream, obj.StringTable); @@ -107,29 +110,3 @@ public static void Save(Stream stream, LocoObject obj) } } } - -[LocoStructSize(0x18)] -[LocoStructType(DatObjectType.InterfaceSkin)] -internal record DatInterfaceSkinObject( - [property: LocoStructOffset(0x00), LocoString, Browsable(false)] string_id Name, - [property: LocoStructOffset(0x02), Browsable(false)] image_id Image, - [property: LocoStructOffset(0x06)] DatColour MapTooltipObjectColour, - [property: LocoStructOffset(0x07)] DatColour MapTooltipCargoColour, - [property: LocoStructOffset(0x08)] DatColour TooltipColour, - [property: LocoStructOffset(0x09)] DatColour ErrorColour, - [property: LocoStructOffset(0x0A)] DatColour WindowPlayerColor, - [property: LocoStructOffset(0x0B)] DatColour WindowTitlebarColour, - [property: LocoStructOffset(0x0C)] DatColour WindowColour, - [property: LocoStructOffset(0x0D)] DatColour WindowConstructionColour, - [property: LocoStructOffset(0x0E)] DatColour WindowTerraFormColour, - [property: LocoStructOffset(0x0F)] DatColour WindowMapColour, - [property: LocoStructOffset(0x10)] DatColour WindowOptionsColour, - [property: LocoStructOffset(0x11)] DatColour Colour_11, - [property: LocoStructOffset(0x12)] DatColour TopToolbarPrimaryColour, - [property: LocoStructOffset(0x13)] DatColour TopToolbarSecondaryColour, - [property: LocoStructOffset(0x14)] DatColour TopToolbarTertiaryColour, - [property: LocoStructOffset(0x15)] DatColour TopToolbarQuaternaryColour, - [property: LocoStructOffset(0x16)] DatColour PlayerInfoToolbarColour, - [property: LocoStructOffset(0x17)] DatColour TimeToolbarColour - ) -{ } diff --git a/Dat/Loaders/LandObjectLoader.cs b/Dat/Loaders/LandObjectLoader.cs index ea471ef8..97b726e7 100644 --- a/Dat/Loaders/LandObjectLoader.cs +++ b/Dat/Loaders/LandObjectLoader.cs @@ -17,6 +17,9 @@ public static class StructSizes public const int Dat = 0x1E; } + public static ObjectType ObjectType => ObjectType.Land; + public static DatObjectType DatObjectType => DatObjectType.Land; + public static LocoObject Load(Stream stream) { var initialStreamPosition = stream.Position; @@ -24,8 +27,6 @@ public static LocoObject Load(Stream stream) using (var br = new LocoBinaryReader(stream)) { var model = new LandObject(); - var stringTable = new StringTable(); - var imageTable = new List(); // fixed br.SkipStringId(); // Name offset, not part of object definition @@ -46,10 +47,10 @@ public static LocoObject Load(Stream stream) br.SkipByte(); // pad // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table - stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType.Land), null); + var stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType), null); // variable model.CliffEdgeHeader = br.ReadS5Header(); @@ -59,9 +60,12 @@ public static LocoObject Load(Stream stream) } // image table - imageTable = SawyerStreamReader.ReadImageTable(br).Table; + var imageList = SawyerStreamReader.ReadImageTable(br).Table; + + // define groups + var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); - return new LocoObject(ObjectType.Land, model, stringTable, imageTable); + return new LocoObject(ObjectType, model, stringTable, imageTable); } } @@ -90,7 +94,7 @@ public static void Save(Stream stream, LocoObject obj) bw.Write((uint8_t)0); // pad // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table SawyerStreamWriter.WriteStringTable(stream, obj.StringTable); diff --git a/Dat/Loaders/LevelCrossingObjectLoader.cs b/Dat/Loaders/LevelCrossingObjectLoader.cs index af828f3e..09ad2426 100644 --- a/Dat/Loaders/LevelCrossingObjectLoader.cs +++ b/Dat/Loaders/LevelCrossingObjectLoader.cs @@ -9,13 +9,8 @@ namespace Dat.Loaders; public abstract class LevelCrossingObjectLoader : IDatObjectLoader { - public static class Constants - { } - - public static class StructSizes - { - public const int Dat = 0x12; - } + public static ObjectType ObjectType => ObjectType.LevelCrossing; + public static DatObjectType DatObjectType => DatObjectType.LevelCrossing; public static LocoObject Load(Stream stream) { @@ -24,8 +19,6 @@ public static LocoObject Load(Stream stream) using (var br = new LocoBinaryReader(stream)) { var model = new LevelCrossingObject(); - var stringTable = new StringTable(); - var imageTable = new List(); // fixed br.SkipStringId(); // Name offset, not part of object definition @@ -41,18 +34,21 @@ public static LocoObject Load(Stream stream) br.SkipImageId(); // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table - stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType.LevelCrossing), null); + var stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType), null); // variable // N/A // image table - imageTable = SawyerStreamReader.ReadImageTable(br).Table; + var imageList = SawyerStreamReader.ReadImageTable(br).Table; + + // define groups + var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); - return new LocoObject(ObjectType.LevelCrossing, model, stringTable, imageTable); + return new LocoObject(ObjectType, model, stringTable, imageTable); } } @@ -76,7 +72,7 @@ public static void Save(Stream stream, LocoObject obj) bw.WriteEmptyImageId(); // Image offset, not part of object definition // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table SawyerStreamWriter.WriteStringTable(stream, obj.StringTable); diff --git a/Dat/Loaders/RegionObjectLoader.cs b/Dat/Loaders/RegionObjectLoader.cs index 4634d8af..391b41ed 100644 --- a/Dat/Loaders/RegionObjectLoader.cs +++ b/Dat/Loaders/RegionObjectLoader.cs @@ -15,10 +15,12 @@ public static class Constants public static class StructSizes { - public const int Dat = 0x12; public const int CargoInfluenceTownFilterType = 0x01; } + public static ObjectType ObjectType => ObjectType.Region; + public static DatObjectType DatObjectType => DatObjectType.Region; + public static LocoObject Load(Stream stream) { var initialStreamPosition = stream.Position; @@ -26,8 +28,6 @@ public static LocoObject Load(Stream stream) using (var br = new LocoBinaryReader(stream)) { var model = new RegionObject(); - var stringTable = new StringTable(); - var imageTable = new List(); // fixed br.SkipStringId(); // Name offset, not part of object definition @@ -44,19 +44,20 @@ public static LocoObject Load(Stream stream) br.SkipByte(); // pad // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table - stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType.Region), null); + var stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType), null); // variable model.CargoInfluenceObjects = br.ReadS5HeaderList(numCargoInfluenceObjects); model.DependentObjects = br.ReadS5HeaderList(); // image table - imageTable = SawyerStreamReader.ReadImageTable(br).Table; + // N/A but Region has an empty image table for some reason + _ = SawyerStreamReader.ReadImageTable(br).Table; - return new LocoObject(ObjectType.Region, model, stringTable, imageTable); + return new LocoObject(ObjectType, model, stringTable); } } @@ -81,7 +82,7 @@ public static void Save(Stream stream, LocoObject obj) bw.Write((uint8_t)0); // pad // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table SawyerStreamWriter.WriteStringTable(stream, obj.StringTable); diff --git a/Dat/Loaders/RoadExtraObjectLoader.cs b/Dat/Loaders/RoadExtraObjectLoader.cs index 11b3cf89..9f78c269 100644 --- a/Dat/Loaders/RoadExtraObjectLoader.cs +++ b/Dat/Loaders/RoadExtraObjectLoader.cs @@ -14,13 +14,14 @@ public static class StructSizes public const int DatStructSize = 0x12; } + public static ObjectType ObjectType => ObjectType.RoadExtra; + public static DatObjectType DatObjectType => DatObjectType.RoadExtra; + public static LocoObject Load(Stream stream) { using (var br = new LocoBinaryReader(stream)) { var model = new RoadExtraObject(); - var stringTable = new StringTable(); - var imageTable = new List(); // fixed br.SkipStringId(); // Name offset, not part of object definition @@ -36,15 +37,18 @@ public static LocoObject Load(Stream stream) ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, StructSizes.DatStructSize, nameof(stream.Position)); // string table - stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType.RoadExtra), null); + var stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType), null); // variable // N/A // image table - imageTable = SawyerStreamReader.ReadImageTable(br).Table; + var imageList = SawyerStreamReader.ReadImageTable(br).Table; + + // define groups + var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); - return new LocoObject(ObjectType.RoadExtra, model, stringTable, imageTable); + return new LocoObject(ObjectType, model, stringTable, imageTable); } } diff --git a/Dat/Loaders/RoadObjectLoader.cs b/Dat/Loaders/RoadObjectLoader.cs index 865a1f48..b946337b 100644 --- a/Dat/Loaders/RoadObjectLoader.cs +++ b/Dat/Loaders/RoadObjectLoader.cs @@ -22,6 +22,9 @@ public static class StructSizes public const int Dat = 0x30; } + public static ObjectType ObjectType => ObjectType.Road; + public static DatObjectType DatObjectType => DatObjectType.Road; + public static LocoObject Load(Stream stream) { var initialStreamPosition = stream.Position; @@ -29,8 +32,6 @@ public static LocoObject Load(Stream stream) using (var br = new LocoBinaryReader(stream)) { var model = new RoadObject(); - var stringTable = new StringTable(); - var imageTable = new List(); // fixed br.SkipStringId(); // Name offset, not part of object definition @@ -58,18 +59,21 @@ public static LocoObject Load(Stream stream) br.SkipByte(); // pad_2F, not part of object definition // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table - stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType.Road), null); + var stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType), null); // variable LoadVariable(br, model, numBridges, numStations, numMods, numCompatible); // image table - imageTable = SawyerStreamReader.ReadImageTable(br).Table; + var imageList = SawyerStreamReader.ReadImageTable(br).Table; + + // define groups + var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); - return new LocoObject(ObjectType.Road, model, stringTable, imageTable); + return new LocoObject(ObjectType, model, stringTable, imageTable); } } @@ -114,7 +118,7 @@ public static void Save(Stream stream, LocoObject obj) bw.Write((uint8_t)0); // pad_2F, not part of object definition // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table SawyerStreamWriter.WriteStringTable(stream, obj.StringTable); diff --git a/Dat/Loaders/RoadStationObjectLoader.cs b/Dat/Loaders/RoadStationObjectLoader.cs index 326ff4c3..0f207c3d 100644 --- a/Dat/Loaders/RoadStationObjectLoader.cs +++ b/Dat/Loaders/RoadStationObjectLoader.cs @@ -23,6 +23,9 @@ public static class StructSizes public const int Dat = 0x6E; } + public static ObjectType ObjectType => ObjectType.RoadStation; + public static DatObjectType DatObjectType => DatObjectType.RoadStation; + public static LocoObject Load(Stream stream) { var initialStreamPosition = stream.Position; @@ -30,8 +33,6 @@ public static LocoObject Load(Stream stream) using (var br = new LocoBinaryReader(stream)) { var model = new RoadStationObject(); - var stringTable = new StringTable(); - var imageTable = new List(); // fixed br.SkipStringId(); // Name offset, not part of object definition @@ -53,18 +54,21 @@ public static LocoObject Load(Stream stream) br.SkipPointer(Constants.CargoOffsetBytesSize); // CargoOffsetBytes, not part of object definition // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table - stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType.RoadStation), null); + var stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType), null); // variable LoadVariable(br, model, compatibleRoadObjectCount); // image table - imageTable = SawyerStreamReader.ReadImageTable(br).Table; + var imageList = SawyerStreamReader.ReadImageTable(br).Table; + + // define groups + var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); - return new LocoObject(ObjectType.RoadStation, model, stringTable, imageTable); + return new LocoObject(ObjectType, model, stringTable, imageTable); } } @@ -109,7 +113,7 @@ public static void Save(Stream stream, LocoObject obj) bw.WriteEmptyPointer(Constants.CargoOffsetBytesSize); // CargoOffsetBytes, not part of object definition // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table SawyerStreamWriter.WriteStringTable(stream, obj.StringTable); diff --git a/Dat/Loaders/ScaffoldingObjectLoader.cs b/Dat/Loaders/ScaffoldingObjectLoader.cs index 8b880707..f6ceb7f4 100644 --- a/Dat/Loaders/ScaffoldingObjectLoader.cs +++ b/Dat/Loaders/ScaffoldingObjectLoader.cs @@ -19,6 +19,9 @@ public static class StructSizes public const int Dat = 0x12; } + public static ObjectType ObjectType => ObjectType.Scaffolding; + public static DatObjectType DatObjectType => DatObjectType.Scaffolding; + public static LocoObject Load(Stream stream) { var initialStreamPosition = stream.Position; @@ -26,8 +29,6 @@ public static LocoObject Load(Stream stream) using (var br = new LocoBinaryReader(stream)) { var model = new ScaffoldingObject(); - var stringTable = new StringTable(); - var imageTable = new List(); // fixed br.SkipStringId(); // Name offset, not part of object definition @@ -44,18 +45,21 @@ public static LocoObject Load(Stream stream) } // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table - stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType.Scaffolding), null); + var stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType), null); // variable // N/A // image table - imageTable = SawyerStreamReader.ReadImageTable(br).Table; + var imageList = SawyerStreamReader.ReadImageTable(br).Table; + + // define groups + var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); - return new LocoObject(ObjectType.Scaffolding, model, stringTable, imageTable); + return new LocoObject(ObjectType, model, stringTable, imageTable); } } @@ -80,7 +84,7 @@ public static void Save(Stream stream, LocoObject obj) } // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table SawyerStreamWriter.WriteStringTable(stream, obj.StringTable); diff --git a/Dat/Loaders/ScenarioTextObjectLoader.cs b/Dat/Loaders/ScenarioTextObjectLoader.cs index a62ebe41..0df0cc36 100644 --- a/Dat/Loaders/ScenarioTextObjectLoader.cs +++ b/Dat/Loaders/ScenarioTextObjectLoader.cs @@ -16,6 +16,9 @@ public static class StructSizes public const int Dat = 0x06; } + public static ObjectType ObjectType => ObjectType.ScenarioText; + public static DatObjectType DatObjectType => DatObjectType.ScenarioText; + public static LocoObject Load(Stream stream) { var initialStreamPosition = stream.Position; @@ -23,8 +26,6 @@ public static LocoObject Load(Stream stream) using (var br = new LocoBinaryReader(stream)) { var model = new ScenarioTextObject(); - var stringTable = new StringTable(); - var imageTable = new List(); // fixed br.SkipStringId(); // Name offset, not part of object definition @@ -32,18 +33,19 @@ public static LocoObject Load(Stream stream) br.SkipByte(0x06 - 0x04); // pad, not used // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table - stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType.ScenarioText), null); + var stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType), null); // variable // N/A // image table - imageTable = SawyerStreamReader.ReadImageTable(br).Table; + // N/A but ScenaroText has an empty image table for some reason + _ = SawyerStreamReader.ReadImageTable(br).Table; - return new LocoObject(ObjectType.ScenarioText, model, stringTable, imageTable); + return new LocoObject(ObjectType, model, stringTable, null); } } @@ -58,7 +60,7 @@ public static void Save(Stream stream, LocoObject obj) bw.WriteEmptyBytes(0x06 - 0x04); // padding // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table SawyerStreamWriter.WriteStringTable(stream, obj.StringTable); diff --git a/Dat/Loaders/SnowObjectLoader.cs b/Dat/Loaders/SnowObjectLoader.cs index 42ca9243..8da11fdb 100644 --- a/Dat/Loaders/SnowObjectLoader.cs +++ b/Dat/Loaders/SnowObjectLoader.cs @@ -13,6 +13,9 @@ internal static class StructSizes public const int Dat = 0x06; } + public static ObjectType ObjectType => ObjectType.Snow; + public static DatObjectType DatObjectType => DatObjectType.Snow; + public static LocoObject Load(Stream stream) { var initialStreamPosition = stream.Position; @@ -20,26 +23,27 @@ public static LocoObject Load(Stream stream) using (var br = new LocoBinaryReader(stream)) { var model = new SnowObject(); - var stringTable = new StringTable(); - var imageTable = new List(); // fixed br.SkipStringId(); // Name offset, not part of object definition br.SkipImageId(); // Image offset, not part of object definition // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table - stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType.Snow), null); + var stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType), null); // variable // N/A // image table - imageTable = SawyerStreamReader.ReadImageTable(br).Table; + var imageList = SawyerStreamReader.ReadImageTable(br).Table; + + // define groups + var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); - return new LocoObject(ObjectType.Snow, model, stringTable, imageTable); + return new LocoObject(ObjectType, model, stringTable, imageTable); } } @@ -53,7 +57,7 @@ public static void Save(Stream stream, LocoObject obj) bw.WriteEmptyImageId(); // Image offset, not part of object definition // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table SawyerStreamWriter.WriteStringTable(stream, obj.StringTable); diff --git a/Dat/Loaders/SoundObjectLoader.cs b/Dat/Loaders/SoundObjectLoader.cs index d2cce9b3..b9b689e7 100644 --- a/Dat/Loaders/SoundObjectLoader.cs +++ b/Dat/Loaders/SoundObjectLoader.cs @@ -23,6 +23,9 @@ public static class StructSizes public const int SoundObjectData = 0x1E; } + public static ObjectType ObjectType => ObjectType.Sound; + public static DatObjectType DatObjectType => DatObjectType.Sound; + public static LocoObject Load(Stream stream) { var initialStreamPosition = stream.Position; @@ -30,8 +33,6 @@ public static LocoObject Load(Stream stream) using (var br = new LocoBinaryReader(stream)) { var model = new SoundObject(); - var stringTable = new StringTable(); - var imageTable = new List(); // fixed br.SkipStringId(); // Name offset, not part of object definition @@ -41,10 +42,10 @@ public static LocoObject Load(Stream stream) model.Volume = br.ReadUInt32(); // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table - stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType.Sound), null); + var stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType), null); // variable LoadVariable(br, model); @@ -52,7 +53,7 @@ public static LocoObject Load(Stream stream) // image table // N/A - return new LocoObject(ObjectType.Sound, model, stringTable, imageTable); + return new LocoObject(ObjectType, model, stringTable); } } @@ -86,7 +87,7 @@ public static void Save(Stream stream, LocoObject obj) bw.Write(model.Volume); // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table SawyerStreamWriter.WriteStringTable(stream, obj.StringTable); diff --git a/Dat/Loaders/SteamObjectLoader.cs b/Dat/Loaders/SteamObjectLoader.cs index 7384bbca..e9713cdb 100644 --- a/Dat/Loaders/SteamObjectLoader.cs +++ b/Dat/Loaders/SteamObjectLoader.cs @@ -21,6 +21,9 @@ public static class StructSizes public const int ImageAndHeight = 2; } + public static ObjectType ObjectType => ObjectType.Steam; + public static DatObjectType DatObjectType => DatObjectType.Steam; + public static LocoObject Load(Stream stream) { var initialStreamPosition = stream.Position; @@ -28,8 +31,6 @@ public static LocoObject Load(Stream stream) using (var br = new LocoBinaryReader(stream)) { var model = new SteamObject(); - var stringTable = new StringTable(); - var imageTable = new List(); // fixed br.SkipStringId(); // Name offset, not part of object definition @@ -49,18 +50,21 @@ public static LocoObject Load(Stream stream) br.SkipObjectId(Constants.MaxSoundEffects); // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table - stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType.Steam), null); + var stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType), null); // variable LoadVariable(br, model, numSoundEffects); // image table - imageTable = SawyerStreamReader.ReadImageTable(br).Table; + var imageList = SawyerStreamReader.ReadImageTable(br).Table; + + // define groups + var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); - return new LocoObject(ObjectType.Steam, model, stringTable, imageTable); + return new LocoObject(ObjectType, model, stringTable, imageTable); } } @@ -108,7 +112,7 @@ public static void Save(Stream stream, LocoObject obj) bw.WriteEmptyObjectId(Constants.MaxSoundEffects); // _SoundEffects, not used // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table SawyerStreamWriter.WriteStringTable(stream, obj.StringTable); diff --git a/Dat/Loaders/StreetLightObjectLoader.cs b/Dat/Loaders/StreetLightObjectLoader.cs index 527b4d04..707de69b 100644 --- a/Dat/Loaders/StreetLightObjectLoader.cs +++ b/Dat/Loaders/StreetLightObjectLoader.cs @@ -19,13 +19,14 @@ public static class StructSizes public const int Dat = 0x0C; } + public static ObjectType ObjectType => ObjectType.StreetLight; + public static DatObjectType DatObjectType => DatObjectType.StreetLight; + public static LocoObject Load(Stream stream) { using (var br = new LocoBinaryReader(stream)) { var model = new StreetLightObject(); - var stringTable = new StringTable(); - var imageTable = new List(); // fixed br.SkipStringId(); @@ -35,15 +36,18 @@ public static LocoObject Load(Stream stream) } // string table - stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType.StreetLight), null); + var stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType), null); // variable // N/A // image table - imageTable = SawyerStreamReader.ReadImageTable(br).Table; + var imageList = SawyerStreamReader.ReadImageTable(br).Table; + + // define groups + var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); - return new LocoObject(ObjectType.StreetLight, model, stringTable, imageTable); + return new LocoObject(ObjectType, model, stringTable, imageTable); } } diff --git a/Dat/Loaders/TownNamesObjectLoader.cs b/Dat/Loaders/TownNamesObjectLoader.cs index 74afc913..df26ee79 100644 --- a/Dat/Loaders/TownNamesObjectLoader.cs +++ b/Dat/Loaders/TownNamesObjectLoader.cs @@ -21,13 +21,14 @@ public static class StructSizes public const int Category = 0x1A; } + public static ObjectType ObjectType => ObjectType.TownNames; + public static DatObjectType DatObjectType => DatObjectType.TownNames; + public static LocoObject Load(Stream stream) { using (var br = new LocoBinaryReader(stream)) { var model = new TownNamesObject(); - var stringTable = new StringTable(); - var imageTable = new List(); // fixed br.SkipStringId(); @@ -44,7 +45,7 @@ public static LocoObject Load(Stream stream) } // string table - stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType.TownNames), null); + var stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType), null); // variable // N/A @@ -52,7 +53,7 @@ public static LocoObject Load(Stream stream) // image table // N/A - return new LocoObject(ObjectType.TownNames, model, stringTable, imageTable); + return new LocoObject(ObjectType, model, stringTable); } } diff --git a/Dat/Loaders/TrackExtraObjectLoader.cs b/Dat/Loaders/TrackExtraObjectLoader.cs index 0d556a02..368a7f14 100644 --- a/Dat/Loaders/TrackExtraObjectLoader.cs +++ b/Dat/Loaders/TrackExtraObjectLoader.cs @@ -17,6 +17,9 @@ public static class StructSizes public const int Dat = 0x12; } + public static ObjectType ObjectType => ObjectType.TrackExtra; + public static DatObjectType DatObjectType => DatObjectType.TrackExtra; + public static LocoObject Load(Stream stream) { var initialStreamPosition = stream.Position; @@ -24,8 +27,6 @@ public static LocoObject Load(Stream stream) using (var br = new LocoBinaryReader(stream)) { var model = new TrackExtraObject(); - var stringTable = new StringTable(); - var imageTable = new List(); // fixed br.SkipStringId(); // Name offset, not part of object definition @@ -38,18 +39,21 @@ public static LocoObject Load(Stream stream) br.SkipImageId(); // BaseImageOffset, not part of object definition // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table - stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType.TrackExtra), null); + var stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType), null); // variable // N/A // image table - imageTable = SawyerStreamReader.ReadImageTable(br).Table; + var imageList = SawyerStreamReader.ReadImageTable(br).Table; + + // define groups + var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); - return new LocoObject(ObjectType.TrackExtra, model, stringTable, imageTable); + return new LocoObject(ObjectType, model, stringTable, imageTable); } } @@ -70,7 +74,7 @@ public static void Save(Stream stream, LocoObject obj) bw.WriteEmptyImageId(); // BaseImageOffset, not part of object definition // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table SawyerStreamWriter.WriteStringTable(stream, obj.StringTable); diff --git a/Dat/Loaders/TrackObjectLoader.cs b/Dat/Loaders/TrackObjectLoader.cs index a0449c65..b1ad3c1c 100644 --- a/Dat/Loaders/TrackObjectLoader.cs +++ b/Dat/Loaders/TrackObjectLoader.cs @@ -17,10 +17,8 @@ public static class Constants public const int MaxMods = 4; } - public static class StructSizes - { - public const int Dat = 0x36; - } + public static ObjectType ObjectType => ObjectType.Track; + public static DatObjectType DatObjectType => DatObjectType.Track; public static LocoObject Load(Stream stream) { @@ -29,8 +27,6 @@ public static LocoObject Load(Stream stream) using (var br = new LocoBinaryReader(stream)) { var model = new TrackObject(); - var stringTable = new StringTable(); - var imageTable = new List(); // fixed br.SkipStringId(); // Name offset, not part of object definition @@ -60,10 +56,10 @@ public static LocoObject Load(Stream stream) br.SkipByte(); // pad_35, not part of object definition // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table - stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType.Track), null); + var stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType), null); // variable model.CompatibleTracksAndRoads = br.ReadS5HeaderList(numCompatibleTracksAndRoads); @@ -74,9 +70,12 @@ public static LocoObject Load(Stream stream) model.Stations = br.ReadS5HeaderList(numStations); // image table - imageTable = SawyerStreamReader.ReadImageTable(br).Table; + var imageList = SawyerStreamReader.ReadImageTable(br).Table; + + // define groups + var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); - return new LocoObject(ObjectType.Track, model, stringTable, imageTable); + return new LocoObject(ObjectType, model, stringTable, imageTable); } } @@ -114,7 +113,7 @@ public static void Save(Stream stream, LocoObject obj) bw.Write((uint8_t)0); // pad_35, not part of object definition // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table SawyerStreamWriter.WriteStringTable(stream, obj.StringTable); diff --git a/Dat/Loaders/TrackSignalObjectLoader.cs b/Dat/Loaders/TrackSignalObjectLoader.cs index 58235afb..10d41efa 100644 --- a/Dat/Loaders/TrackSignalObjectLoader.cs +++ b/Dat/Loaders/TrackSignalObjectLoader.cs @@ -19,6 +19,9 @@ internal static class StructSizes public const int Dat = 0x1E; } + public static ObjectType ObjectType => ObjectType.TrackSignal; + public static DatObjectType DatObjectType => DatObjectType.TrackSignal; + public static LocoObject Load(Stream stream) { var initialStreamPosition = stream.Position; @@ -26,8 +29,6 @@ public static LocoObject Load(Stream stream) using (var br = new LocoBinaryReader(stream)) { var model = new TrackSignalObject(); - var stringTable = new StringTable(); - var imageTable = new List(); // fixed br.SkipStringId(); // Name offset, not part of object definition @@ -46,18 +47,21 @@ public static LocoObject Load(Stream stream) model.ObsoleteYear = br.ReadUInt16(); // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table - stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType.TrackSignal), null); + var stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType), null); // variable model.CompatibleTrackObjects = br.ReadS5HeaderList(compatibleTrackCount); // image table - imageTable = SawyerStreamReader.ReadImageTable(br).Table; + var imageList = SawyerStreamReader.ReadImageTable(br).Table; + + // define groups + var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); - return new LocoObject(ObjectType.TrackSignal, model, stringTable, imageTable); + return new LocoObject(ObjectType, model, stringTable, imageTable); } } @@ -84,7 +88,7 @@ public static void Save(Stream stream, LocoObject obj) bw.Write(model.ObsoleteYear); // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table SawyerStreamWriter.WriteStringTable(stream, obj.StringTable); diff --git a/Dat/Loaders/TrackStationObjectLoader.cs b/Dat/Loaders/TrackStationObjectLoader.cs index dc49b9ef..9b07a0ce 100644 --- a/Dat/Loaders/TrackStationObjectLoader.cs +++ b/Dat/Loaders/TrackStationObjectLoader.cs @@ -24,6 +24,9 @@ public static class StructSizes public const int CargoOffset = 6; } + public static ObjectType ObjectType => ObjectType.TrackStation; + public static DatObjectType DatObjectType => DatObjectType.TrackStation; + public static LocoObject Load(Stream stream) { var initialStreamPosition = stream.Position; @@ -31,8 +34,6 @@ public static LocoObject Load(Stream stream) using (var br = new LocoBinaryReader(stream)) { var model = new TrackStationObject(); - var stringTable = new StringTable(); - var imageTable = new List(); // fixed br.SkipStringId(); // Name offset, not part of object definition @@ -55,18 +56,21 @@ public static LocoObject Load(Stream stream) br.SkipPointer(Constants.var_6E_Length); // CargoOffsetBytes, not part of object definition // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table - stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType.TrackStation), null); + var stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType), null); // variable LoadVariable(br, model, compatibleTrackObjectCount); // image table - imageTable = SawyerStreamReader.ReadImageTable(br).Table; + var imageList = SawyerStreamReader.ReadImageTable(br).Table; + + // define groups + var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); - return new LocoObject(ObjectType.TrackStation, model, stringTable, imageTable); + return new LocoObject(ObjectType, model, stringTable, imageTable); } } @@ -120,7 +124,7 @@ public static void Save(Stream stream, LocoObject obj) bw.WriteEmptyPointer(Constants.var_6E_Length); // var_6E, not part of object definition // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table SawyerStreamWriter.WriteStringTable(stream, obj.StringTable); diff --git a/Dat/Loaders/TreeObjectLoader.cs b/Dat/Loaders/TreeObjectLoader.cs index 6a054159..cb18617d 100644 --- a/Dat/Loaders/TreeObjectLoader.cs +++ b/Dat/Loaders/TreeObjectLoader.cs @@ -19,6 +19,9 @@ public static class StructSizes public const int Dat = 0x4C; } + public static ObjectType ObjectType => ObjectType.Tree; + public static DatObjectType DatObjectType => DatObjectType.Tree; + public static LocoObject Load(Stream stream) { var initialStreamPosition = stream.Position; @@ -26,8 +29,6 @@ public static LocoObject Load(Stream stream) using (var br = new LocoBinaryReader(stream)) { var model = new TreeObject(); - var stringTable = new StringTable(); - var imageTable = new List(); // fixed br.SkipStringId(); // Name offset, not part of object definition @@ -52,18 +53,21 @@ public static LocoObject Load(Stream stream) model.DemolishRatingReduction = br.ReadInt16(); // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table - stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType.Tree), null); + var stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType), null); // variable // N/A // image table - imageTable = SawyerStreamReader.ReadImageTable(br).Table; + var imageList = SawyerStreamReader.ReadImageTable(br).Table; + + // define groups + var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); - return new LocoObject(ObjectType.Tree, model, stringTable, imageTable); + return new LocoObject(ObjectType, model, stringTable, imageTable); } } @@ -96,7 +100,7 @@ public static void Save(Stream stream, LocoObject obj) bw.Write(model.DemolishRatingReduction); // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table SawyerStreamWriter.WriteStringTable(stream, obj.StringTable); @@ -152,27 +156,3 @@ public static TreeFlagsUnk Convert(this DatTreeFlagsUnk datTreeFlagsUnk) public static DatTreeFlagsUnk Convert(this TreeFlagsUnk treeFlagsUnk) => (DatTreeFlagsUnk)treeFlagsUnk; } - -[LocoStructSize(0x4C)] -[LocoStructType(DatObjectType.Tree)] -internal record DatTreeObject( - [property: LocoStructOffset(0x00), LocoString, Browsable(false)] string_id Name, - [property: LocoStructOffset(0x02)] uint8_t Clearance, - [property: LocoStructOffset(0x03)] uint8_t Height, - [property: LocoStructOffset(0x04)] uint8_t var_04, - [property: LocoStructOffset(0x05)] uint8_t var_05, - [property: LocoStructOffset(0x06)] uint8_t NumRotations, - [property: LocoStructOffset(0x07)] uint8_t NumGrowthStages, - [property: LocoStructOffset(0x08)] DatTreeObjectFlags Flags, - [property: LocoStructOffset(0x0A), LocoArrayLength(6), Browsable(false)] image_id[] Sprites, - [property: LocoStructOffset(0x22), LocoArrayLength(6), Browsable(false)] image_id[] SnowSprites, - [property: LocoStructOffset(0x3A), Browsable(false)] uint16_t ShadowImageOffset, - [property: LocoStructOffset(0x3C)] DatTreeFlagsUnk var_3C, // something with images - [property: LocoStructOffset(0x3D)] uint8_t SeasonState, - [property: LocoStructOffset(0x3E)] uint8_t Season, - [property: LocoStructOffset(0x3F)] uint8_t CostIndex, - [property: LocoStructOffset(0x40)] int16_t BuildCostFactor, - [property: LocoStructOffset(0x42)] int16_t ClearCostFactor, - [property: LocoStructOffset(0x44)] uint32_t Colours, - [property: LocoStructOffset(0x48)] int16_t Rating, - [property: LocoStructOffset(0x4A)] int16_t DemolishRatingReduction); diff --git a/Dat/Loaders/TunnelObjectLoader.cs b/Dat/Loaders/TunnelObjectLoader.cs index fca34c11..a7418a76 100644 --- a/Dat/Loaders/TunnelObjectLoader.cs +++ b/Dat/Loaders/TunnelObjectLoader.cs @@ -16,6 +16,9 @@ public static class StructSizes public const int Dat = 0x06; } + public static ObjectType ObjectType => ObjectType.Tunnel; + public static DatObjectType DatObjectType => DatObjectType.Tunnel; + public static LocoObject Load(Stream stream) { var initialStreamPosition = stream.Position; @@ -23,26 +26,27 @@ public static LocoObject Load(Stream stream) using (var br = new LocoBinaryReader(stream)) { var model = new TunnelObject(); - var stringTable = new StringTable(); - var imageTable = new List(); // fixed br.SkipStringId(); // Name offset, not part of object definition br.SkipImageId(); // Image offset, not part of object definition // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table - stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType.Tunnel), null); + var stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType), null); // variable // N/A // image table - imageTable = SawyerStreamReader.ReadImageTable(br).Table; + var imageList = SawyerStreamReader.ReadImageTable(br).Table; + + // define groups + var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); - return new LocoObject(ObjectType.Tunnel, model, stringTable, imageTable); + return new LocoObject(ObjectType, model, stringTable, imageTable); } } @@ -56,7 +60,7 @@ public static void Save(Stream stream, LocoObject obj) bw.WriteEmptyImageId(); // Image offset, not part of object definition // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table SawyerStreamWriter.WriteStringTable(stream, obj.StringTable); diff --git a/Dat/Loaders/Vehicle/VehicleObjectLoader.cs b/Dat/Loaders/Vehicle/VehicleObjectLoader.cs index db7d6948..6bbe900e 100644 --- a/Dat/Loaders/Vehicle/VehicleObjectLoader.cs +++ b/Dat/Loaders/Vehicle/VehicleObjectLoader.cs @@ -28,13 +28,16 @@ public static class Constants public static class StructSizes { - public const int Dat = 0x15E; + //public const int Dat = 0x15E; public const int SoundData = 0x1B; public const int FrictionSound = 0x0B; public const int SimpleMotorSound = 0x11; public const int GearboxMotorSound = 0x1B; } + public static ObjectType ObjectType => ObjectType.Vehicle; + public static DatObjectType DatObjectType => DatObjectType.Vehicle; + public static LocoObject Load(Stream stream) { var initialStreamPosition = stream.Position; @@ -42,25 +45,26 @@ public static LocoObject Load(Stream stream) using (var br = new LocoBinaryReader(stream)) { var model = new VehicleObject(); - var stringTable = new StringTable(); - var imageTable = new List(); // fixed LoadFixed(br, model, out var numRequiredTrackExtras, out var numCompatibleVehicles, out var numStartSounds); // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table - stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType.Vehicle), null); + var stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType), null); // variable LoadVariable(br, model, numRequiredTrackExtras, numCompatibleVehicles, numStartSounds); // image table - imageTable = SawyerStreamReader.ReadImageTable(br).Table; + var imageList = SawyerStreamReader.ReadImageTable(br).Table; + + // define groups + var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); - return new LocoObject(ObjectType.Vehicle, model, stringTable, imageTable); + return new LocoObject(ObjectType, model, stringTable, imageTable); } } @@ -277,7 +281,7 @@ public static void Save(Stream stream, LocoObject obj) bw.WriteEmptyBytes(Constants.MaxStartSounds * 1); // StartSounds, not part of object // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table SawyerStreamWriter.WriteStringTable(stream, obj.StringTable); diff --git a/Dat/Loaders/WallObjectLoader.cs b/Dat/Loaders/WallObjectLoader.cs index b27e5ed9..a150e2c1 100644 --- a/Dat/Loaders/WallObjectLoader.cs +++ b/Dat/Loaders/WallObjectLoader.cs @@ -17,6 +17,9 @@ internal static class StructSizes public const int Dat = 0x0A; } + public static ObjectType ObjectType => ObjectType.Wall; + public static DatObjectType DatObjectType => DatObjectType.Wall; + public static LocoObject Load(Stream stream) { var initialStreamPosition = stream.Position; @@ -24,8 +27,6 @@ public static LocoObject Load(Stream stream) using (var br = new LocoBinaryReader(stream)) { var model = new WallObject(); - var stringTable = new StringTable(); - var imageTable = new List(); // fixed br.SkipStringId(); // Name offset, not part of object definition @@ -36,18 +37,21 @@ public static LocoObject Load(Stream stream) model.Flags2 = ((DatWallObjectFlags2)br.ReadByte()).Convert(); // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table - stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType.Wall), null); + var stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType), null); // variable // N/A // image table - imageTable = SawyerStreamReader.ReadImageTable(br).Table; + var imageList = SawyerStreamReader.ReadImageTable(br).Table; + + // define groups + var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); - return new LocoObject(ObjectType.Wall, model, stringTable, imageTable); + return new LocoObject(ObjectType, model, stringTable, imageTable); } } @@ -66,7 +70,7 @@ public static void Save(Stream stream, LocoObject obj) bw.Write((uint8_t)model.Flags2.Convert()); // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table SawyerStreamWriter.WriteStringTable(stream, obj.StringTable); diff --git a/Dat/Loaders/WaterObjectLoader.cs b/Dat/Loaders/WaterObjectLoader.cs index 4b73ed06..10e9fa4b 100644 --- a/Dat/Loaders/WaterObjectLoader.cs +++ b/Dat/Loaders/WaterObjectLoader.cs @@ -16,6 +16,9 @@ public static class StructSizes public const int Dat = 0x0E; } + public static ObjectType ObjectType => ObjectType.Water; + public static DatObjectType DatObjectType => DatObjectType.Water; + public static LocoObject Load(Stream stream) { var initialStreamPosition = stream.Position; @@ -23,8 +26,6 @@ public static LocoObject Load(Stream stream) using (var br = new LocoBinaryReader(stream)) { var model = new WaterObject(); - var stringTable = new StringTable(); - var imageTable = new List(); // fixed br.SkipStringId(); // Name offset, not part of object definition @@ -35,18 +36,21 @@ public static LocoObject Load(Stream stream) br.SkipImageId(); // MapPixelImage, not part of object definition // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table - stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType.Water), null); + var stringTable = SawyerStreamReader.ReadStringTableStream(stream, ObjectAttributes.StringTable(DatObjectType), null); // variable // N/A // image table - imageTable = SawyerStreamReader.ReadImageTable(br).Table; + var imageList = SawyerStreamReader.ReadImageTable(br).Table; + + // define groups + var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); - return new LocoObject(ObjectType.Water, model, stringTable, imageTable); + return new LocoObject(ObjectType, model, stringTable, imageTable); } } @@ -65,7 +69,7 @@ public static void Save(Stream stream, LocoObject obj) bw.WriteEmptyImageId(); // MapPixelImage, not part of object definition // sanity check - ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + StructSizes.Dat, nameof(stream.Position)); + ArgumentOutOfRangeException.ThrowIfNotEqual(stream.Position, initialStreamPosition + ObjectAttributes.StructSize(DatObjectType), nameof(stream.Position)); // string table SawyerStreamWriter.WriteStringTable(stream, obj.StringTable); @@ -78,17 +82,3 @@ public static void Save(Stream stream, LocoObject obj) } } } - -[LocoStructSize(0x0E)] -[LocoStructType(DatObjectType.Water)] -internal record DatWaterObject( - [property: LocoStructOffset(0x00), LocoString, Browsable(false)] string_id Name, - [property: LocoStructOffset(0x02)] uint8_t CostIndex, - [property: LocoStructOffset(0x03), LocoPropertyMaybeUnused] uint8_t var_03, - [property: LocoStructOffset(0x04)] int16_t CostFactor, - [property: LocoStructOffset(0x06), Browsable(false)] image_id Image, - [property: LocoStructOffset(0x0A), Browsable(false)] image_id MapPixelImage - ) -{ - -} diff --git a/Dat/Types/G1Dat.cs b/Dat/Types/G1Dat.cs index ac5fee99..2327a385 100644 --- a/Dat/Types/G1Dat.cs +++ b/Dat/Types/G1Dat.cs @@ -1,6 +1,7 @@ using Definitions.ObjectModels; using Definitions.ObjectModels.Types; using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; namespace Dat.Types; @@ -23,7 +24,7 @@ public G1Dat(G1Header g1Header, List graphicsElements) }; } - public bool TryGetImageName(int id, out string? value) + public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) => id < 3550 ? BaseImageIdNameMap.TryGetValue(id, out value) : (IsSteamG1 ? SteamImageIdNameMap : GoGImageIdNameMap).TryGetValue(id, out value); diff --git a/Definitions/ObjectModels/IImageTableNameProvider.cs b/Definitions/ObjectModels/IImageTableNameProvider.cs index 2491458d..4bf89b1c 100644 --- a/Definitions/ObjectModels/IImageTableNameProvider.cs +++ b/Definitions/ObjectModels/IImageTableNameProvider.cs @@ -1,4 +1,5 @@ using Definitions.ObjectModels.Types; +using System.Diagnostics.CodeAnalysis; namespace Definitions.ObjectModels; @@ -9,12 +10,12 @@ public interface IHasGraphicsElements public interface IImageTableNameProvider { - public bool TryGetImageName(int id, out string? value); + public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value); } public class DefaultImageTableNameProvider : IImageTableNameProvider { - public bool TryGetImageName(int id, out string? value) + public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) { value = GetImageName(id); return true; diff --git a/Definitions/ObjectModels/LocoObject.cs b/Definitions/ObjectModels/LocoObject.cs index 2188d6ae..2bcfc8d5 100644 --- a/Definitions/ObjectModels/LocoObject.cs +++ b/Definitions/ObjectModels/LocoObject.cs @@ -4,11 +4,7 @@ namespace Definitions.ObjectModels; public class LocoObject { - public LocoObject(ObjectType objectType, ILocoStruct obj, StringTable stringTable, List graphicsElements) - : this(objectType, obj, stringTable, new ImageTable { Groups = [("All", graphicsElements)] }) - { } - - public LocoObject(ObjectType objectType, ILocoStruct obj, StringTable stringTable, ImageTable imageTable) + public LocoObject(ObjectType objectType, ILocoStruct obj, StringTable stringTable, ImageTable? imageTable = null) { ObjectType = objectType; Object = obj; @@ -19,5 +15,18 @@ public LocoObject(ObjectType objectType, ILocoStruct obj, StringTable stringTabl public ObjectType ObjectType { get; init; } public ILocoStruct Object { get; set; } public StringTable StringTable { get; set; } - public ImageTable ImageTable { get; set; } + + public ImageTable? ImageTable { get; set; } } + +//public class LocoObjectWithGraphics : LocoObject +//{ +// public LocoObjectWithGraphics(ObjectType objectType, ILocoStruct obj, StringTable stringTable, ImageTable imageTable) +// : base(objectType, obj, stringTable) +// { +// ImageTable = imageTable; +// } + +// public ImageTable ImageTable { get; set; } +//} + diff --git a/Definitions/ObjectModels/Objects/Airport/AirportObject.cs b/Definitions/ObjectModels/Objects/Airport/AirportObject.cs index c97df4f9..f44526e1 100644 --- a/Definitions/ObjectModels/Objects/Airport/AirportObject.cs +++ b/Definitions/ObjectModels/Objects/Airport/AirportObject.cs @@ -1,8 +1,9 @@ using Definitions.ObjectModels.Objects.Common; +using System.Diagnostics.CodeAnalysis; namespace Definitions.ObjectModels.Objects.Airport; -public class AirportObject : ILocoStruct, IHasBuildingComponents +public class AirportObject : ILocoStruct, IHasBuildingComponents, IImageTableNameProvider { public int16_t BuildCostFactor { get; set; } public int16_t SellCostFactor { get; set; } @@ -43,4 +44,7 @@ public bool Validate() return BuildCostFactor > 0; } + + public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) + => throw new NotImplementedException(); } diff --git a/Definitions/ObjectModels/Objects/Bridge/BridgeObject.cs b/Definitions/ObjectModels/Objects/Bridge/BridgeObject.cs index 010155f3..0a751259 100644 --- a/Definitions/ObjectModels/Objects/Bridge/BridgeObject.cs +++ b/Definitions/ObjectModels/Objects/Bridge/BridgeObject.cs @@ -1,8 +1,9 @@ using Definitions.ObjectModels.Types; +using System.Diagnostics.CodeAnalysis; namespace Definitions.ObjectModels.Objects.Bridge; -public class BridgeObject : ILocoStruct +public class BridgeObject : ILocoStruct, IImageTableNameProvider { public BridgeObjectFlags Flags { get; set; } public uint16_t ClearHeight { get; set; } @@ -66,4 +67,7 @@ public bool Validate() return true; } + + public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) + => throw new NotImplementedException(); } diff --git a/Definitions/ObjectModels/Objects/Building/BuildingObject.cs b/Definitions/ObjectModels/Objects/Building/BuildingObject.cs index e2c3c838..6b3040e7 100644 --- a/Definitions/ObjectModels/Objects/Building/BuildingObject.cs +++ b/Definitions/ObjectModels/Objects/Building/BuildingObject.cs @@ -1,5 +1,6 @@ using Definitions.ObjectModels.Objects.Common; using Definitions.ObjectModels.Types; +using System.Diagnostics.CodeAnalysis; namespace Definitions.ObjectModels.Objects.Building; @@ -36,7 +37,7 @@ public bool Validate() => ProducedQuantity.Count == 2 && BuildingComponents.Validate(); - public bool TryGetImageName(int id, out string? value) + public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) { var direction = (CardinalDirection)(id % 4); var level = id / 4; diff --git a/Definitions/ObjectModels/Objects/Cargo/CargoObject.cs b/Definitions/ObjectModels/Objects/Cargo/CargoObject.cs index e5f7aee2..1554d70d 100644 --- a/Definitions/ObjectModels/Objects/Cargo/CargoObject.cs +++ b/Definitions/ObjectModels/Objects/Cargo/CargoObject.cs @@ -1,3 +1,5 @@ +using System.Diagnostics.CodeAnalysis; + namespace Definitions.ObjectModels.Objects.Cargo; public class CargoObject : ILocoStruct, IImageTableNameProvider @@ -20,7 +22,7 @@ public bool Validate() => var_02 <= 3840 && CargoTransferTime != 0; - public bool TryGetImageName(int id, out string? value) + public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) { value = id == 0 ? "kInlineSprite" diff --git a/Definitions/ObjectModels/Objects/CliffEdge/CliffEdgeObject.cs b/Definitions/ObjectModels/Objects/CliffEdge/CliffEdgeObject.cs index 16f2e052..0529d414 100644 --- a/Definitions/ObjectModels/Objects/CliffEdge/CliffEdgeObject.cs +++ b/Definitions/ObjectModels/Objects/CliffEdge/CliffEdgeObject.cs @@ -1,10 +1,12 @@ +using System.Diagnostics.CodeAnalysis; + namespace Definitions.ObjectModels.Objects.CliffEdge; public class CliffEdgeObject : ILocoStruct, IImageTableNameProvider { public bool Validate() => true; - public bool TryGetImageName(int id, out string? value) + public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) { if (id is >= 0 and <= 63) { diff --git a/Definitions/ObjectModels/Objects/Climate/ClimateObject.cs b/Definitions/ObjectModels/Objects/Climate/ClimateObject.cs index f1ede564..16a52763 100644 --- a/Definitions/ObjectModels/Objects/Climate/ClimateObject.cs +++ b/Definitions/ObjectModels/Objects/Climate/ClimateObject.cs @@ -1,6 +1,8 @@ +using System.Diagnostics.CodeAnalysis; + namespace Definitions.ObjectModels.Objects.Climate; -public class ClimateObject : ILocoStruct +public class ClimateObject : ILocoStruct, IImageTableNameProvider { public uint8_t FirstSeason { get; set; } public uint8_t SeasonLength1 { get; set; } @@ -13,4 +15,6 @@ public class ClimateObject : ILocoStruct public bool Validate() => WinterSnowLine <= SummerSnowLine && FirstSeason < 4; + public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) + => throw new NotImplementedException(); } diff --git a/Definitions/ObjectModels/Objects/Competitor/CompetitorObject.cs b/Definitions/ObjectModels/Objects/Competitor/CompetitorObject.cs index c9239abd..e0b700d9 100644 --- a/Definitions/ObjectModels/Objects/Competitor/CompetitorObject.cs +++ b/Definitions/ObjectModels/Objects/Competitor/CompetitorObject.cs @@ -1,3 +1,5 @@ +using System.Diagnostics.CodeAnalysis; + namespace Definitions.ObjectModels.Objects.Competitor; public class CompetitorObject : ILocoStruct, IImageTableNameProvider @@ -30,7 +32,7 @@ public bool Validate() return Competitiveness is >= 1 and <= 9; } - public bool TryGetImageName(int id, out string? value) + public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) => ImageIdNameMap.TryGetValue(id, out value); public static Dictionary ImageIdNameMap = new() diff --git a/Definitions/ObjectModels/Objects/Currency/CurrencyObject.cs b/Definitions/ObjectModels/Objects/Currency/CurrencyObject.cs index 6b303d13..c986d0f3 100644 --- a/Definitions/ObjectModels/Objects/Currency/CurrencyObject.cs +++ b/Definitions/ObjectModels/Objects/Currency/CurrencyObject.cs @@ -1,6 +1,8 @@ +using System.Diagnostics.CodeAnalysis; + namespace Definitions.ObjectModels.Objects.Currency; -public class CurrencyObject : ILocoStruct +public class CurrencyObject : ILocoStruct, IImageTableNameProvider { public uint8_t Separator { get; set; } public uint8_t Factor { get; set; } @@ -19,4 +21,7 @@ public bool Validate() return true; } + + public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) + => throw new NotImplementedException(); } diff --git a/Definitions/ObjectModels/Objects/Dock/DockObject.cs b/Definitions/ObjectModels/Objects/Dock/DockObject.cs index 2fd5d9fc..0435d79d 100644 --- a/Definitions/ObjectModels/Objects/Dock/DockObject.cs +++ b/Definitions/ObjectModels/Objects/Dock/DockObject.cs @@ -1,9 +1,10 @@ using Definitions.ObjectModels.Objects.Common; using Definitions.ObjectModels.Types; +using System.Diagnostics.CodeAnalysis; namespace Definitions.ObjectModels.Objects.Dock; -public class DockObject : ILocoStruct, IHasBuildingComponents +public class DockObject : ILocoStruct, IHasBuildingComponents, IImageTableNameProvider { public int16_t BuildCostFactor { get; set; } public int16_t SellCostFactor { get; set; } @@ -35,4 +36,7 @@ public bool Validate() return BuildCostFactor > 0; } + + public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) + => throw new NotImplementedException(); } diff --git a/Definitions/ObjectModels/Objects/HillShapes/HillShapesObject.cs b/Definitions/ObjectModels/Objects/HillShapes/HillShapesObject.cs index babd34e3..3150305c 100644 --- a/Definitions/ObjectModels/Objects/HillShapes/HillShapesObject.cs +++ b/Definitions/ObjectModels/Objects/HillShapes/HillShapesObject.cs @@ -1,3 +1,5 @@ +using System.Diagnostics.CodeAnalysis; + namespace Definitions.ObjectModels.Objects.HillShape; public class HillShapesObject : ILocoStruct, IImageTableNameProvider @@ -8,7 +10,7 @@ public class HillShapesObject : ILocoStruct, IImageTableNameProvider public bool Validate() => true; - public bool TryGetImageName(int id, out string? value) + public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) => ImageIdNameMap.TryGetValue(id, out value); public static Dictionary ImageIdNameMap = new() diff --git a/Definitions/ObjectModels/Objects/Industry/IndustryObject.cs b/Definitions/ObjectModels/Objects/Industry/IndustryObject.cs index aa9da922..0b255b6b 100644 --- a/Definitions/ObjectModels/Objects/Industry/IndustryObject.cs +++ b/Definitions/ObjectModels/Objects/Industry/IndustryObject.cs @@ -1,9 +1,10 @@ using Definitions.ObjectModels.Objects.Common; using Definitions.ObjectModels.Types; +using System.Diagnostics.CodeAnalysis; namespace Definitions.ObjectModels.Objects.Industry; -public class IndustryObject : ILocoStruct, IHasBuildingComponents +public class IndustryObject : ILocoStruct, IHasBuildingComponents, IImageTableNameProvider { public uint32_t FarmImagesPerGrowthStage { get; set; } public uint8_t MinNumBuildings { get; set; } @@ -95,4 +96,7 @@ public bool Validate() return InitialProductionRate[1].Min <= 100; } + + public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) + => throw new NotImplementedException(); } diff --git a/Definitions/ObjectModels/Objects/InterfaceSkin/InterfaceSkinObject.cs b/Definitions/ObjectModels/Objects/InterfaceSkin/InterfaceSkinObject.cs index 9471858c..56b235dd 100644 --- a/Definitions/ObjectModels/Objects/InterfaceSkin/InterfaceSkinObject.cs +++ b/Definitions/ObjectModels/Objects/InterfaceSkin/InterfaceSkinObject.cs @@ -1,4 +1,5 @@ using Definitions.ObjectModels.Types; +using System.Diagnostics.CodeAnalysis; namespace Definitions.ObjectModels.Objects.InterfaceSkin; @@ -25,7 +26,7 @@ public class InterfaceSkinObject : ILocoStruct, IImageTableNameProvider public bool Validate() => true; - public bool TryGetImageName(int id, out string? value) + public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) => ImageIdNameMap.TryGetValue(id, out value); public static Dictionary ImageIdNameMap = new() diff --git a/Definitions/ObjectModels/Objects/Land/LandObject.cs b/Definitions/ObjectModels/Objects/Land/LandObject.cs index 33e3d4a2..5594f45a 100644 --- a/Definitions/ObjectModels/Objects/Land/LandObject.cs +++ b/Definitions/ObjectModels/Objects/Land/LandObject.cs @@ -1,4 +1,5 @@ using Definitions.ObjectModels.Types; +using System.Diagnostics.CodeAnalysis; namespace Definitions.ObjectModels.Objects.Land; @@ -42,7 +43,7 @@ public bool Validate() return NumImageAngles is 1 or 2 or 4; } - public bool TryGetImageName(int id, out string? value) + public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) => ImageIdNameMap.TryGetValue(id, out value); public static readonly Dictionary ImageIdNameMap = new() diff --git a/Definitions/ObjectModels/Objects/LevelCrossing/LevelCrossingObject.cs b/Definitions/ObjectModels/Objects/LevelCrossing/LevelCrossingObject.cs index 66cf30d8..7b84e757 100644 --- a/Definitions/ObjectModels/Objects/LevelCrossing/LevelCrossingObject.cs +++ b/Definitions/ObjectModels/Objects/LevelCrossing/LevelCrossingObject.cs @@ -1,6 +1,8 @@ +using System.Diagnostics.CodeAnalysis; + namespace Definitions.ObjectModels.Objects.LevelCrossing; -public class LevelCrossingObject : ILocoStruct +public class LevelCrossingObject : ILocoStruct, IImageTableNameProvider { public int16_t CostFactor { get; set; } public int16_t SellCostFactor { get; set; } @@ -29,4 +31,7 @@ public bool Validate() _ => false, }; } + + public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) + => throw new NotImplementedException(); } diff --git a/Definitions/ObjectModels/Objects/Road/RoadObject.cs b/Definitions/ObjectModels/Objects/Road/RoadObject.cs index 86f63754..82dd3243 100644 --- a/Definitions/ObjectModels/Objects/Road/RoadObject.cs +++ b/Definitions/ObjectModels/Objects/Road/RoadObject.cs @@ -1,4 +1,5 @@ using Definitions.ObjectModels.Types; +using System.Diagnostics.CodeAnalysis; namespace Definitions.ObjectModels.Objects.Road; @@ -62,9 +63,15 @@ public bool Validate() return true; } - public bool TryGetImageName(int id, out string? value) + public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) { - if (id is >= 0 and <= 33) + if (id is >= 0 and <= 31) + { + value = $"uiPreviewImage{id}"; + return true; + } + + if (id is >= 32 and <= 33) { return ImageIdNameMap.TryGetValue(id, out value); } @@ -81,39 +88,6 @@ public bool TryGetImageName(int id, out string? value) public static Dictionary ImageIdNameMap = new() { - { 0, "uiPreviewImage0" }, - { 1, "uiPreviewImage1" }, - { 2, "uiPreviewImage2" }, - { 3, "uiPreviewImage3" }, - { 4, "uiPreviewImage4" }, - { 5, "uiPreviewImage5" }, - { 6, "uiPreviewImage6" }, - { 7, "uiPreviewImage7" }, - { 8, "uiPreviewImage8" }, - { 9, "uiPreviewImage9" }, - { 10, "uiPreviewImage10" }, - { 11, "uiPreviewImage11" }, - { 12, "uiPreviewImage12" }, - { 13, "uiPreviewImage13" }, - { 14, "uiPreviewImage14" }, - { 15, "uiPreviewImage15" }, - { 16, "uiPreviewImage0" }, - { 17, "uiPreviewImage1" }, - { 18, "uiPreviewImage2" }, - { 19, "uiPreviewImage3" }, - { 20, "uiPreviewImage4" }, - { 21, "uiPreviewImage5" }, - { 22, "uiPreviewImage6" }, - { 23, "uiPreviewImage7" }, - { 24, "uiPreviewImage8" }, - { 25, "uiPreviewImage9" }, - { 26, "uiPreviewImage10" }, - { 27, "uiPreviewImage11" }, - { 28, "uiPreviewImage12" }, - { 29, "uiPreviewImage13" }, - { 30, "uiPreviewImage14" }, - { 31, "uiPreviewImage15" }, - { 32, "uiPickupFromTrack" }, { 33, "uiPlaceOnTrack" }, }; diff --git a/Definitions/ObjectModels/Objects/RoadExtra/RoadExtraObject.cs b/Definitions/ObjectModels/Objects/RoadExtra/RoadExtraObject.cs index e0537de3..d5bf0877 100644 --- a/Definitions/ObjectModels/Objects/RoadExtra/RoadExtraObject.cs +++ b/Definitions/ObjectModels/Objects/RoadExtra/RoadExtraObject.cs @@ -1,8 +1,9 @@ using Definitions.ObjectModels.Objects.Road; +using System.Diagnostics.CodeAnalysis; namespace Definitions.ObjectModels.Objects.RoadExtra; -public class RoadExtraObject : ILocoStruct +public class RoadExtraObject : ILocoStruct, IImageTableNameProvider { public RoadTraitFlags RoadPieces { get; set; } = RoadTraitFlags.None; public uint8_t PaintStyle { get; set; } @@ -30,4 +31,7 @@ public bool Validate() return BuildCostFactor > 0; } + + public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) + => throw new NotImplementedException(); } diff --git a/Definitions/ObjectModels/Objects/RoadStation/RoadStationObject.cs b/Definitions/ObjectModels/Objects/RoadStation/RoadStationObject.cs index d05da515..0f54430d 100644 --- a/Definitions/ObjectModels/Objects/RoadStation/RoadStationObject.cs +++ b/Definitions/ObjectModels/Objects/RoadStation/RoadStationObject.cs @@ -1,6 +1,7 @@ using Definitions.ObjectModels.Objects.Road; using Definitions.ObjectModels.Objects.Shared; using Definitions.ObjectModels.Types; +using System.Diagnostics.CodeAnalysis; namespace Definitions.ObjectModels.Objects.RoadStation; @@ -58,7 +59,7 @@ public bool Validate() return true; } - public bool TryGetImageName(int id, out string? value) + public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) => ImageIdNameMap.TryGetValue(id, out value); public static Dictionary ImageIdNameMap = new() diff --git a/Definitions/ObjectModels/Objects/Scaffolding/ScaffoldingObject.cs b/Definitions/ObjectModels/Objects/Scaffolding/ScaffoldingObject.cs index bf282e94..6cf28762 100644 --- a/Definitions/ObjectModels/Objects/Scaffolding/ScaffoldingObject.cs +++ b/Definitions/ObjectModels/Objects/Scaffolding/ScaffoldingObject.cs @@ -1,12 +1,14 @@ +using System.Diagnostics.CodeAnalysis; + namespace Definitions.ObjectModels.Objects.Scaffolding; -public class ScaffoldingObject : ILocoStruct +public class ScaffoldingObject : ILocoStruct, IImageTableNameProvider { public List SegmentHeights { get; set; } = []; public List RoofHeights { get; set; } = []; public bool Validate() => true; - public bool TryGetImageName(int id, out string? value) + public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) => ImageIdNameMap.TryGetValue(id, out value); public static Dictionary ImageIdNameMap = new() diff --git a/Definitions/ObjectModels/Objects/ScenarioText/ScenarioTextObject.cs b/Definitions/ObjectModels/Objects/ScenarioText/ScenarioTextObject.cs index 74a89a09..861f557f 100644 --- a/Definitions/ObjectModels/Objects/ScenarioText/ScenarioTextObject.cs +++ b/Definitions/ObjectModels/Objects/ScenarioText/ScenarioTextObject.cs @@ -1,5 +1,8 @@ +using System.Diagnostics.CodeAnalysis; + namespace Definitions.ObjectModels.Objects.ScenarioText; public class ScenarioTextObject : ILocoStruct { - public bool Validate() => true; + public bool Validate() + => true; } diff --git a/Definitions/ObjectModels/Objects/Snow/SnowObject.cs b/Definitions/ObjectModels/Objects/Snow/SnowObject.cs index b9e6023f..c80429ad 100644 --- a/Definitions/ObjectModels/Objects/Snow/SnowObject.cs +++ b/Definitions/ObjectModels/Objects/Snow/SnowObject.cs @@ -1,9 +1,11 @@ +using System.Diagnostics.CodeAnalysis; + namespace Definitions.ObjectModels.Objects.Snow; public class SnowObject : ILocoStruct, IImageTableNameProvider { public bool Validate() => true; - public bool TryGetImageName(int id, out string? value) + public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) => ImageIdNameMap.TryGetValue(id, out value); public static Dictionary ImageIdNameMap = new() diff --git a/Definitions/ObjectModels/Objects/Steam/SteamObject.cs b/Definitions/ObjectModels/Objects/Steam/SteamObject.cs index a1ae6023..ab0693fc 100644 --- a/Definitions/ObjectModels/Objects/Steam/SteamObject.cs +++ b/Definitions/ObjectModels/Objects/Steam/SteamObject.cs @@ -1,8 +1,9 @@ using Definitions.ObjectModels.Types; +using System.Diagnostics.CodeAnalysis; namespace Definitions.ObjectModels.Objects.Steam; -public class SteamObject : ILocoStruct +public class SteamObject : ILocoStruct, IImageTableNameProvider { public uint8_t NumStationaryTicks { get; set; } public uint8_t SpriteWidth { get; set; } @@ -17,4 +18,7 @@ public class SteamObject : ILocoStruct public bool Validate() => true; + + public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) + => throw new NotImplementedException(); } diff --git a/Definitions/ObjectModels/Objects/StreetLight/StreetLightObject.cs b/Definitions/ObjectModels/Objects/StreetLight/StreetLightObject.cs index aced3133..90247e58 100644 --- a/Definitions/ObjectModels/Objects/StreetLight/StreetLightObject.cs +++ b/Definitions/ObjectModels/Objects/StreetLight/StreetLightObject.cs @@ -1,3 +1,5 @@ +using System.Diagnostics.CodeAnalysis; + namespace Definitions.ObjectModels.Objects.Streetlight; public class StreetLightObject : ILocoStruct, IImageTableNameProvider { @@ -6,7 +8,7 @@ public class StreetLightObject : ILocoStruct, IImageTableNameProvider public bool Validate() => true; - public bool TryGetImageName(int id, out string? value) + public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) => ImageIdNameMap.TryGetValue(id, out value); public static Dictionary ImageIdNameMap = new() diff --git a/Definitions/ObjectModels/Objects/Track/TrackObject.cs b/Definitions/ObjectModels/Objects/Track/TrackObject.cs index 9ebbbb14..da751fcb 100644 --- a/Definitions/ObjectModels/Objects/Track/TrackObject.cs +++ b/Definitions/ObjectModels/Objects/Track/TrackObject.cs @@ -1,4 +1,5 @@ using Definitions.ObjectModels.Types; +using System.Diagnostics.CodeAnalysis; namespace Definitions.ObjectModels.Objects.Track; @@ -64,7 +65,7 @@ public bool Validate() return Stations.Count <= 7; } - public bool TryGetImageName(int id, out string? value) + public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) => ImageIdNameMap.TryGetValue(id, out value); // taken from OpenLoco TrackObject.h diff --git a/Definitions/ObjectModels/Objects/TrackExtra/TrackExtraObject.cs b/Definitions/ObjectModels/Objects/TrackExtra/TrackExtraObject.cs index 1c22a6f6..72b3fa05 100644 --- a/Definitions/ObjectModels/Objects/TrackExtra/TrackExtraObject.cs +++ b/Definitions/ObjectModels/Objects/TrackExtra/TrackExtraObject.cs @@ -1,4 +1,5 @@ using Definitions.ObjectModels.Objects.Track; +using System.Diagnostics.CodeAnalysis; namespace Definitions.ObjectModels.Objects.TrackExtra; @@ -31,7 +32,7 @@ public bool Validate() return BuildCostFactor > 0; } - public bool TryGetImageName(int id, out string? value) + public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) => ImageIdNameMap.TryGetValue(id - 8, out value); // taken from OpenLoco TrackExtraObject.h diff --git a/Definitions/ObjectModels/Objects/TrackSignal/TrackSignalObject.cs b/Definitions/ObjectModels/Objects/TrackSignal/TrackSignalObject.cs index 7d4d9607..3dfc1e8f 100644 --- a/Definitions/ObjectModels/Objects/TrackSignal/TrackSignalObject.cs +++ b/Definitions/ObjectModels/Objects/TrackSignal/TrackSignalObject.cs @@ -1,4 +1,5 @@ using Definitions.ObjectModels.Types; +using System.Diagnostics.CodeAnalysis; namespace Definitions.ObjectModels.Objects.TrackSignal; @@ -58,7 +59,7 @@ public bool Validate() return true; } - public bool TryGetImageName(int id, out string? value) + public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) => ImageIdNameMap.TryGetValue(id, out value); public static Dictionary ImageIdNameMap = new() diff --git a/Definitions/ObjectModels/Objects/TrackStation/TrackStationObject.cs b/Definitions/ObjectModels/Objects/TrackStation/TrackStationObject.cs index 2b232334..54f1ef02 100644 --- a/Definitions/ObjectModels/Objects/TrackStation/TrackStationObject.cs +++ b/Definitions/ObjectModels/Objects/TrackStation/TrackStationObject.cs @@ -1,6 +1,7 @@ using Definitions.ObjectModels.Objects.Shared; using Definitions.ObjectModels.Objects.Track; using Definitions.ObjectModels.Types; +using System.Diagnostics.CodeAnalysis; namespace Definitions.ObjectModels.Objects.TrackStation; @@ -49,8 +50,10 @@ public bool Validate() return true; // CompatibleTrackObjects.Count <= TrackStationObjectLoader.Constants.MaxNumCompatible; } - public bool TryGetImageName(int id, out string? value) - => ImageIdNameMap.TryGetValue(id, out value); + public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) + { + return ImageIdNameMap.TryGetValue(id, out value); + } public static readonly Dictionary ImageIdNameMap = new() { diff --git a/Definitions/ObjectModels/Objects/Tree/TreeObject.cs b/Definitions/ObjectModels/Objects/Tree/TreeObject.cs index 87c44232..85e4fa31 100644 --- a/Definitions/ObjectModels/Objects/Tree/TreeObject.cs +++ b/Definitions/ObjectModels/Objects/Tree/TreeObject.cs @@ -1,6 +1,8 @@ +using System.Diagnostics.CodeAnalysis; + namespace Definitions.ObjectModels.Objects.Tree; -public class TreeObject : ILocoStruct +public class TreeObject : ILocoStruct, IImageTableNameProvider { public uint8_t Clearance { get; set; } public uint8_t Height { get; set; } @@ -55,4 +57,7 @@ public bool Validate() return var_05 >= var_04; } + + public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) + => throw new NotImplementedException(); } diff --git a/Definitions/ObjectModels/Objects/Tunnel/TunnelObject.cs b/Definitions/ObjectModels/Objects/Tunnel/TunnelObject.cs index 9de4de22..5fcbfaf0 100644 --- a/Definitions/ObjectModels/Objects/Tunnel/TunnelObject.cs +++ b/Definitions/ObjectModels/Objects/Tunnel/TunnelObject.cs @@ -1,10 +1,12 @@ +using System.Diagnostics.CodeAnalysis; + namespace Definitions.ObjectModels.Objects.Tunnel; public class TunnelObject : ILocoStruct, IImageTableNameProvider { public bool Validate() => true; - public bool TryGetImageName(int id, out string? value) + public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) => ImageIdNameMap.TryGetValue(id, out value); public static readonly Dictionary ImageIdNameMap = new() diff --git a/Definitions/ObjectModels/Objects/Vehicle/VehicleObject.cs b/Definitions/ObjectModels/Objects/Vehicle/VehicleObject.cs index c1a39565..b712bb8c 100644 --- a/Definitions/ObjectModels/Objects/Vehicle/VehicleObject.cs +++ b/Definitions/ObjectModels/Objects/Vehicle/VehicleObject.cs @@ -1,9 +1,10 @@ using Definitions.ObjectModels.Objects.Cargo; using Definitions.ObjectModels.Types; +using System.Diagnostics.CodeAnalysis; namespace Definitions.ObjectModels.Objects.Vehicle; -public class VehicleObject : ILocoStruct +public class VehicleObject : ILocoStruct, IImageTableNameProvider { public TransportMode Mode { get; set; } public VehicleType Type { get; set; } @@ -178,4 +179,7 @@ public bool Validate() return true; } + + public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) + => throw new NotImplementedException(); } diff --git a/Definitions/ObjectModels/Objects/Wall/WallObject.cs b/Definitions/ObjectModels/Objects/Wall/WallObject.cs index 35b1b889..bc3f6e9f 100644 --- a/Definitions/ObjectModels/Objects/Wall/WallObject.cs +++ b/Definitions/ObjectModels/Objects/Wall/WallObject.cs @@ -1,3 +1,5 @@ +using System.Diagnostics.CodeAnalysis; + namespace Definitions.ObjectModels.Objects.Wall; public class WallObject : ILocoStruct, IImageTableNameProvider @@ -9,7 +11,7 @@ public class WallObject : ILocoStruct, IImageTableNameProvider public bool Validate() => true; - public bool TryGetImageName(int id, out string? value) + public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) { var result = ImageIdNameMap.TryGetValue(id, out value); diff --git a/Definitions/ObjectModels/Objects/Water/WaterObject.cs b/Definitions/ObjectModels/Objects/Water/WaterObject.cs index a5bca8ff..b867f1e9 100644 --- a/Definitions/ObjectModels/Objects/Water/WaterObject.cs +++ b/Definitions/ObjectModels/Objects/Water/WaterObject.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using System.Text.Json.Serialization; namespace Definitions.ObjectModels.Objects.Water; @@ -24,7 +25,7 @@ public bool Validate() return true; } - public bool TryGetImageName(int id, out string? value) + public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) => ImageIdNameMap.TryGetValue(id, out value); public static Dictionary ImageIdNameMap = new() diff --git a/Gui/ViewModels/Graphics/ImageTableViewModel.cs b/Gui/ViewModels/Graphics/ImageTableViewModel.cs index 73a8783e..014782fe 100644 --- a/Gui/ViewModels/Graphics/ImageTableViewModel.cs +++ b/Gui/ViewModels/Graphics/ImageTableViewModel.cs @@ -102,7 +102,7 @@ public class ImageTableViewModel : ReactiveObject, IExtraContentViewModel public BuildingComponentsViewModel? BuildingComponents { get; set; } - public ImageTableViewModel(ImageTable imageTable, ILogger logger, BuildingComponentsModel buildingComponents = null) + public ImageTableViewModel(ImageTable imageTable, ILogger logger, BuildingComponentsModel? buildingComponents = null) { ArgumentNullException.ThrowIfNull(imageTable); PaletteMap = imageTable.PaletteMap; @@ -129,7 +129,10 @@ public ImageTableViewModel(ImageTable imageTable, ILogger logger, BuildingCompon } // building components - BuildingComponents = new(buildingComponents, imageTable); + if (buildingComponents != null) + { + BuildingComponents = new(buildingComponents, imageTable); + } _ = this.WhenAnyValue(o => o.SelectedPrimarySwatch).Skip(1) .Subscribe(_ => RecolourImages(SelectedPrimarySwatch.Swatch, SelectedSecondarySwatch.Swatch)); @@ -344,7 +347,9 @@ public async Task ImportImages(string directory) var img = is1Pixel ? OnePixelTransparent : Image.Load(Path.Combine(directory, offset.Path)); var newOffset = is1Pixel ? offset with { Flags = GraphicsElementFlags.HasTransparency } : offset; var graphicsElement = GraphicsElementFromImage(newOffset, img, PaletteMap); - graphicsElement.Name = string.IsNullOrEmpty(graphicsElement.Name) ? DefaultImageTableNameProvider.GetImageName(i) : graphicsElement.Name; + graphicsElement.Name = string.IsNullOrEmpty(graphicsElement.Name) + ? DefaultImageTableNameProvider.GetImageName(i) + : graphicsElement.Name; GroupedImageViewModels[currentGroup].Images.Add(new ImageViewModel(graphicsElement, PaletteMap)); currentGroupIndex++; diff --git a/Gui/ViewModels/LocoTypes/ObjectEditorViewModel.cs b/Gui/ViewModels/LocoTypes/ObjectEditorViewModel.cs index b555adf6..90e541ee 100644 --- a/Gui/ViewModels/LocoTypes/ObjectEditorViewModel.cs +++ b/Gui/ViewModels/LocoTypes/ObjectEditorViewModel.cs @@ -167,7 +167,23 @@ public override void Load() else { CurrentObject.LocoObject.ImageTable?.PaletteMap = Model.PaletteMap; - ExtraContentViewModel = new ImageTableViewModel(CurrentObject.LocoObject.ImageTable, Model.Logger, (CurrentObject.LocoObject.Object as IHasBuildingComponents)?.BuildingComponents); + + // temporary hack to show building components + if (CurrentObject.LocoObject.ObjectType == Definitions.ObjectModels.Types.ObjectType.Building) + { + ExtraContentViewModel = new ImageTableViewModel(CurrentObject.LocoObject.ImageTable, Model.Logger, (CurrentObject.LocoObject.Object as IHasBuildingComponents)?.BuildingComponents); + } + else + { + if (CurrentObject.LocoObject.ImageTable == null) + { + logger.Info("${CurrentFile.DisplayName has no image table"); + } + else + { + ExtraContentViewModel = new ImageTableViewModel(CurrentObject.LocoObject.ImageTable, Model.Logger, null); + } + } } } else diff --git a/Gui/Views/ImageTableView.axaml b/Gui/Views/ImageTableView.axaml index 3d1ea410..1a0f6192 100644 --- a/Gui/Views/ImageTableView.axaml +++ b/Gui/Views/ImageTableView.axaml @@ -251,8 +251,8 @@ - - + + diff --git a/Tests/LoadSaveTests.cs b/Tests/LoadSaveTests.cs index d7185f4f..992dc95c 100644 --- a/Tests/LoadSaveTests.cs +++ b/Tests/LoadSaveTests.cs @@ -622,7 +622,7 @@ void assertFunc(LocoObject obj, LandObject struc) => Assert.Multiple(() => Assert.That(struc.CliffEdgeHeader.Name, Is.EqualTo("LSBROWN"), nameof(struc.CliffEdgeHeader)); Assert.That(struc.CliffEdgeHeader.Checksum, Is.Zero, nameof(struc.CliffEdgeHeader)); Assert.That(struc.CliffEdgeHeader.ObjectType, Is.EqualTo(ObjectType.CliffEdge), nameof(struc.CliffEdgeHeader)); - Assert.That(struc.CliffEdgeHeader.ObjectSource, Is.Zero, nameof(struc.CliffEdgeHeader)); + Assert.That(struc.CliffEdgeHeader.ObjectSource, Is.EqualTo(ObjectSource.Custom), nameof(struc.CliffEdgeHeader)); Assert.That(struc.UnkObjectHeader, Is.Null, nameof(struc.UnkObjectHeader)); From d3ec84008d6d25c57db105adc656f06ad6ff92e1 Mon Sep 17 00:00:00 2001 From: Benjamin Sutas Date: Wed, 17 Sep 2025 00:42:18 +1000 Subject: [PATCH 15/21] move image naming out of object definitions --- Dat/Loaders/AirportObjectLoader.cs | 2 +- Dat/Loaders/BridgeObjectLoader.cs | 2 +- Dat/Loaders/BuildingObjectLoader.cs | 2 +- Dat/Loaders/CargoObjectLoader.cs | 2 +- Dat/Loaders/CliffEdgeObjectLoader.cs | 2 +- Dat/Loaders/ClimateObjectLoader.cs | 2 +- Dat/Loaders/CompetitorObjectLoader.cs | 2 +- Dat/Loaders/CurrencyObjectLoader.cs | 2 +- Dat/Loaders/DockObjectLoader.cs | 2 +- Dat/Loaders/HillShapesObjectLoader.cs | 2 +- Dat/Loaders/ImageTableLoader.cs | 269 --- Dat/Loaders/IndustryObjectLoader.cs | 2 +- Dat/Loaders/InterfaceSkinObjectLoader.cs | 2 +- Dat/Loaders/LandObjectLoader.cs | 2 +- Dat/Loaders/LevelCrossingObjectLoader.cs | 2 +- Dat/Loaders/RoadExtraObjectLoader.cs | 2 +- Dat/Loaders/RoadObjectLoader.cs | 2 +- Dat/Loaders/RoadStationObjectLoader.cs | 2 +- Dat/Loaders/ScaffoldingObjectLoader.cs | 2 +- Dat/Loaders/SnowObjectLoader.cs | 2 +- Dat/Loaders/SteamObjectLoader.cs | 2 +- Dat/Loaders/StreetLightObjectLoader.cs | 2 +- Dat/Loaders/TrackExtraObjectLoader.cs | 2 +- Dat/Loaders/TrackObjectLoader.cs | 2 +- Dat/Loaders/TrackSignalObjectLoader.cs | 2 +- Dat/Loaders/TrackStationObjectLoader.cs | 2 +- Dat/Loaders/TreeObjectLoader.cs | 2 +- Dat/Loaders/TunnelObjectLoader.cs | 2 +- Dat/Loaders/Vehicle/VehicleObjectLoader.cs | 2 +- Dat/Loaders/WallObjectLoader.cs | 2 +- Dat/Loaders/WaterObjectLoader.cs | 2 +- Dat/Types/G1Dat.cs | 2 +- .../ObjectModels/IImageTableNameProvider.cs | 29 +- Definitions/ObjectModels/ImageTableGrouper.cs | 2101 +++++++++++++++++ .../Objects/Airport/AirportObject.cs | 5 +- .../Objects/Bridge/BridgeObject.cs | 5 +- .../Objects/Building/BuildingObject.cs | 11 +- .../ObjectModels/Objects/Cargo/CargoObject.cs | 11 +- .../Objects/CliffEdge/CliffEdgeObject.cs | 34 +- .../Objects/Climate/ClimateObject.cs | 6 +- .../Objects/Competitor/CompetitorObject.cs | 27 +- .../Objects/Currency/CurrencyObject.cs | 7 +- .../ObjectModels/Objects/Dock/DockObject.cs | 6 +- .../Objects/HillShapes/HillShapesObject.cs | 17 +- .../Objects/Industry/IndustryObject.cs | 5 +- .../InterfaceSkin/InterfaceSkinObject.cs | 479 +--- .../ObjectModels/Objects/Land/LandObject.cs | 29 +- .../LevelCrossing/LevelCrossingObject.cs | 7 +- .../ObjectModels/Objects/Road/RoadObject.cs | 285 +-- .../Objects/RoadExtra/RoadExtraObject.cs | 6 +- .../Objects/RoadStation/RoadStationObject.cs | 28 +- .../Objects/Scaffolding/ScaffoldingObject.cs | 45 +- .../ObjectModels/Objects/Snow/SnowObject.cs | 18 +- .../ObjectModels/Objects/Steam/SteamObject.cs | 6 +- .../Objects/StreetLight/StreetLightObject.cs | 23 +- .../ObjectModels/Objects/Track/TrackObject.cs | 424 +--- .../Objects/TrackExtra/TrackExtraObject.cs | 227 +- .../Objects/TrackSignal/TrackSignalObject.cs | 14 +- .../TrackStation/TrackStationObject.cs | 43 +- .../ObjectModels/Objects/Tree/TreeObject.cs | 7 +- .../Objects/Tunnel/TunnelObject.cs | 13 +- .../Objects/Vehicle/VehicleObject.cs | 6 +- .../ObjectModels/Objects/Wall/WallObject.cs | 44 +- .../ObjectModels/Objects/Water/WaterObject.cs | 89 +- 64 files changed, 2191 insertions(+), 2197 deletions(-) delete mode 100644 Dat/Loaders/ImageTableLoader.cs create mode 100644 Definitions/ObjectModels/ImageTableGrouper.cs diff --git a/Dat/Loaders/AirportObjectLoader.cs b/Dat/Loaders/AirportObjectLoader.cs index 60856ee1..2d608aea 100644 --- a/Dat/Loaders/AirportObjectLoader.cs +++ b/Dat/Loaders/AirportObjectLoader.cs @@ -76,7 +76,7 @@ public static LocoObject Load(Stream stream) var imageList = SawyerStreamReader.ReadImageTable(br).Table; // define groups - var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); + var imageTable = ImageTableGrouper.CreateImageTable(model, ObjectType, imageList); return new LocoObject(ObjectType, model, stringTable, imageTable); } diff --git a/Dat/Loaders/BridgeObjectLoader.cs b/Dat/Loaders/BridgeObjectLoader.cs index 3f2ba0ec..3fd0e68c 100644 --- a/Dat/Loaders/BridgeObjectLoader.cs +++ b/Dat/Loaders/BridgeObjectLoader.cs @@ -63,7 +63,7 @@ public static LocoObject Load(Stream stream) var imageList = SawyerStreamReader.ReadImageTable(br).Table; // define groups - var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); + var imageTable = ImageTableGrouper.CreateImageTable(model, ObjectType, imageList); return new LocoObject(ObjectType, model, stringTable, imageTable); } diff --git a/Dat/Loaders/BuildingObjectLoader.cs b/Dat/Loaders/BuildingObjectLoader.cs index f631949d..32e1caed 100644 --- a/Dat/Loaders/BuildingObjectLoader.cs +++ b/Dat/Loaders/BuildingObjectLoader.cs @@ -84,7 +84,7 @@ public static LocoObject Load(Stream stream) var imageList = SawyerStreamReader.ReadImageTable(br).Table; // define groups - var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); + var imageTable = ImageTableGrouper.CreateImageTable(model, ObjectType, imageList); return new LocoObject(ObjectType, model, stringTable, imageTable); } diff --git a/Dat/Loaders/CargoObjectLoader.cs b/Dat/Loaders/CargoObjectLoader.cs index 6eb29ac7..b3412616 100644 --- a/Dat/Loaders/CargoObjectLoader.cs +++ b/Dat/Loaders/CargoObjectLoader.cs @@ -52,7 +52,7 @@ public static LocoObject Load(Stream stream) var imageList = SawyerStreamReader.ReadImageTable(br).Table; // define groups - var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); + var imageTable = ImageTableGrouper.CreateImageTable(model, ObjectType, imageList); return new LocoObject(ObjectType, model, stringTable, imageTable); } diff --git a/Dat/Loaders/CliffEdgeObjectLoader.cs b/Dat/Loaders/CliffEdgeObjectLoader.cs index 2dbc90f1..2e475f29 100644 --- a/Dat/Loaders/CliffEdgeObjectLoader.cs +++ b/Dat/Loaders/CliffEdgeObjectLoader.cs @@ -33,7 +33,7 @@ public static LocoObject Load(Stream stream) // image table var imageList = SawyerStreamReader.ReadImageTable(br).Table; - var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); + var imageTable = ImageTableGrouper.CreateImageTable(model, ObjectType, imageList); return new LocoObject(ObjectType, model, stringTable, imageTable); } diff --git a/Dat/Loaders/ClimateObjectLoader.cs b/Dat/Loaders/ClimateObjectLoader.cs index ee8d50ce..86dad4c0 100644 --- a/Dat/Loaders/ClimateObjectLoader.cs +++ b/Dat/Loaders/ClimateObjectLoader.cs @@ -49,7 +49,7 @@ public static LocoObject Load(Stream stream) var imageList = SawyerStreamReader.ReadImageTable(br).Table; // define groups - var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); + var imageTable = ImageTableGrouper.CreateImageTable(model, ObjectType, imageList); return new LocoObject(ObjectType, model, stringTable, imageTable); } diff --git a/Dat/Loaders/CompetitorObjectLoader.cs b/Dat/Loaders/CompetitorObjectLoader.cs index b689d9e7..a4c3aec2 100644 --- a/Dat/Loaders/CompetitorObjectLoader.cs +++ b/Dat/Loaders/CompetitorObjectLoader.cs @@ -49,7 +49,7 @@ public static LocoObject Load(Stream stream) var imageList = SawyerStreamReader.ReadImageTable(br).Table; // define groups - var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); + var imageTable = ImageTableGrouper.CreateImageTable(model, ObjectType, imageList); return new LocoObject(ObjectType, model, stringTable, imageTable); } diff --git a/Dat/Loaders/CurrencyObjectLoader.cs b/Dat/Loaders/CurrencyObjectLoader.cs index 33b76549..16ed729b 100644 --- a/Dat/Loaders/CurrencyObjectLoader.cs +++ b/Dat/Loaders/CurrencyObjectLoader.cs @@ -50,7 +50,7 @@ public static LocoObject Load(Stream stream) var imageList = SawyerStreamReader.ReadImageTable(br).Table; // define groups - var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); + var imageTable = ImageTableGrouper.CreateImageTable(model, ObjectType, imageList); return new LocoObject(ObjectType, model, stringTable, imageTable); } diff --git a/Dat/Loaders/DockObjectLoader.cs b/Dat/Loaders/DockObjectLoader.cs index 05ba1997..0e696576 100644 --- a/Dat/Loaders/DockObjectLoader.cs +++ b/Dat/Loaders/DockObjectLoader.cs @@ -69,7 +69,7 @@ public static LocoObject Load(Stream stream) var imageList = SawyerStreamReader.ReadImageTable(br).Table; // define groups - var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); + var imageTable = ImageTableGrouper.CreateImageTable(model, ObjectType, imageList); return new LocoObject(ObjectType, model, stringTable, imageTable); } diff --git a/Dat/Loaders/HillShapesObjectLoader.cs b/Dat/Loaders/HillShapesObjectLoader.cs index 7e093062..3365fc3f 100644 --- a/Dat/Loaders/HillShapesObjectLoader.cs +++ b/Dat/Loaders/HillShapesObjectLoader.cs @@ -48,7 +48,7 @@ public static LocoObject Load(Stream stream) var imageList = SawyerStreamReader.ReadImageTable(br).Table; // define groups - var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); + var imageTable = ImageTableGrouper.CreateImageTable(model, ObjectType, imageList); return new LocoObject(ObjectType, model, stringTable, imageTable); } diff --git a/Dat/Loaders/ImageTableLoader.cs b/Dat/Loaders/ImageTableLoader.cs deleted file mode 100644 index 7bf97cdb..00000000 --- a/Dat/Loaders/ImageTableLoader.cs +++ /dev/null @@ -1,269 +0,0 @@ -using Definitions.ObjectModels; -using Definitions.ObjectModels.Objects.Airport; -using Definitions.ObjectModels.Types; - -namespace Dat.Loaders; -public static class ImageTableLoader -{ - public static ImageTable CreateImageTable(IImageTableNameProvider imageNamer, ObjectType objectType, List imageList) - { - NameImages(imageNamer, imageList); - - var imageTable = new ImageTable(); - - switch (objectType) - { - case ObjectType.InterfaceSkin: - CreateInterfaceGroups(imageList, imageTable); - break; - case ObjectType.Sound: - imageTable.Groups.Add(("", imageList.ToList())); - break; - case ObjectType.Currency: - imageTable.Groups.Add(("", imageList.ToList())); - break; - case ObjectType.Steam: - imageTable.Groups.Add(("", imageList.ToList())); - break; - case ObjectType.CliffEdge: - CreateCliffEdgeGroups(imageList, imageTable); - break; - case ObjectType.Water: - imageTable.Groups.Add(("", imageList.ToList())); - break; - case ObjectType.Land: - imageTable.Groups.Add(("", imageList.ToList())); - break; - case ObjectType.TownNames: - imageTable.Groups.Add(("", imageList.ToList())); - break; - case ObjectType.Cargo: - CreateCargoGroups(imageList, imageTable); - break; - case ObjectType.Wall: - imageTable.Groups.Add(("", imageList.ToList())); - break; - case ObjectType.TrackSignal: - imageTable.Groups.Add(("", imageList.ToList())); - break; - case ObjectType.LevelCrossing: - imageTable.Groups.Add(("", imageList.ToList())); - break; - case ObjectType.StreetLight: - imageTable.Groups.Add(("", imageList.ToList())); - break; - case ObjectType.Tunnel: - imageTable.Groups.Add(("", imageList.ToList())); - break; - case ObjectType.Bridge: - CreateBridgeGroups(imageList, imageTable); - break; - case ObjectType.TrackStation: - imageTable.Groups.Add(("", imageList.ToList())); - break; - case ObjectType.TrackExtra: - imageTable.Groups.Add(("", imageList.ToList())); - break; - case ObjectType.Track: - imageTable.Groups.Add(("", imageList.ToList())); - break; - case ObjectType.RoadStation: - imageTable.Groups.Add(("", imageList.ToList())); - break; - case ObjectType.RoadExtra: - imageTable.Groups.Add(("", imageList.ToList())); - break; - case ObjectType.Road: - imageTable.Groups.Add(("", imageList.ToList())); - break; - case ObjectType.Airport: - CreateAirportGroups(imageList, imageTable); - break; - case ObjectType.Dock: - CreateDockGroups(imageList, imageTable); - break; - case ObjectType.Vehicle: - imageTable.Groups.Add(("", imageList.ToList())); - break; - case ObjectType.Tree: - imageTable.Groups.Add(("", imageList.ToList())); - break; - case ObjectType.Snow: - imageTable.Groups.Add(("", imageList.ToList())); - break; - case ObjectType.Climate: - imageTable.Groups.Add(("", imageList.ToList())); - break; - case ObjectType.HillShapes: - imageTable.Groups.Add(("", imageList.ToList())); - break; - case ObjectType.Building: - CreateBuildingGroups(imageList, imageTable); - break; - case ObjectType.Scaffolding: - CreateScaffoldingGroups(imageList, imageTable); - break; - case ObjectType.Industry: - CreateBuildingGroups(imageList, imageTable); - break; - case ObjectType.Region: - imageTable.Groups.Add(("", imageList.ToList())); - break; - case ObjectType.Competitor: - CreateCompetitorGroups(imageList, imageTable); - break; - case ObjectType.ScenarioText: - imageTable.Groups.Add(("", imageList.ToList())); - break; - default: - break; - } - - return imageTable; - } - - #region NewImpl - - private static void NameImages(IImageTableNameProvider imageNamer, List imageList) - { - for (var i = 0; i < imageList.Count; ++i) - { - imageList[i].Name = imageNamer.TryGetImageName(i, out var name) - ? name - : DefaultImageTableNameProvider.GetImageName(i); - } - } - - private static ImageTable CreateImageTable(AirportObject airportObject, List imageList) - { - var imageTable = new ImageTable(); - - imageTable.Groups.Add(("preview", imageList[0..1])); - - imageTable.Groups.AddRange(imageList - .Skip(1) - .Chunk(4) - .Select((x, i) => ($"Part {i}", x.ToList())) - .ToList()); - - return imageTable; - } - - #endregion - - private static void CreateAirportGroups(List imageList, ImageTable imageTable) - { - imageTable.Groups.Add(("preview", imageList[0..1])); - - imageTable.Groups.AddRange(imageList - .Skip(1) - .Chunk(4) - .Select((x, i) => ($"Part {i}", x.ToList())) - .ToList()); - } - - private static void CreateBridgeGroups(List imageList, ImageTable imageTable) - { - imageTable.Groups.Add(("preview", imageList[0..1])); - imageTable.Groups.Add(("base plates", imageList[1..5])); - imageTable.Groups.Add(("unk", imageList[6..11])); - imageTable.Groups.Add(("", imageList[12..])); - } - - private static void CreateBuildingGroups(List imageList, ImageTable imageTable) - => imageTable.Groups = imageList - .Chunk(4) - .Select((x, i) => ($"Part {i}", x.ToList())) - .ToList(); - - private static void CreateCargoGroups(List imageList, ImageTable imageTable) - { - imageTable.Groups.Add(("preview", imageList[0..1])); - imageTable.Groups.Add(("station variations", imageList[1..])); - } - - private static void CreateCliffEdgeGroups(List imageList, ImageTable imageTable) - { - imageTable.Groups.Add(("left west", imageList[0..16])); - imageTable.Groups.Add(("right east", imageList[16..32])); - imageTable.Groups.Add(("right west", imageList[32..48])); - imageTable.Groups.Add(("left east", imageList[48..64])); - imageTable.Groups.Add(("far-side slopes", imageList[64..])); - } - - private static void CreateCompetitorGroups(List imageList, ImageTable imageTable) - { - imageTable.Groups.Add(("neutral", imageList[0..2])); - imageTable.Groups.Add(("happy", imageList[2..4])); - imageTable.Groups.Add(("worried", imageList[4..6])); - imageTable.Groups.Add(("thinking", imageList[6..8])); - imageTable.Groups.Add(("dejected", imageList[8..10])); - imageTable.Groups.Add(("surprised", imageList[10..12])); - imageTable.Groups.Add(("scared", imageList[12..14])); - imageTable.Groups.Add(("angry", imageList[14..16])); - imageTable.Groups.Add(("disgusted", imageList[16..18])); - } - - private static void CreateDockGroups(List imageList, ImageTable imageTable) - { - imageTable.Groups.Add(("preview", [imageList[0]])); - - imageTable.Groups.AddRange(imageList - .Skip(1) - .Chunk(4) - .Select((x, i) => ($"Part {i}", x.ToList())) - .ToList()); - } - - private static void CreateInterfaceGroups(List imageList, ImageTable imageTable) - { - imageTable.Groups.Add(("preview", imageList[0..1])); - imageTable.Groups.Add(("toolbar", imageList[1..31])); - imageTable.Groups.Add(("build-vehicle", imageList[31..43])); - imageTable.Groups.Add(("toolbar", imageList[43..49])); - imageTable.Groups.Add(("paint", imageList[49..55])); - imageTable.Groups.Add(("population", imageList[57..65])); - imageTable.Groups.Add(("performance-index", imageList[65..73])); - imageTable.Groups.Add(("cargo-units", imageList[73..81])); - imageTable.Groups.Add(("cargo-distance", imageList[81..89])); - imageTable.Groups.Add(("production", imageList[89..97])); - imageTable.Groups.Add(("wrench", imageList[97..113])); - imageTable.Groups.Add(("finances", imageList[113..129])); - imageTable.Groups.Add(("cup", imageList[129..145])); - imageTable.Groups.Add(("ratings", imageList[145..161])); - imageTable.Groups.Add(("transported", imageList[161..168])); - imageTable.Groups.Add(("cogs", imageList[168..172])); - imageTable.Groups.Add(("toolbar", imageList[172..203])); - imageTable.Groups.Add(("tab-train", imageList[203..211])); - imageTable.Groups.Add(("tab-aircraft", imageList[211..219])); - imageTable.Groups.Add(("tab-bus", imageList[219..227])); - imageTable.Groups.Add(("tab-tram", imageList[227..235])); - imageTable.Groups.Add(("tab-truck", imageList[235..243])); - imageTable.Groups.Add(("tab-ship", imageList[243..251])); - imageTable.Groups.Add(("build-train", imageList[251..267])); - imageTable.Groups.Add(("build-aircraft", imageList[267..283])); - imageTable.Groups.Add(("build-bus", imageList[283..299])); - imageTable.Groups.Add(("build-tram", imageList[299..315])); - imageTable.Groups.Add(("build-truck", imageList[315..331])); - imageTable.Groups.Add(("build-ship", imageList[331..347])); - imageTable.Groups.Add(("build-industry", imageList[347..363])); - imageTable.Groups.Add(("build-town", imageList[363..379])); - imageTable.Groups.Add(("build-buildings", imageList[379..395])); - imageTable.Groups.Add(("build-misc-buildings", imageList[395..411])); - imageTable.Groups.Add(("build-extra", imageList[411..418])); - imageTable.Groups.Add(("train", imageList[418..426])); - imageTable.Groups.Add(("aircraft", imageList[426..434])); - imageTable.Groups.Add(("bus", imageList[434..442])); - imageTable.Groups.Add(("tram", imageList[442..450])); - imageTable.Groups.Add(("truck", imageList[450..458])); - imageTable.Groups.Add(("ship", imageList[458..466])); - imageTable.Groups.Add(("toolbar-map", imageList[466..470])); - } - - private static void CreateScaffoldingGroups(List imageList, ImageTable imageTable) - { - imageTable.Groups.Add(("type 0", imageList[0..10])); - imageTable.Groups.Add(("type 1", imageList[10..24])); - imageTable.Groups.Add(("type 2", imageList[24..36])); - } -} diff --git a/Dat/Loaders/IndustryObjectLoader.cs b/Dat/Loaders/IndustryObjectLoader.cs index 95d9a525..4d11c127 100644 --- a/Dat/Loaders/IndustryObjectLoader.cs +++ b/Dat/Loaders/IndustryObjectLoader.cs @@ -103,7 +103,7 @@ public static LocoObject Load(Stream stream) var imageList = SawyerStreamReader.ReadImageTable(br).Table; // define groups - var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); + var imageTable = ImageTableGrouper.CreateImageTable(model, ObjectType, imageList); return new LocoObject(ObjectType, model, stringTable, imageTable); } diff --git a/Dat/Loaders/InterfaceSkinObjectLoader.cs b/Dat/Loaders/InterfaceSkinObjectLoader.cs index 3454652b..c53ae22a 100644 --- a/Dat/Loaders/InterfaceSkinObjectLoader.cs +++ b/Dat/Loaders/InterfaceSkinObjectLoader.cs @@ -62,7 +62,7 @@ public static LocoObject Load(Stream stream) var imageList = SawyerStreamReader.ReadImageTable(br).Table; // define groups - var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); + var imageTable = ImageTableGrouper.CreateImageTable(model, ObjectType, imageList); return new LocoObject(ObjectType, model, stringTable, imageTable); } diff --git a/Dat/Loaders/LandObjectLoader.cs b/Dat/Loaders/LandObjectLoader.cs index 97b726e7..c649cb3d 100644 --- a/Dat/Loaders/LandObjectLoader.cs +++ b/Dat/Loaders/LandObjectLoader.cs @@ -63,7 +63,7 @@ public static LocoObject Load(Stream stream) var imageList = SawyerStreamReader.ReadImageTable(br).Table; // define groups - var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); + var imageTable = ImageTableGrouper.CreateImageTable(model, ObjectType, imageList); return new LocoObject(ObjectType, model, stringTable, imageTable); } diff --git a/Dat/Loaders/LevelCrossingObjectLoader.cs b/Dat/Loaders/LevelCrossingObjectLoader.cs index 09ad2426..2f8cb438 100644 --- a/Dat/Loaders/LevelCrossingObjectLoader.cs +++ b/Dat/Loaders/LevelCrossingObjectLoader.cs @@ -46,7 +46,7 @@ public static LocoObject Load(Stream stream) var imageList = SawyerStreamReader.ReadImageTable(br).Table; // define groups - var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); + var imageTable = ImageTableGrouper.CreateImageTable(model, ObjectType, imageList); return new LocoObject(ObjectType, model, stringTable, imageTable); } diff --git a/Dat/Loaders/RoadExtraObjectLoader.cs b/Dat/Loaders/RoadExtraObjectLoader.cs index 9f78c269..481fdbe4 100644 --- a/Dat/Loaders/RoadExtraObjectLoader.cs +++ b/Dat/Loaders/RoadExtraObjectLoader.cs @@ -46,7 +46,7 @@ public static LocoObject Load(Stream stream) var imageList = SawyerStreamReader.ReadImageTable(br).Table; // define groups - var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); + var imageTable = ImageTableGrouper.CreateImageTable(model, ObjectType, imageList); return new LocoObject(ObjectType, model, stringTable, imageTable); } diff --git a/Dat/Loaders/RoadObjectLoader.cs b/Dat/Loaders/RoadObjectLoader.cs index b946337b..06a6b24c 100644 --- a/Dat/Loaders/RoadObjectLoader.cs +++ b/Dat/Loaders/RoadObjectLoader.cs @@ -71,7 +71,7 @@ public static LocoObject Load(Stream stream) var imageList = SawyerStreamReader.ReadImageTable(br).Table; // define groups - var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); + var imageTable = ImageTableGrouper.CreateImageTable(model, ObjectType, imageList); return new LocoObject(ObjectType, model, stringTable, imageTable); } diff --git a/Dat/Loaders/RoadStationObjectLoader.cs b/Dat/Loaders/RoadStationObjectLoader.cs index 0f207c3d..41ecf952 100644 --- a/Dat/Loaders/RoadStationObjectLoader.cs +++ b/Dat/Loaders/RoadStationObjectLoader.cs @@ -66,7 +66,7 @@ public static LocoObject Load(Stream stream) var imageList = SawyerStreamReader.ReadImageTable(br).Table; // define groups - var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); + var imageTable = ImageTableGrouper.CreateImageTable(model, ObjectType, imageList); return new LocoObject(ObjectType, model, stringTable, imageTable); } diff --git a/Dat/Loaders/ScaffoldingObjectLoader.cs b/Dat/Loaders/ScaffoldingObjectLoader.cs index f6ceb7f4..90f9b264 100644 --- a/Dat/Loaders/ScaffoldingObjectLoader.cs +++ b/Dat/Loaders/ScaffoldingObjectLoader.cs @@ -57,7 +57,7 @@ public static LocoObject Load(Stream stream) var imageList = SawyerStreamReader.ReadImageTable(br).Table; // define groups - var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); + var imageTable = ImageTableGrouper.CreateImageTable(model, ObjectType, imageList); return new LocoObject(ObjectType, model, stringTable, imageTable); } diff --git a/Dat/Loaders/SnowObjectLoader.cs b/Dat/Loaders/SnowObjectLoader.cs index 8da11fdb..31404d1e 100644 --- a/Dat/Loaders/SnowObjectLoader.cs +++ b/Dat/Loaders/SnowObjectLoader.cs @@ -41,7 +41,7 @@ public static LocoObject Load(Stream stream) var imageList = SawyerStreamReader.ReadImageTable(br).Table; // define groups - var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); + var imageTable = ImageTableGrouper.CreateImageTable(model, ObjectType, imageList); return new LocoObject(ObjectType, model, stringTable, imageTable); } diff --git a/Dat/Loaders/SteamObjectLoader.cs b/Dat/Loaders/SteamObjectLoader.cs index e9713cdb..0bd4643d 100644 --- a/Dat/Loaders/SteamObjectLoader.cs +++ b/Dat/Loaders/SteamObjectLoader.cs @@ -62,7 +62,7 @@ public static LocoObject Load(Stream stream) var imageList = SawyerStreamReader.ReadImageTable(br).Table; // define groups - var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); + var imageTable = ImageTableGrouper.CreateImageTable(model, ObjectType, imageList); return new LocoObject(ObjectType, model, stringTable, imageTable); } diff --git a/Dat/Loaders/StreetLightObjectLoader.cs b/Dat/Loaders/StreetLightObjectLoader.cs index 707de69b..739ea372 100644 --- a/Dat/Loaders/StreetLightObjectLoader.cs +++ b/Dat/Loaders/StreetLightObjectLoader.cs @@ -45,7 +45,7 @@ public static LocoObject Load(Stream stream) var imageList = SawyerStreamReader.ReadImageTable(br).Table; // define groups - var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); + var imageTable = ImageTableGrouper.CreateImageTable(model, ObjectType, imageList); return new LocoObject(ObjectType, model, stringTable, imageTable); } diff --git a/Dat/Loaders/TrackExtraObjectLoader.cs b/Dat/Loaders/TrackExtraObjectLoader.cs index 368a7f14..6e093f1f 100644 --- a/Dat/Loaders/TrackExtraObjectLoader.cs +++ b/Dat/Loaders/TrackExtraObjectLoader.cs @@ -51,7 +51,7 @@ public static LocoObject Load(Stream stream) var imageList = SawyerStreamReader.ReadImageTable(br).Table; // define groups - var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); + var imageTable = ImageTableGrouper.CreateImageTable(model, ObjectType, imageList); return new LocoObject(ObjectType, model, stringTable, imageTable); } diff --git a/Dat/Loaders/TrackObjectLoader.cs b/Dat/Loaders/TrackObjectLoader.cs index b1ad3c1c..c3d55689 100644 --- a/Dat/Loaders/TrackObjectLoader.cs +++ b/Dat/Loaders/TrackObjectLoader.cs @@ -73,7 +73,7 @@ public static LocoObject Load(Stream stream) var imageList = SawyerStreamReader.ReadImageTable(br).Table; // define groups - var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); + var imageTable = ImageTableGrouper.CreateImageTable(model, ObjectType, imageList); return new LocoObject(ObjectType, model, stringTable, imageTable); } diff --git a/Dat/Loaders/TrackSignalObjectLoader.cs b/Dat/Loaders/TrackSignalObjectLoader.cs index 10d41efa..2ee5bf0f 100644 --- a/Dat/Loaders/TrackSignalObjectLoader.cs +++ b/Dat/Loaders/TrackSignalObjectLoader.cs @@ -59,7 +59,7 @@ public static LocoObject Load(Stream stream) var imageList = SawyerStreamReader.ReadImageTable(br).Table; // define groups - var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); + var imageTable = ImageTableGrouper.CreateImageTable(model, ObjectType, imageList); return new LocoObject(ObjectType, model, stringTable, imageTable); } diff --git a/Dat/Loaders/TrackStationObjectLoader.cs b/Dat/Loaders/TrackStationObjectLoader.cs index 9b07a0ce..cae91b1c 100644 --- a/Dat/Loaders/TrackStationObjectLoader.cs +++ b/Dat/Loaders/TrackStationObjectLoader.cs @@ -68,7 +68,7 @@ public static LocoObject Load(Stream stream) var imageList = SawyerStreamReader.ReadImageTable(br).Table; // define groups - var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); + var imageTable = ImageTableGrouper.CreateImageTable(model, ObjectType, imageList); return new LocoObject(ObjectType, model, stringTable, imageTable); } diff --git a/Dat/Loaders/TreeObjectLoader.cs b/Dat/Loaders/TreeObjectLoader.cs index cb18617d..5fe93e77 100644 --- a/Dat/Loaders/TreeObjectLoader.cs +++ b/Dat/Loaders/TreeObjectLoader.cs @@ -65,7 +65,7 @@ public static LocoObject Load(Stream stream) var imageList = SawyerStreamReader.ReadImageTable(br).Table; // define groups - var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); + var imageTable = ImageTableGrouper.CreateImageTable(model, ObjectType, imageList); return new LocoObject(ObjectType, model, stringTable, imageTable); } diff --git a/Dat/Loaders/TunnelObjectLoader.cs b/Dat/Loaders/TunnelObjectLoader.cs index a7418a76..be18a654 100644 --- a/Dat/Loaders/TunnelObjectLoader.cs +++ b/Dat/Loaders/TunnelObjectLoader.cs @@ -44,7 +44,7 @@ public static LocoObject Load(Stream stream) var imageList = SawyerStreamReader.ReadImageTable(br).Table; // define groups - var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); + var imageTable = ImageTableGrouper.CreateImageTable(model, ObjectType, imageList); return new LocoObject(ObjectType, model, stringTable, imageTable); } diff --git a/Dat/Loaders/Vehicle/VehicleObjectLoader.cs b/Dat/Loaders/Vehicle/VehicleObjectLoader.cs index 6bbe900e..60409b71 100644 --- a/Dat/Loaders/Vehicle/VehicleObjectLoader.cs +++ b/Dat/Loaders/Vehicle/VehicleObjectLoader.cs @@ -62,7 +62,7 @@ public static LocoObject Load(Stream stream) var imageList = SawyerStreamReader.ReadImageTable(br).Table; // define groups - var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); + var imageTable = ImageTableGrouper.CreateImageTable(model, ObjectType, imageList); return new LocoObject(ObjectType, model, stringTable, imageTable); } diff --git a/Dat/Loaders/WallObjectLoader.cs b/Dat/Loaders/WallObjectLoader.cs index a150e2c1..25294775 100644 --- a/Dat/Loaders/WallObjectLoader.cs +++ b/Dat/Loaders/WallObjectLoader.cs @@ -49,7 +49,7 @@ public static LocoObject Load(Stream stream) var imageList = SawyerStreamReader.ReadImageTable(br).Table; // define groups - var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); + var imageTable = ImageTableGrouper.CreateImageTable(model, ObjectType, imageList); return new LocoObject(ObjectType, model, stringTable, imageTable); } diff --git a/Dat/Loaders/WaterObjectLoader.cs b/Dat/Loaders/WaterObjectLoader.cs index 10e9fa4b..22ca4642 100644 --- a/Dat/Loaders/WaterObjectLoader.cs +++ b/Dat/Loaders/WaterObjectLoader.cs @@ -48,7 +48,7 @@ public static LocoObject Load(Stream stream) var imageList = SawyerStreamReader.ReadImageTable(br).Table; // define groups - var imageTable = ImageTableLoader.CreateImageTable(model, ObjectType, imageList); + var imageTable = ImageTableGrouper.CreateImageTable(model, ObjectType, imageList); return new LocoObject(ObjectType, model, stringTable, imageTable); } diff --git a/Dat/Types/G1Dat.cs b/Dat/Types/G1Dat.cs index 2327a385..81de0c3b 100644 --- a/Dat/Types/G1Dat.cs +++ b/Dat/Types/G1Dat.cs @@ -6,7 +6,7 @@ namespace Dat.Types; [TypeConverter(typeof(ExpandableObjectConverter))] -public class G1Dat : IImageTableNameProvider +public class G1Dat { public G1Header G1Header { get; set; } diff --git a/Definitions/ObjectModels/IImageTableNameProvider.cs b/Definitions/ObjectModels/IImageTableNameProvider.cs index 4bf89b1c..44488704 100644 --- a/Definitions/ObjectModels/IImageTableNameProvider.cs +++ b/Definitions/ObjectModels/IImageTableNameProvider.cs @@ -5,17 +5,40 @@ namespace Definitions.ObjectModels; public interface IHasGraphicsElements { - public List GraphicsElements { get; set; } // todo: probably change to IEnumerable + List GraphicsElements { get; set; } // todo: probably change to IEnumerable } public interface IImageTableNameProvider { - public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value); + bool TryGetImageName(T model, int id, [MaybeNullWhen(false)] out string value) where T : ILocoStruct; +} + +public interface IImageTableNameProvider where T : ILocoStruct +{ + bool TryGetImageName(T model, int id, [MaybeNullWhen(false)] out string value); +} + +public abstract class ImageTableNamer : IImageTableNameProvider, IImageTableNameProvider where T : ILocoStruct +{ + public abstract bool TryGetImageName(T model, int id, [MaybeNullWhen(false)] out string value); + + public bool TryGetImageName(T1 model, int id, [MaybeNullWhen(false)] out string value) where T1 : ILocoStruct + { + if (model is T tModel) + { + return TryGetImageName(tModel, id, out value); // call your specialized method + } + + value = DefaultImageTableNameProvider.GetImageName(id); + return false; + } } public class DefaultImageTableNameProvider : IImageTableNameProvider { - public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) + public static DefaultImageTableNameProvider Instance { get; } = new(); + + public bool TryGetImageName(T model, int id, [MaybeNullWhen(false)] out string value) where T : ILocoStruct { value = GetImageName(id); return true; diff --git a/Definitions/ObjectModels/ImageTableGrouper.cs b/Definitions/ObjectModels/ImageTableGrouper.cs new file mode 100644 index 00000000..93ce2a4f --- /dev/null +++ b/Definitions/ObjectModels/ImageTableGrouper.cs @@ -0,0 +1,2101 @@ +using Definitions.ObjectModels.Objects.Cargo; +using Definitions.ObjectModels.Objects.CliffEdge; +using Definitions.ObjectModels.Objects.Competitor; +using Definitions.ObjectModels.Objects.HillShape; +using Definitions.ObjectModels.Objects.InterfaceSkin; +using Definitions.ObjectModels.Objects.Land; +using Definitions.ObjectModels.Objects.Road; +using Definitions.ObjectModels.Objects.RoadStation; +using Definitions.ObjectModels.Objects.Streetlight; +using Definitions.ObjectModels.Objects.Track; +using Definitions.ObjectModels.Objects.TrackExtra; +using Definitions.ObjectModels.Objects.TrackSignal; +using Definitions.ObjectModels.Objects.TrackStation; +using Definitions.ObjectModels.Objects.Wall; +using Definitions.ObjectModels.Objects.Water; +using Definitions.ObjectModels.Types; +using System.Collections.ObjectModel; +using System.Diagnostics.CodeAnalysis; + +namespace Definitions.ObjectModels; + +public static class ImageTableNamer +{ + class CliffEdgeObjectNamer : ImageTableNamer + { + public static CliffEdgeObjectNamer Instance { get; } = new(); + + public override bool TryGetImageName(CliffEdgeObject model, int id, [MaybeNullWhen(false)] out string value) + { + if (id is >= 0 and <= 63) + { + var direction = id / 16 % 2 == 0 ? "west" : "east"; + var side = id is >= 16 and <= 47 ? "right" : "left"; + var level = id % 16; + value = $"south {direction} {side} {level}"; + return true; + } + + if (id is >= 64 and <= 69) + { + return ImageIdNameMap.TryGetValue(id, out value); + } + + value = null; + return false; + } + + static readonly Dictionary ImageIdNameMap = new() + { + { 64, "north west slope 1" }, + { 65, "north west slope 2" }, + { 66, "north west slope 3" }, + { 67, "north east slope 1" }, + { 68, "north east slope 2" }, + { 69, "north east slope 3" }, + }; + } + + class TrackSignalObjectNamer : ImageTableNamer + { + public static TrackSignalObjectNamer Instance { get; } = new(); + + public override bool TryGetImageName(TrackSignalObject model, int id, [MaybeNullWhen(false)] out string value) + => ImageIdNameMap.TryGetValue(id, out value); + + static readonly Dictionary ImageIdNameMap = new() + { + { 80, "redLights" }, + { 88, "redLights2" }, + { 96, "greenLights" }, + { 104, "greenLights2" }, + }; + } + + class WaterObjectNamer : ImageTableNamer + { + public static WaterObjectNamer Instance { get; } = new(); + + public override bool TryGetImageName(WaterObject model, int id, [MaybeNullWhen(false)] out string value) + => ImageIdNameMap.TryGetValue(id, out value); + + static readonly Dictionary ImageIdNameMap = new() + { + { 0, "zoom1 wave overlay full" }, + { 1, "zoom1 wave overlay west" }, + { 2, "zoom1 wave overlay east" }, + { 3, "zoom1 wave overlay north" }, + { 4, "zoom1 wave overlay south" }, + { 5, "zoom1 wave overlay full" }, + { 6, "zoom1 wave half-tile west" }, + { 7, "zoom1 wave half-tile east" }, + { 8, "zoom1 wave half-tile north" }, + { 9, "zoom1 wave half-tile south" }, + { 10, "zoom2 wave overlay full" }, + { 11, "zoom2 wave overlay west" }, + { 12, "zoom2 wave overlay east" }, + { 13, "zoom2 wave overlay north" }, + { 14, "zoom2 wave overlay south" }, + { 15, "zoom2 wave overlay full" }, + { 16, "zoom2 wave half-tile west" }, + { 17, "zoom2 wave half-tile east" }, + { 18, "zoom2 wave half-tile north" }, + { 19, "zoom2 wave half-tile south" }, + { 20, "zoom3 wave overlay full" }, + { 21, "zoom3 wave overlay west" }, + { 22, "zoom3 wave overlay east" }, + { 23, "zoom3 wave overlay north" }, + { 24, "zoom3 wave overlay south" }, + { 25, "zoom3 wave overlay full" }, + { 26, "zoom3 wave half-tile west" }, + { 27, "zoom3 wave half-tile east" }, + { 28, "zoom3 wave half-tile north" }, + { 29, "zoom3 wave half-tile south" }, + { 30, "zoom4 wave overlay full" }, + { 31, "zoom4 wave overlay west" }, + { 32, "zoom4 wave overlay east" }, + { 33, "zoom4 wave overlay north" }, + { 34, "zoom4 wave overlay south" }, + { 35, "zoom4 wave overlay full" }, + { 36, "zoom4 wave half-tile west" }, + { 37, "zoom4 wave half-tile east" }, + { 38, "zoom4 wave half-tile north" }, + { 39, "zoom4 wave half-tile south" }, + { 40, "minimap palette" }, + { 41, "water colour palette" }, + { 42, "water icon animation 0" }, + { 43, "water icon animation 1" }, + { 44, "water icon animation 2" }, + { 45, "water icon animation 3" }, + { 46, "water icon animation 4" }, + { 47, "water icon animation 5" }, + { 48, "water icon animation 6" }, + { 49, "water icon animation 7" }, + { 50, "water icon animation 8" }, + { 51, "water icon animation 9" }, + { 52, "water icon animation 10" }, + { 54, "water icon animation 11" }, + { 55, "water icon animation 12" }, + { 56, "water icon animation 13" }, + { 57, "water icon animation 14" }, + { 53, "water icon animation 15" }, + { 58, "pick up water vehicle" }, + { 59, "place down water vehicle" }, + { 60, "water animation frame 0" }, + { 61, "water animation frame 1" }, + { 62, "water animation frame 2" }, + { 63, "water animation frame 3" }, + { 64, "water animation frame 4" }, + { 65, "water animation frame 5" }, + { 66, "water animation frame 6" }, + { 67, "water animation frame 7" }, + { 68, "water animation frame 8" }, + { 69, "water animation frame 9" }, + { 70, "water animation frame 10" }, + { 71, "water animation frame 11" }, + { 72, "water animation frame 12" }, + { 73, "water animation frame 13" }, + { 74, "water animation frame 14" }, + { 75, "water animation frame 15" }, + }; + } + + class WallObjectNamer : ImageTableNamer + { + public static WallObjectNamer Instance { get; } = new(); + + public override bool TryGetImageName(WallObject model, int id, [MaybeNullWhen(false)] out string value) + { + var result = ImageIdNameMap.TryGetValue(id, out value); + + if (id is >= 0 and <= 5 && model.Flags1.HasFlag(WallObjectFlags1.DoubleSided)) + { + value = $"{value} back"; + } + + if (id is >= 6 and <= 11 && model.Flags1.HasFlag(WallObjectFlags1.HasGlass)) + { + value = $"{value} glass overlay"; + } + else + { + value = $"{value} front"; + } + + return result; + } + + static readonly Dictionary ImageIdNameMap = new() + { + { 0, "Flat SE" }, + { 1, "Flat NE" }, + { 2, "Sloped SE" }, + { 3, "Sloped NE" }, + { 4, "Sloped NW" }, + { 5, "Sloped SW" }, + { 6, "Flat SE" }, + { 7, "Flat NE" }, + { 8, "Sloped SE" }, + { 9, "Sloped NE" }, + { 10, "Sloped NW" }, + { 11, "Sloped SW" }, + }; + } + + class InterfaceSkinObjectNamer : ImageTableNamer + { + public static InterfaceSkinObjectNamer Instance { get; } = new(); + + public override bool TryGetImageName(InterfaceSkinObject model, int id, [MaybeNullWhen(false)] out string value) + => ImageIdNameMap.TryGetValue(id, out value); + + static readonly Dictionary ImageIdNameMap = new() + { + { 0, "preview_image" }, + { 1, "toolbar_pause" }, + { 2, "toolbar_pause_hover" }, + { 3, "toolbar_loadsave" }, + { 4, "toolbar_loadsave_hover" }, + { 5, "toolbar_zoom" }, + { 6, "toolbar_zoom_hover" }, + { 7, "toolbar_rotate" }, + { 8, "toolbar_rotate_hover" }, + { 9, "toolbar_terraform" }, + { 10, "toolbar_terraform_hover" }, + { 11, "toolbar_audio_active" }, + { 12, "toolbar_audio_active_hover" }, + { 13, "toolbar_audio_inactive" }, + { 14, "toolbar_audio_inactive_hover" }, + { 15, "toolbar_view" }, + { 16, "toolbar_view_hover" }, + { 17, "toolbar_towns" }, + { 18, "toolbar_towns_hover" }, + { 19, "toolbar_empty_opaque" }, + { 20, "toolbar_empty_opaque_hover" }, + { 21, "toolbar_empty_transparent" }, + { 22, "toolbar_empty_transparent_hover" }, + { 23, "toolbar_industries" }, + { 24, "toolbar_industries_hover" }, + { 25, "toolbar_airports" }, + { 26, "toolbar_airports_hover" }, + { 27, "toolbar_ports" }, + { 28, "toolbar_ports_hover" }, + { 29, "toolbar_cogwheels" }, + { 30, "toolbar_cogwheels_hover" }, + { 31, "toolbar_build_vehicle_train" }, + { 32, "toolbar_build_vehicle_train_hover" }, + { 33, "toolbar_build_vehicle_bus" }, + { 34, "toolbar_build_vehicle_bus_hover" }, + { 35, "toolbar_build_vehicle_truck" }, + { 36, "toolbar_build_vehicle_truck_hover" }, + { 37, "toolbar_build_vehicle_tram" }, + { 38, "toolbar_build_vehicle_tram_hover" }, + { 39, "toolbar_build_vehicle_airplane" }, + { 40, "toolbar_build_vehicle_airplane_hover" }, + { 41, "toolbar_build_vehicle_boat" }, + { 42, "toolbar_build_vehicle_boat_hover" }, + { 43, "toolbar_stations" }, + { 44, "toolbar_stations_hover" }, + { 45, "tab_awards" }, + { 46, "toolbar_menu_airport" }, + { 47, "toolbar_menu_ship_port" }, + { 48, "tab_cargo_ratings" }, + { 49, "tab_colour_scheme_frame0" }, + { 50, "tab_colour_scheme_frame1" }, + { 51, "tab_colour_scheme_frame2" }, + { 52, "tab_colour_scheme_frame3" }, + { 53, "tab_colour_scheme_frame4" }, + { 54, "tab_colour_scheme_frame5" }, + { 55, "tab_colour_scheme_frame6" }, + { 56, "tab_colour_scheme_frame7" }, + { 57, "tab_population_frame0" }, + { 58, "tab_population_frame1" }, + { 59, "tab_population_frame2" }, + { 60, "tab_population_frame3" }, + { 61, "tab_population_frame4" }, + { 62, "tab_population_frame5" }, + { 63, "tab_population_frame6" }, + { 64, "tab_population_frame7" }, + { 65, "tab_performance_index_frame0" }, + { 66, "tab_performance_index_frame1" }, + { 67, "tab_performance_index_frame2" }, + { 68, "tab_performance_index_frame3" }, + { 69, "tab_performance_index_frame4" }, + { 70, "tab_performance_index_frame5" }, + { 71, "tab_performance_index_frame6" }, + { 72, "tab_performance_index_frame7" }, + { 73, "tab_cargo_units_frame0" }, + { 74, "tab_cargo_units_frame1" }, + { 75, "tab_cargo_units_frame2" }, + { 76, "tab_cargo_units_frame3" }, + { 77, "tab_cargo_units_frame4" }, + { 78, "tab_cargo_units_frame5" }, + { 79, "tab_cargo_units_frame6" }, + { 80, "tab_cargo_units_frame7" }, + { 81, "tab_cargo_distance_frame0" }, + { 82, "tab_cargo_distance_frame1" }, + { 83, "tab_cargo_distance_frame2" }, + { 84, "tab_cargo_distance_frame3" }, + { 85, "tab_cargo_distance_frame4" }, + { 86, "tab_cargo_distance_frame5" }, + { 87, "tab_cargo_distance_frame6" }, + { 88, "tab_cargo_distance_frame7" }, + { 89, "tab_production_frame0" }, + { 90, "tab_production_frame1" }, + { 91, "tab_production_frame2" }, + { 92, "tab_production_frame3" }, + { 93, "tab_production_frame4" }, + { 94, "tab_production_frame5" }, + { 95, "tab_production_frame6" }, + { 96, "tab_production_frame7" }, + { 97, "tab_wrench_frame0" }, + { 98, "tab_wrench_frame1" }, + { 99, "tab_wrench_frame2" }, + { 100, "tab_wrench_frame3" }, + { 101, "tab_wrench_frame4" }, + { 102, "tab_wrench_frame5" }, + { 103, "tab_wrench_frame6" }, + { 104, "tab_wrench_frame7" }, + { 105, "tab_wrench_frame8" }, + { 106, "tab_wrench_frame9" }, + { 107, "tab_wrench_frame10" }, + { 108, "tab_wrench_frame11" }, + { 109, "tab_wrench_frame12" }, + { 110, "tab_wrench_frame13" }, + { 111, "tab_wrench_frame14" }, + { 112, "tab_wrench_frame15" }, + { 113, "tab_finances_frame0" }, + { 114, "tab_finances_frame1" }, + { 115, "tab_finances_frame2" }, + { 116, "tab_finances_frame3" }, + { 117, "tab_finances_frame4" }, + { 118, "tab_finances_frame5" }, + { 119, "tab_finances_frame6" }, + { 120, "tab_finances_frame7" }, + { 121, "tab_finances_frame8" }, + { 122, "tab_finances_frame9" }, + { 123, "tab_finances_frame10" }, + { 124, "tab_finances_frame11" }, + { 125, "tab_finances_frame12" }, + { 126, "tab_finances_frame13" }, + { 127, "tab_finances_frame14" }, + { 128, "tab_finances_frame15" }, + { 129, "tab_cup_frame0" }, + { 130, "tab_cup_frame1" }, + { 131, "tab_cup_frame2" }, + { 132, "tab_cup_frame3" }, + { 133, "tab_cup_frame4" }, + { 134, "tab_cup_frame5" }, + { 135, "tab_cup_frame6" }, + { 136, "tab_cup_frame7" }, + { 137, "tab_cup_frame8" }, + { 138, "tab_cup_frame9" }, + { 139, "tab_cup_frame10" }, + { 140, "tab_cup_frame11" }, + { 141, "tab_cup_frame12" }, + { 142, "tab_cup_frame13" }, + { 143, "tab_cup_frame14" }, + { 144, "tab_cup_frame15" }, + { 145, "tab_ratings_frame0" }, + { 146, "tab_ratings_frame1" }, + { 147, "tab_ratings_frame2" }, + { 148, "tab_ratings_frame3" }, + { 149, "tab_ratings_frame4" }, + { 150, "tab_ratings_frame5" }, + { 151, "tab_ratings_frame6" }, + { 152, "tab_ratings_frame7" }, + { 153, "tab_ratings_frame8" }, + { 154, "tab_ratings_frame9" }, + { 155, "tab_ratings_frame10" }, + { 156, "tab_ratings_frame11" }, + { 157, "tab_ratings_frame12" }, + { 158, "tab_ratings_frame13" }, + { 159, "tab_ratings_frame14" }, + { 160, "tab_ratings_frame15" }, + { 161, "tab_transported_frame0" }, + { 162, "tab_transported_frame1" }, + { 163, "tab_transported_frame2" }, + { 164, "tab_transported_frame3" }, + { 165, "tab_transported_frame4" }, + { 166, "tab_transported_frame5" }, + { 167, "tab_transported_frame6" }, + { 168, "tab_cogs_frame0" }, + { 169, "tab_cogs_frame1" }, + { 170, "tab_cogs_frame2" }, + { 171, "tab_cogs_frame3" }, + { 172, "tab_scenario_details" }, + { 173, "tab_company" }, + { 174, "tab_companies" }, + { 175, "toolbar_menu_zoom_in" }, + { 176, "toolbar_menu_zoom_out" }, + { 177, "toolbar_menu_rotate_clockwise" }, + { 178, "toolbar_menu_rotate_anti_clockwise" }, + { 179, "toolbar_menu_plant_trees" }, + { 180, "toolbar_menu_bulldozer" }, + { 181, "tab_company_details" }, + { 182, "all_stations" }, + { 183, "rail_stations" }, + { 184, "road_stations" }, + { 185, "airports" }, + { 186, "ship_ports" }, + { 187, "toolbar_menu_build_walls" }, + { 188, "phone" }, + { 189, "toolbar_menu_towns" }, + { 190, "toolbar_menu_stations" }, + { 191, "toolbar_menu_industries" }, + { 192, "tab_routes_frame_0" }, + { 193, "tab_routes_frame_1" }, + { 194, "tab_routes_frame_2" }, + { 195, "tab_routes_frame_3" }, + { 196, "tab_messages" }, + { 197, "tab_message_settings" }, + { 198, "tab_cargo_delivered_frame0" }, + { 199, "tab_cargo_delivered_frame1" }, + { 200, "tab_cargo_delivered_frame2" }, + { 201, "tab_cargo_delivered_frame3" }, + { 202, "tab_cargo_payment_rates" }, + { 203, "tab_vehicle_train_frame0" }, + { 204, "tab_vehicle_train_frame1" }, + { 205, "tab_vehicle_train_frame2" }, + { 206, "tab_vehicle_train_frame3" }, + { 207, "tab_vehicle_train_frame4" }, + { 208, "tab_vehicle_train_frame5" }, + { 209, "tab_vehicle_train_frame6" }, + { 210, "tab_vehicle_train_frame7" }, + { 211, "tab_vehicle_aircraft_frame0" }, + { 212, "tab_vehicle_aircraft_frame1" }, + { 213, "tab_vehicle_aircraft_frame2" }, + { 214, "tab_vehicle_aircraft_frame3" }, + { 215, "tab_vehicle_aircraft_frame4" }, + { 216, "tab_vehicle_aircraft_frame5" }, + { 217, "tab_vehicle_aircraft_frame6" }, + { 218, "tab_vehicle_aircraft_frame7" }, + { 219, "tab_vehicle_bus_frame0" }, + { 220, "tab_vehicle_bus_frame1" }, + { 221, "tab_vehicle_bus_frame2" }, + { 222, "tab_vehicle_bus_frame3" }, + { 223, "tab_vehicle_bus_frame4" }, + { 224, "tab_vehicle_bus_frame5" }, + { 225, "tab_vehicle_bus_frame6" }, + { 226, "tab_vehicle_bus_frame7" }, + { 227, "tab_vehicle_tram_frame0" }, + { 228, "tab_vehicle_tram_frame1" }, + { 229, "tab_vehicle_tram_frame2" }, + { 230, "tab_vehicle_tram_frame3" }, + { 231, "tab_vehicle_tram_frame4" }, + { 232, "tab_vehicle_tram_frame5" }, + { 233, "tab_vehicle_tram_frame6" }, + { 234, "tab_vehicle_tram_frame7" }, + { 235, "tab_vehicle_truck_frame0" }, + { 236, "tab_vehicle_truck_frame1" }, + { 237, "tab_vehicle_truck_frame2" }, + { 238, "tab_vehicle_truck_frame3" }, + { 239, "tab_vehicle_truck_frame4" }, + { 240, "tab_vehicle_truck_frame5" }, + { 241, "tab_vehicle_truck_frame6" }, + { 242, "tab_vehicle_truck_frame7" }, + { 243, "tab_vehicle_ship_frame0" }, + { 244, "tab_vehicle_ship_frame1" }, + { 245, "tab_vehicle_ship_frame2" }, + { 246, "tab_vehicle_ship_frame3" }, + { 247, "tab_vehicle_ship_frame4" }, + { 248, "tab_vehicle_ship_frame5" }, + { 249, "tab_vehicle_ship_frame6" }, + { 250, "tab_vehicle_ship_frame7" }, + { 251, "build_vehicle_train_frame_0" }, + { 252, "build_vehicle_train_frame_1" }, + { 253, "build_vehicle_train_frame_2" }, + { 254, "build_vehicle_train_frame_3" }, + { 255, "build_vehicle_train_frame_4" }, + { 256, "build_vehicle_train_frame_5" }, + { 257, "build_vehicle_train_frame_6" }, + { 258, "build_vehicle_train_frame_7" }, + { 259, "build_vehicle_train_frame_8" }, + { 260, "build_vehicle_train_frame_9" }, + { 261, "build_vehicle_train_frame_10" }, + { 262, "build_vehicle_train_frame_11" }, + { 263, "build_vehicle_train_frame_12" }, + { 264, "build_vehicle_train_frame_13" }, + { 265, "build_vehicle_train_frame_14" }, + { 266, "build_vehicle_train_frame_15" }, + { 267, "build_vehicle_aircraft_frame_0" }, + { 268, "build_vehicle_aircraft_frame_1" }, + { 269, "build_vehicle_aircraft_frame_2" }, + { 270, "build_vehicle_aircraft_frame_3" }, + { 271, "build_vehicle_aircraft_frame_4" }, + { 272, "build_vehicle_aircraft_frame_5" }, + { 273, "build_vehicle_aircraft_frame_6" }, + { 274, "build_vehicle_aircraft_frame_7" }, + { 275, "build_vehicle_aircraft_frame_8" }, + { 276, "build_vehicle_aircraft_frame_9" }, + { 277, "build_vehicle_aircraft_frame_10" }, + { 278, "build_vehicle_aircraft_frame_11" }, + { 279, "build_vehicle_aircraft_frame_12" }, + { 280, "build_vehicle_aircraft_frame_13" }, + { 281, "build_vehicle_aircraft_frame_14" }, + { 282, "build_vehicle_aircraft_frame_15" }, + { 283, "build_vehicle_bus_frame_0" }, + { 284, "build_vehicle_bus_frame_1" }, + { 285, "build_vehicle_bus_frame_2" }, + { 286, "build_vehicle_bus_frame_3" }, + { 287, "build_vehicle_bus_frame_4" }, + { 288, "build_vehicle_bus_frame_5" }, + { 289, "build_vehicle_bus_frame_6" }, + { 290, "build_vehicle_bus_frame_7" }, + { 291, "build_vehicle_bus_frame_8" }, + { 292, "build_vehicle_bus_frame_9" }, + { 293, "build_vehicle_bus_frame_10" }, + { 294, "build_vehicle_bus_frame_11" }, + { 295, "build_vehicle_bus_frame_12" }, + { 296, "build_vehicle_bus_frame_13" }, + { 297, "build_vehicle_bus_frame_14" }, + { 298, "build_vehicle_bus_frame_15" }, + { 299, "build_vehicle_tram_frame_0" }, + { 300, "build_vehicle_tram_frame_1" }, + { 301, "build_vehicle_tram_frame_2" }, + { 302, "build_vehicle_tram_frame_3" }, + { 303, "build_vehicle_tram_frame_4" }, + { 304, "build_vehicle_tram_frame_5" }, + { 305, "build_vehicle_tram_frame_6" }, + { 306, "build_vehicle_tram_frame_7" }, + { 307, "build_vehicle_tram_frame_8" }, + { 308, "build_vehicle_tram_frame_9" }, + { 309, "build_vehicle_tram_frame_10" }, + { 310, "build_vehicle_tram_frame_11" }, + { 311, "build_vehicle_tram_frame_12" }, + { 312, "build_vehicle_tram_frame_13" }, + { 313, "build_vehicle_tram_frame_14" }, + { 314, "build_vehicle_tram_frame_15" }, + { 315, "build_vehicle_truck_frame_0" }, + { 316, "build_vehicle_truck_frame_1" }, + { 317, "build_vehicle_truck_frame_2" }, + { 318, "build_vehicle_truck_frame_3" }, + { 319, "build_vehicle_truck_frame_4" }, + { 320, "build_vehicle_truck_frame_5" }, + { 321, "build_vehicle_truck_frame_6" }, + { 322, "build_vehicle_truck_frame_7" }, + { 323, "build_vehicle_truck_frame_8" }, + { 324, "build_vehicle_truck_frame_9" }, + { 325, "build_vehicle_truck_frame_10" }, + { 326, "build_vehicle_truck_frame_11" }, + { 327, "build_vehicle_truck_frame_12" }, + { 328, "build_vehicle_truck_frame_13" }, + { 329, "build_vehicle_truck_frame_14" }, + { 330, "build_vehicle_truck_frame_15" }, + { 331, "build_vehicle_ship_frame_0" }, + { 332, "build_vehicle_ship_frame_1" }, + { 333, "build_vehicle_ship_frame_2" }, + { 334, "build_vehicle_ship_frame_3" }, + { 335, "build_vehicle_ship_frame_4" }, + { 336, "build_vehicle_ship_frame_5" }, + { 337, "build_vehicle_ship_frame_6" }, + { 338, "build_vehicle_ship_frame_7" }, + { 339, "build_vehicle_ship_frame_8" }, + { 340, "build_vehicle_ship_frame_9" }, + { 341, "build_vehicle_ship_frame_10" }, + { 342, "build_vehicle_ship_frame_11" }, + { 343, "build_vehicle_ship_frame_12" }, + { 344, "build_vehicle_ship_frame_13" }, + { 345, "build_vehicle_ship_frame_14" }, + { 346, "build_vehicle_ship_frame_15" }, + { 347, "build_industry_frame_0" }, + { 348, "build_industry_frame_1" }, + { 349, "build_industry_frame_2" }, + { 350, "build_industry_frame_3" }, + { 351, "build_industry_frame_4" }, + { 352, "build_industry_frame_5" }, + { 353, "build_industry_frame_6" }, + { 354, "build_industry_frame_7" }, + { 355, "build_industry_frame_8" }, + { 356, "build_industry_frame_9" }, + { 357, "build_industry_frame_10" }, + { 358, "build_industry_frame_11" }, + { 359, "build_industry_frame_12" }, + { 360, "build_industry_frame_13" }, + { 361, "build_industry_frame_14" }, + { 362, "build_industry_frame_15" }, + { 363, "build_town_frame_0" }, + { 364, "build_town_frame_1" }, + { 365, "build_town_frame_2" }, + { 366, "build_town_frame_3" }, + { 367, "build_town_frame_4" }, + { 368, "build_town_frame_5" }, + { 369, "build_town_frame_6" }, + { 370, "build_town_frame_7" }, + { 371, "build_town_frame_8" }, + { 372, "build_town_frame_9" }, + { 373, "build_town_frame_10" }, + { 374, "build_town_frame_11" }, + { 375, "build_town_frame_12" }, + { 376, "build_town_frame_13" }, + { 377, "build_town_frame_14" }, + { 378, "build_town_frame_15" }, + { 379, "build_buildings_frame_0" }, + { 380, "build_buildings_frame_1" }, + { 381, "build_buildings_frame_2" }, + { 382, "build_buildings_frame_3" }, + { 383, "build_buildings_frame_4" }, + { 384, "build_buildings_frame_5" }, + { 385, "build_buildings_frame_6" }, + { 386, "build_buildings_frame_7" }, + { 387, "build_buildings_frame_8" }, + { 388, "build_buildings_frame_9" }, + { 389, "build_buildings_frame_10" }, + { 390, "build_buildings_frame_11" }, + { 391, "build_buildings_frame_12" }, + { 392, "build_buildings_frame_13" }, + { 393, "build_buildings_frame_14" }, + { 394, "build_buildings_frame_15" }, + { 395, "build_misc_buildings_frame_0" }, + { 396, "build_misc_buildings_frame_1" }, + { 397, "build_misc_buildings_frame_2" }, + { 398, "build_misc_buildings_frame_3" }, + { 399, "build_misc_buildings_frame_4" }, + { 400, "build_misc_buildings_frame_5" }, + { 401, "build_misc_buildings_frame_6" }, + { 402, "build_misc_buildings_frame_7" }, + { 403, "build_misc_buildings_frame_8" }, + { 404, "build_misc_buildings_frame_9" }, + { 405, "build_misc_buildings_frame_10" }, + { 406, "build_misc_buildings_frame_11" }, + { 407, "build_misc_buildings_frame_12" }, + { 408, "build_misc_buildings_frame_13" }, + { 409, "build_misc_buildings_frame_14" }, + { 410, "build_misc_buildings_frame_15" }, + { 411, "build_additional_train" }, + { 412, "build_additional_bus" }, + { 413, "build_additional_truck" }, + { 414, "build_additional_tram" }, + { 415, "build_additional_aircraft" }, + { 416, "build_additional_ship" }, + { 417, "build_headquarters" }, + { 418, "vehicle_train_frame_0" }, + { 419, "vehicle_train_frame_1" }, + { 420, "vehicle_train_frame_2" }, + { 421, "vehicle_train_frame_3" }, + { 422, "vehicle_train_frame_4" }, + { 423, "vehicle_train_frame_5" }, + { 424, "vehicle_train_frame_6" }, + { 425, "vehicle_train_frame_7" }, + { 426, "vehicle_aircraft_frame_0" }, + { 427, "vehicle_aircraft_frame_1" }, + { 428, "vehicle_aircraft_frame_2" }, + { 429, "vehicle_aircraft_frame_3" }, + { 430, "vehicle_aircraft_frame_4" }, + { 431, "vehicle_aircraft_frame_5" }, + { 432, "vehicle_aircraft_frame_6" }, + { 433, "vehicle_aircraft_frame_7" }, + { 434, "vehicle_buses_frame_0" }, + { 435, "vehicle_buses_frame_1" }, + { 436, "vehicle_buses_frame_2" }, + { 437, "vehicle_buses_frame_3" }, + { 438, "vehicle_buses_frame_4" }, + { 439, "vehicle_buses_frame_5" }, + { 440, "vehicle_buses_frame_6" }, + { 441, "vehicle_buses_frame_7" }, + { 442, "vehicle_trams_frame_0" }, + { 443, "vehicle_trams_frame_1" }, + { 444, "vehicle_trams_frame_2" }, + { 445, "vehicle_trams_frame_3" }, + { 446, "vehicle_trams_frame_4" }, + { 447, "vehicle_trams_frame_5" }, + { 448, "vehicle_trams_frame_6" }, + { 449, "vehicle_trams_frame_7" }, + { 450, "vehicle_trucks_frame_0" }, + { 451, "vehicle_trucks_frame_1" }, + { 452, "vehicle_trucks_frame_2" }, + { 453, "vehicle_trucks_frame_3" }, + { 454, "vehicle_trucks_frame_4" }, + { 455, "vehicle_trucks_frame_5" }, + { 456, "vehicle_trucks_frame_6" }, + { 457, "vehicle_trucks_frame_7" }, + { 458, "vehicle_ships_frame_0" }, + { 459, "vehicle_ships_frame_1" }, + { 460, "vehicle_ships_frame_2" }, + { 461, "vehicle_ships_frame_3" }, + { 462, "vehicle_ships_frame_4" }, + { 463, "vehicle_ships_frame_5" }, + { 464, "vehicle_ships_frame_6" }, + { 465, "vehicle_ships_frame_7" }, + { 466, "toolbar_menu_map_north" }, + { 467, "toolbar_menu_map_west" }, + { 468, "toolbar_menu_map_south" }, + { 469, "toolbar_menu_map_east" }, + }; + } + + class CargoObjectNamer : ImageTableNamer + { + public static CargoObjectNamer Instance { get; } = new(); + + public override bool TryGetImageName(CargoObject model, int id, [MaybeNullWhen(false)] out string value) + { + value = id == 0 + ? "kInlineSprite" + : $"kStationPlatform{id}"; + + return true; + } + } + + class CompetitorObjectNamer : ImageTableNamer + { + public static CompetitorObjectNamer Instance { get; } = new(); + + public override bool TryGetImageName(CompetitorObject model, int id, [MaybeNullWhen(false)] out string value) + => ImageIdNameMap.TryGetValue(id, out value); + + static readonly Dictionary ImageIdNameMap = new() + { + { 0, "smallNeutral" }, + { 1, "largeNeutral" }, + { 2, "smallHappy" }, + { 3, "largeHappy" }, + { 4, "smallWorried" }, + { 5, "largeWorried" }, + { 6, "smallThinking" }, + { 7, "largeThinking" }, + { 8, "smallDejected" }, + { 9, "largeDejected" }, + { 10, "smallSurprised" }, + { 11, "largeSurprised" }, + { 12, "smallScared" }, + { 13, "largeScared" }, + { 14, "smallAngry" }, + { 15, "largeAngry" }, + { 16, "smallDisgusted" }, + { 17, "largeDisgusted" }, + }; + } + + class HillShapesObjectNamer : ImageTableNamer + { + public static HillShapesObjectNamer Instance { get; } = new(); + + public override bool TryGetImageName(HillShapesObject model, int id, [MaybeNullWhen(false)] out string value) + => ImageIdNameMap.TryGetValue(id, out value); + + static readonly Dictionary ImageIdNameMap = new() + { + { 0, "hill shape 1" }, + { 1, "hill shape 2" }, + { 2, "mountain shape 1" }, + { 3, "mountain shape 2" }, + { 4, "preview image" }, + + }; + } + + class LandObjectNamer : ImageTableNamer + { + public static LandObjectNamer Instance { get; } = new(); + + public override bool TryGetImageName(LandObject model, int id, [MaybeNullWhen(false)] out string value) + => ImageIdNameMap.TryGetValue(id, out value); + + static readonly Dictionary ImageIdNameMap = new() + { + { 0, "flat" }, + { 1, "west corner up" }, + { 2, "south corner up" }, + { 3, "north east slope" }, + { 4, "east corner up" }, + { 5, "west and east corner up" }, + { 6, "north west slope" }, + { 7, "north corner down" }, + { 8, "north corner up" }, + { 9, "south east slope" }, + { 10, "north and south corners up" }, + { 11, "east corner down" }, + { 12, "north west slope" }, + { 13, "south corner down" }, + { 14, "west corner down" }, + { 15, "south slope" }, + { 16, "north slope" }, + { 17, "east slope" }, + { 18, "west slope" } + }; + } + + class StreetLightObjectNamer : ImageTableNamer + { + public static StreetLightObjectNamer Instance { get; } = new(); + + public override bool TryGetImageName(StreetLightObject model, int id, [MaybeNullWhen(false)] out string value) + => ImageIdNameMap.TryGetValue(id, out value); + + static readonly Dictionary ImageIdNameMap = new() + { + { 0, "style0NE" }, + { 1, "style0SE" }, + { 2, "style0SW" }, + { 3, "style0NW" }, + { 4, "style1NE" }, + { 5, "style1SE" }, + { 6, "style1SW" }, + { 7, "style1NW" }, + { 8, "style2NE" }, + { 9, "style2SE" }, + { 10, "style2SW" }, + { 11, "style2NW" }, + }; + } + + class RoadStationObjectNamer : ImageTableNamer + { + public static RoadStationObjectNamer Instance { get; } = new(); + + public override bool TryGetImageName(RoadStationObject model, int id, [MaybeNullWhen(false)] out string value) + => ImageIdNameMap.TryGetValue(id, out value); + + static readonly Dictionary ImageIdNameMap = new() + { + { 0, "preview_image" }, + { 1, "preview_image_glass_overlay" }, + { 2, "North West Back Wall" }, + { 3, "North West Front Platform" }, + { 4, "North West Front Wall/Roof" }, + { 5, "North West Glass Overlay" }, + { 6, "South West Back Wall" }, + { 7, "South West Front Platform" }, + { 8, "South West Front Wall/Roof" }, + { 9, "South West Glass Overlay" }, + { 10, "South East Back Wall" }, + { 11, "South East Front Platform" }, + { 12, "South East Front Wall/Roof" }, + { 13, "South East Glass Overlay" }, + { 14, "North East Back Wall" }, + { 15, "North East Front Platform" }, + { 16, "North East Front Wall/Roof" }, + { 17, "North East Glass Overlay" }, + }; + } + + class TrackStationObjectNamer : ImageTableNamer + { + public static TrackStationObjectNamer Instance { get; } = new(); + + public override bool TryGetImageName(TrackStationObject model, int id, [MaybeNullWhen(false)] out string value) + => ImageIdNameMap.TryGetValue(id, out value); + + static readonly Dictionary ImageIdNameMap = new() + { + { 0, "preview_image" }, + { 1, "preview_image_glass_overlay" }, + { 2, "totalPreviewImages" }, + }; + + // These are relative to ImageOffsets + // ImageOffsets is the imageIds per sequenceIndex (for start/middle/end of the platform) + //namespace Style0 + //{ + // constexpr uint32_t straightBackNE = 0; + // constexpr uint32_t straightFrontNE = 1; + // constexpr uint32_t straightCanopyNE = 2; + // constexpr uint32_t straightCanopyTranslucentNE = 3; + // constexpr uint32_t straightBackSE = 4; + // constexpr uint32_t straightFrontSE = 5; + // constexpr uint32_t straightCanopySE = 6; + // constexpr uint32_t straightCanopyTranslucentSE = 7; + // constexpr uint32_t diagonalNE0 = 8; + // constexpr uint32_t diagonalNE3 = 9; + // constexpr uint32_t diagonalNE1 = 10; + // constexpr uint32_t diagonalCanopyNE1 = 11; + // constexpr uint32_t diagonalCanopyTranslucentNE1 = 12; + // constexpr uint32_t diagonalSE1 = 13; + // constexpr uint32_t diagonalSE2 = 14; + // constexpr uint32_t diagonalSE3 = 15; + // constexpr uint32_t diagonalCanopyTranslucentSE3 = 16; + // constexpr uint32_t totalNumImages = 17; + //} + //namespace Style1 + //{ + // constexpr uint32_t totalNumImages = 8; + //} + } + + class TrackExtraObjectNamer : ImageTableNamer + { + public static TrackExtraObjectNamer Instance { get; } = new(); + + public override bool TryGetImageName(TrackExtraObject model, int id, [MaybeNullWhen(false)] out string value) + => ImageIdNameMap.TryGetValue(id - 8, out value); + + static readonly Dictionary ImageIdNameMap = new() + { + { -8, "previewImage0" }, + { -7, "previewImage1" }, + { -6, "previewImage2" }, + { -5, "previewImage3" }, + { -4, "previewImage4" }, + { -3, "previewImage5" }, + { -2, "previewImage6" }, + { -1, "previewImage7" }, + { 0, "kStraight0NE" }, + { 1, "kStraight0SE" }, + { 2, "kStraight0SW" }, + { 3, "kStraight0NW" }, + { 4, "kRightCurveSmall0NE" }, + { 5, "kRightCurveSmall1NE" }, + { 6, "kRightCurveSmall2NE" }, + { 7, "kRightCurveSmall3NE" }, + { 8, "kRightCurveSmall0SE" }, + { 9, "kRightCurveSmall1SE" }, + { 10, "kRightCurveSmall2SE" }, + { 11, "kRightCurveSmall3SE" }, + { 12, "kRightCurveSmall0SW" }, + { 13, "kRightCurveSmall1SW" }, + { 14, "kRightCurveSmall2SW" }, + { 15, "kRightCurveSmall3SW" }, + { 16, "kRightCurveSmall0NW" }, + { 17, "kRightCurveSmall1NW" }, + { 18, "kRightCurveSmall2NW" }, + { 19, "kRightCurveSmall3NW" }, + { 20, "kRightCurve0NE" }, + { 21, "kRightCurve1NE" }, + { 22, "kRightCurve2NE" }, + { 23, "kRightCurve3NE" }, + { 24, "kRightCurve4NE" }, + { 25, "kRightCurve0SE" }, + { 26, "kRightCurve1SE" }, + { 27, "kRightCurve2SE" }, + { 28, "kRightCurve3SE" }, + { 29, "kRightCurve4SE" }, + { 30, "kRightCurve0SW" }, + { 31, "kRightCurve1SW" }, + { 32, "kRightCurve2SW" }, + { 33, "kRightCurve3SW" }, + { 34, "kRightCurve4SW" }, + { 35, "kRightCurve0NW" }, + { 36, "kRightCurve1NW" }, + { 37, "kRightCurve2NW" }, + { 38, "kRightCurve3NW" }, + { 39, "kRightCurve4NW" }, + { 40, "kSBendLeft0NE" }, + { 41, "kSBendLeft1NE" }, + { 42, "kSBendLeft2NE" }, + { 43, "kSBendLeft3NE" }, + { 44, "kSBendLeft0SE" }, + { 45, "kSBendLeft1SE" }, + { 46, "kSBendLeft2SE" }, + { 47, "kSBendLeft3SE" }, + { 48, "kSBendLeft3SW" }, + { 49, "kSBendLeft2SW" }, + { 50, "kSBendLeft1SW" }, + { 51, "kSBendLeft0SW" }, + { 52, "kSBendLeft3NW" }, + { 53, "kSBendLeft2NW" }, + { 54, "kSBendLeft1NW" }, + { 55, "kSBendLeft0NW" }, + { 56, "kSBendRight0NE" }, + { 57, "kSBendRight1NE" }, + { 58, "kSBendRight2NE" }, + { 59, "kSBendRight3NE" }, + { 60, "kSBendRight0SE" }, + { 61, "kSBendRight1SE" }, + { 62, "kSBendRight2SE" }, + { 63, "kSBendRight3SE" }, + { 64, "kSBendRight3SW" }, + { 65, "kSBendRight2SW" }, + { 66, "kSBendRight1SW" }, + { 67, "kSBendRight0SW" }, + { 68, "kSBendRight3NW" }, + { 69, "kSBendRight2NW" }, + { 70, "kSBendRight1NW" }, + { 71, "kSBendRight0NW" }, + { 72, "kStraightSlopeUp0NE" }, + { 73, "kStraightSlopeUp1NE" }, + { 74, "kStraightSlopeUp0SE" }, + { 75, "kStraightSlopeUp1SE" }, + { 76, "kStraightSlopeUp0SW" }, + { 77, "kStraightSlopeUp1SW" }, + { 78, "kStraightSlopeUp0NW" }, + { 79, "kStraightSlopeUp1NW" }, + { 80, "kStraightSteepSlopeUp0NE" }, + { 81, "kStraightSteepSlopeUp0SE" }, + { 82, "kStraightSteepSlopeUp0SW" }, + { 83, "kStraightSteepSlopeUp0NW" }, + { 84, "kRightCurveSmallSlopeUp0NE" }, + { 85, "kRightCurveSmallSlopeUp1NE" }, + { 86, "kRightCurveSmallSlopeUp2NE" }, + { 87, "kRightCurveSmallSlopeUp3NE" }, + { 88, "kRightCurveSmallSlopeUp0SE" }, + { 89, "kRightCurveSmallSlopeUp1SE" }, + { 90, "kRightCurveSmallSlopeUp2SE" }, + { 91, "kRightCurveSmallSlopeUp3SE" }, + { 92, "kRightCurveSmallSlopeUp0SW" }, + { 93, "kRightCurveSmallSlopeUp1SW" }, + { 94, "kRightCurveSmallSlopeUp2SW" }, + { 95, "kRightCurveSmallSlopeUp3SW" }, + { 96, "kRightCurveSmallSlopeUp0NW" }, + { 97, "kRightCurveSmallSlopeUp1NW" }, + { 98, "kRightCurveSmallSlopeUp2NW" }, + { 99, "kRightCurveSmallSlopeUp3NW" }, + { 100, "kRightCurveSmallSlopeDown0NE" }, + { 101, "kRightCurveSmallSlopeDown1NE" }, + { 102, "kRightCurveSmallSlopeDown2NE" }, + { 103, "kRightCurveSmallSlopeDown3NE" }, + { 104, "kRightCurveSmallSlopeDown0SE" }, + { 105, "kRightCurveSmallSlopeDown1SE" }, + { 106, "kRightCurveSmallSlopeDown2SE" }, + { 107, "kRightCurveSmallSlopeDown3SE" }, + { 108, "kRightCurveSmallSlopeDown0SW" }, + { 109, "kRightCurveSmallSlopeDown1SW" }, + { 110, "kRightCurveSmallSlopeDown2SW" }, + { 111, "kRightCurveSmallSlopeDown3SW" }, + { 112, "kRightCurveSmallSlopeDown0NW" }, + { 113, "kRightCurveSmallSlopeDown1NW" }, + { 114, "kRightCurveSmallSlopeDown2NW" }, + { 115, "kRightCurveSmallSlopeDown3NW" }, + { 116, "kRightCurveSmallSteepSlopeUp0NE" }, + { 117, "kRightCurveSmallSteepSlopeUp1NE" }, + { 118, "kRightCurveSmallSteepSlopeUp2NE" }, + { 119, "kRightCurveSmallSteepSlopeUp3NE" }, + { 120, "kRightCurveSmallSteepSlopeUp0SE" }, + { 121, "kRightCurveSmallSteepSlopeUp1SE" }, + { 122, "kRightCurveSmallSteepSlopeUp2SE" }, + { 123, "kRightCurveSmallSteepSlopeUp3SE" }, + { 124, "kRightCurveSmallSteepSlopeUp0SW" }, + { 125, "kRightCurveSmallSteepSlopeUp1SW" }, + { 126, "kRightCurveSmallSteepSlopeUp2SW" }, + { 127, "kRightCurveSmallSteepSlopeUp3SW" }, + { 128, "kRightCurveSmallSteepSlopeUp0NW" }, + { 129, "kRightCurveSmallSteepSlopeUp1NW" }, + { 130, "kRightCurveSmallSteepSlopeUp2NW" }, + { 131, "kRightCurveSmallSteepSlopeUp3NW" }, + { 132, "kRightCurveSmallSteepSlopeDown0NE" }, + { 133, "kRightCurveSmallSteepSlopeDown1NE" }, + { 134, "kRightCurveSmallSteepSlopeDown2NE" }, + { 135, "kRightCurveSmallSteepSlopeDown3NE" }, + { 136, "kRightCurveSmallSteepSlopeDown0SE" }, + { 137, "kRightCurveSmallSteepSlopeDown1SE" }, + { 138, "kRightCurveSmallSteepSlopeDown2SE" }, + { 139, "kRightCurveSmallSteepSlopeDown3SE" }, + { 140, "kRightCurveSmallSteepSlopeDown0SW" }, + { 141, "kRightCurveSmallSteepSlopeDown1SW" }, + { 142, "kRightCurveSmallSteepSlopeDown2SW" }, + { 143, "kRightCurveSmallSteepSlopeDown3SW" }, + { 144, "kRightCurveSmallSteepSlopeDown0NW" }, + { 145, "kRightCurveSmallSteepSlopeDown1NW" }, + { 146, "kRightCurveSmallSteepSlopeDown2NW" }, + { 147, "kRightCurveSmallSteepSlopeDown3NW" }, + { 148, "kRightCurveLarge0NE" }, + { 149, "kRightCurveLarge1NE" }, + { 150, "kRightCurveLarge2NE" }, + { 151, "kRightCurveLarge3NE" }, + { 152, "kRightCurveLarge4NE" }, + { 153, "kRightCurveLarge0SE" }, + { 154, "kRightCurveLarge1SE" }, + { 155, "kRightCurveLarge2SE" }, + { 156, "kRightCurveLarge3SE" }, + { 157, "kRightCurveLarge4SE" }, + { 158, "kRightCurveLarge0SW" }, + { 159, "kRightCurveLarge1SW" }, + { 160, "kRightCurveLarge2SW" }, + { 161, "kRightCurveLarge3SW" }, + { 162, "kRightCurveLarge4SW" }, + { 163, "kRightCurveLarge0NW" }, + { 164, "kRightCurveLarge1NW" }, + { 165, "kRightCurveLarge2NW" }, + { 166, "kRightCurveLarge3NW" }, + { 167, "kRightCurveLarge4NW" }, + { 168, "kLeftCurveLarge0NE" }, + { 169, "kLeftCurveLarge1NE" }, + { 170, "kLeftCurveLarge2NE" }, + { 171, "kLeftCurveLarge3NE" }, + { 172, "kLeftCurveLarge4NE" }, + { 173, "kLeftCurveLarge0SE" }, + { 174, "kLeftCurveLarge1SE" }, + { 175, "kLeftCurveLarge2SE" }, + { 176, "kLeftCurveLarge3SE" }, + { 177, "kLeftCurveLarge4SE" }, + { 178, "kLeftCurveLarge0SW" }, + { 179, "kLeftCurveLarge1SW" }, + { 180, "kLeftCurveLarge2SW" }, + { 181, "kLeftCurveLarge3SW" }, + { 182, "kLeftCurveLarge4SW" }, + { 183, "kLeftCurveLarge0NW" }, + { 184, "kLeftCurveLarge1NW" }, + { 185, "kLeftCurveLarge2NW" }, + { 186, "kLeftCurveLarge3NW" }, + { 187, "kLeftCurveLarge4NW" }, + { 188, "kDiagonal0NE" }, + { 189, "kDiagonal2NE" }, + { 190, "kDiagonal1NE" }, + { 191, "kDiagonal3NE" }, + { 192, "kDiagonal0SE" }, + { 193, "kDiagonal2SE" }, + { 194, "kDiagonal1SE" }, + { 195, "kDiagonal3SE" }, + { 196, "kDiagonal0SW" }, + { 197, "kDiagonal2SW" }, + { 198, "kDiagonal1SW" }, + { 199, "kDiagonal3SW" }, + { 200, "kDiagonal0NW" }, + { 201, "kDiagonal2NW" }, + { 202, "kDiagonal1NW" }, + { 203, "kDiagonal3NW" }, + { 204, "kRightCurveVerySmall0NE" }, + { 205, "kRightCurveVerySmall0SE" }, + { 206, "kRightCurveVerySmall0SW" }, + { 207, "kRightCurveVerySmall0NW" }, + }; + } + + class TrackObjectNamer : ImageTableNamer + { + public static TrackObjectNamer Instance { get; } = new(); + + public override bool TryGetImageName(TrackObject model, int id, [MaybeNullWhen(false)] out string value) + => ImageIdNameMap.TryGetValue(id, out value); + + static readonly Dictionary ImageIdNameMap = new() + { + { 0, "uiPreviewImage0" }, + { 1, "uiPreviewImage1" }, + { 2, "uiPreviewImage2" }, + { 3, "uiPreviewImage3" }, + { 4, "uiPreviewImage4" }, + { 5, "uiPreviewImage5" }, + { 6, "uiPreviewImage6" }, + { 7, "uiPreviewImage7" }, + { 8, "uiPreviewImage8" }, + { 9, "uiPreviewImage9" }, + { 10, "uiPreviewImage10" }, + { 11, "uiPreviewImage11" }, + { 12, "uiPreviewImage12" }, + { 13, "uiPreviewImage13" }, + { 14, "uiPreviewImage14" }, + { 15, "uiPreviewImage15" }, + { 16, "uiPickupFromTrack" }, + { 17, "uiPlaceOnTrack" }, + // + { 18, "straight0BallastNE" }, + { 19, "straight0BallastSE" }, + { 20, "straight0SleeperNE" }, + { 21, "straight0SleeperSE" }, + { 22, "straight0RailNE" }, + { 23, "straight0RailSE" }, + { 24, "rightCurveSmall0BallastNE" }, + { 25, "rightCurveSmall1BallastNE" }, + { 26, "rightCurveSmall2BallastNE" }, + { 27, "rightCurveSmall3BallastNE" }, + { 28, "rightCurveSmall0BallastSE" }, + { 29, "rightCurveSmall1BallastSE" }, + { 30, "rightCurveSmall2BallastSE" }, + { 31, "rightCurveSmall3BallastSE" }, + { 32, "rightCurveSmall0BallastSW" }, + { 33, "rightCurveSmall1BallastSW" }, + { 34, "rightCurveSmall2BallastSW" }, + { 35, "rightCurveSmall3BallastSW" }, + { 36, "rightCurveSmall0BallastNW" }, + { 37, "rightCurveSmall1BallastNW" }, + { 38, "rightCurveSmall2BallastNW" }, + { 39, "rightCurveSmall3BallastNW" }, + { 40, "rightCurveSmall0SleeperNE" }, + { 41, "rightCurveSmall1SleeperNE" }, + { 42, "rightCurveSmall2SleeperNE" }, + { 43, "rightCurveSmall3SleeperNE" }, + { 44, "rightCurveSmall0SleeperSE" }, + { 45, "rightCurveSmall1SleeperSE" }, + { 46, "rightCurveSmall2SleeperSE" }, + { 47, "rightCurveSmall3SleeperSE" }, + { 48, "rightCurveSmall0SleeperSW" }, + { 49, "rightCurveSmall1SleeperSW" }, + { 50, "rightCurveSmall2SleeperSW" }, + { 51, "rightCurveSmall3SleeperSW" }, + { 52, "rightCurveSmall0SleeperNW" }, + { 53, "rightCurveSmall1SleeperNW" }, + { 54, "rightCurveSmall2SleeperNW" }, + { 55, "rightCurveSmall3SleeperNW" }, + { 56, "rightCurveSmall0RailNE" }, + { 57, "rightCurveSmall1RailNE" }, + { 58, "rightCurveSmall2RailNE" }, + { 59, "rightCurveSmall3RailNE" }, + { 60, "rightCurveSmall0RailSE" }, + { 61, "rightCurveSmall1RailSE" }, + { 62, "rightCurveSmall2RailSE" }, + { 63, "rightCurveSmall3RailSE" }, + { 64, "rightCurveSmall0RailSW" }, + { 65, "rightCurveSmall1RailSW" }, + { 66, "rightCurveSmall2RailSW" }, + { 67, "rightCurveSmall3RailSW" }, + { 68, "rightCurveSmall0RailNW" }, + { 69, "rightCurveSmall1RailNW" }, + { 70, "rightCurveSmall2RailNW" }, + { 71, "rightCurveSmall3RailNW" }, + { 72, "rightCurveSmallSlopeUp0NE" }, + { 73, "rightCurveSmallSlopeUp1NE" }, + { 74, "rightCurveSmallSlopeUp2NE" }, + { 75, "rightCurveSmallSlopeUp3NE" }, + { 76, "rightCurveSmallSlopeUp0SE" }, + { 77, "rightCurveSmallSlopeUp1SE" }, + { 78, "rightCurveSmallSlopeUp2SE" }, + { 79, "rightCurveSmallSlopeUp3SE" }, + { 80, "rightCurveSmallSlopeUp0SW" }, + { 81, "rightCurveSmallSlopeUp1SW" }, + { 82, "rightCurveSmallSlopeUp2SW" }, + { 83, "rightCurveSmallSlopeUp3SW" }, + { 84, "rightCurveSmallSlopeUp0NW" }, + { 85, "rightCurveSmallSlopeUp1NW" }, + { 86, "rightCurveSmallSlopeUp2NW" }, + { 87, "rightCurveSmallSlopeUp3NW" }, + { 88, "rightCurveSmallSlopeDown0NE" }, + { 89, "rightCurveSmallSlopeDown1NE" }, + { 90, "rightCurveSmallSlopeDown2NE" }, + { 91, "rightCurveSmallSlopeDown3NE" }, + { 92, "rightCurveSmallSlopeDown0SE" }, + { 93, "rightCurveSmallSlopeDown1SE" }, + { 94, "rightCurveSmallSlopeDown2SE" }, + { 95, "rightCurveSmallSlopeDown3SE" }, + { 96, "rightCurveSmallSlopeDown0SW" }, + { 97, "rightCurveSmallSlopeDown1SW" }, + { 98, "rightCurveSmallSlopeDown2SW" }, + { 99, "rightCurveSmallSlopeDown3SW" }, + { 100, "rightCurveSmallSlopeDown0NW" }, + { 101, "rightCurveSmallSlopeDown1NW" }, + { 102, "rightCurveSmallSlopeDown2NW" }, + { 103, "rightCurveSmallSlopeDown3NW" }, + { 104, "rightCurveSmallSteepSlopeUp0NE" }, + { 105, "rightCurveSmallSteepSlopeUp1NE" }, + { 106, "rightCurveSmallSteepSlopeUp2NE" }, + { 107, "rightCurveSmallSteepSlopeUp3NE" }, + { 108, "rightCurveSmallSteepSlopeUp0SE" }, + { 109, "rightCurveSmallSteepSlopeUp1SE" }, + { 110, "rightCurveSmallSteepSlopeUp2SE" }, + { 111, "rightCurveSmallSteepSlopeUp3SE" }, + { 112, "rightCurveSmallSteepSlopeUp0SW" }, + { 113, "rightCurveSmallSteepSlopeUp1SW" }, + { 114, "rightCurveSmallSteepSlopeUp2SW" }, + { 115, "rightCurveSmallSteepSlopeUp3SW" }, + { 116, "rightCurveSmallSteepSlopeUp0NW" }, + { 117, "rightCurveSmallSteepSlopeUp1NW" }, + { 118, "rightCurveSmallSteepSlopeUp2NW" }, + { 119, "rightCurveSmallSteepSlopeUp3NW" }, + { 120, "rightCurveSmallSteepSlopeDown0NE" }, + { 121, "rightCurveSmallSteepSlopeDown1NE" }, + { 122, "rightCurveSmallSteepSlopeDown2NE" }, + { 123, "rightCurveSmallSteepSlopeDown3NE" }, + { 124, "rightCurveSmallSteepSlopeDown0SE" }, + { 125, "rightCurveSmallSteepSlopeDown1SE" }, + { 126, "rightCurveSmallSteepSlopeDown2SE" }, + { 127, "rightCurveSmallSteepSlopeDown3SE" }, + { 128, "rightCurveSmallSteepSlopeDown0SW" }, + { 129, "rightCurveSmallSteepSlopeDown1SW" }, + { 130, "rightCurveSmallSteepSlopeDown2SW" }, + { 131, "rightCurveSmallSteepSlopeDown3SW" }, + { 132, "rightCurveSmallSteepSlopeDown0NW" }, + { 133, "rightCurveSmallSteepSlopeDown1NW" }, + { 134, "rightCurveSmallSteepSlopeDown2NW" }, + { 135, "rightCurveSmallSteepSlopeDown3NW" }, + { 136, "rightCurve0BallastNE" }, + { 137, "rightCurve1BallastNE" }, + { 138, "rightCurve2BallastNE" }, + { 139, "rightCurve3BallastNE" }, + { 140, "rightCurve4BallastNE" }, + { 141, "rightCurve0BallastSE" }, + { 142, "rightCurve1BallastSE" }, + { 143, "rightCurve2BallastSE" }, + { 144, "rightCurve3BallastSE" }, + { 145, "rightCurve4BallastSE" }, + { 146, "rightCurve0BallastSW" }, + { 147, "rightCurve1BallastSW" }, + { 148, "rightCurve2BallastSW" }, + { 149, "rightCurve3BallastSW" }, + { 150, "rightCurve4BallastSW" }, + { 151, "rightCurve0BallastNW" }, + { 152, "rightCurve1BallastNW" }, + { 153, "rightCurve2BallastNW" }, + { 154, "rightCurve3BallastNW" }, + { 155, "rightCurve4BallastNW" }, + { 156, "rightCurve0SleeperNE" }, + { 157, "rightCurve1SleeperNE" }, + { 158, "rightCurve2SleeperNE" }, + { 159, "rightCurve3SleeperNE" }, + { 160, "rightCurve4SleeperNE" }, + { 161, "rightCurve0SleeperSE" }, + { 162, "rightCurve1SleeperSE" }, + { 163, "rightCurve2SleeperSE" }, + { 164, "rightCurve3SleeperSE" }, + { 165, "rightCurve4SleeperSE" }, + { 166, "rightCurve0SleeperSW" }, + { 167, "rightCurve1SleeperSW" }, + { 168, "rightCurve2SleeperSW" }, + { 169, "rightCurve3SleeperSW" }, + { 170, "rightCurve4SleeperSW" }, + { 171, "rightCurve0SleeperNW" }, + { 172, "rightCurve1SleeperNW" }, + { 173, "rightCurve2SleeperNW" }, + { 174, "rightCurve3SleeperNW" }, + { 175, "rightCurve4SleeperNW" }, + { 176, "rightCurve0RailNE" }, + { 177, "rightCurve1RailNE" }, + { 178, "rightCurve2RailNE" }, + { 179, "rightCurve3RailNE" }, + { 180, "rightCurve4RailNE" }, + { 181, "rightCurve0RailSE" }, + { 182, "rightCurve1RailSE" }, + { 183, "rightCurve2RailSE" }, + { 184, "rightCurve3RailSE" }, + { 185, "rightCurve4RailSE" }, + { 186, "rightCurve0RailSW" }, + { 187, "rightCurve1RailSW" }, + { 188, "rightCurve2RailSW" }, + { 189, "rightCurve3RailSW" }, + { 190, "rightCurve4RailSW" }, + { 191, "rightCurve0RailNW" }, + { 192, "rightCurve1RailNW" }, + { 193, "rightCurve2RailNW" }, + { 194, "rightCurve3RailNW" }, + { 195, "rightCurve4RailNW" }, + { 196, "straightSlopeUp0NE" }, + { 197, "straightSlopeUp1NE" }, + { 198, "straightSlopeUp0SE" }, + { 199, "straightSlopeUp1SE" }, + { 200, "straightSlopeUp0SW" }, + { 201, "straightSlopeUp1SW" }, + { 202, "straightSlopeUp0NW" }, + { 203, "straightSlopeUp1NW" }, + { 204, "straightSteepSlopeUp0NE" }, + { 205, "straightSteepSlopeUp0SE" }, + { 206, "straightSteepSlopeUp0SW" }, + { 207, "straightSteepSlopeUp0NW" }, + { 208, "rightCurveLarge0BallastNE" }, + { 209, "rightCurveLarge1BallastNE" }, + { 210, "rightCurveLarge2BallastNE" }, + { 211, "rightCurveLarge3BallastNE" }, + { 212, "rightCurveLarge4BallastNE" }, + { 213, "rightCurveLarge0BallastSE" }, + { 214, "rightCurveLarge1BallastSE" }, + { 215, "rightCurveLarge2BallastSE" }, + { 216, "rightCurveLarge3BallastSE" }, + { 217, "rightCurveLarge4BallastSE" }, + { 218, "rightCurveLarge0BallastSW" }, + { 219, "rightCurveLarge1BallastSW" }, + { 220, "rightCurveLarge2BallastSW" }, + { 221, "rightCurveLarge3BallastSW" }, + { 222, "rightCurveLarge4BallastSW" }, + { 223, "rightCurveLarge0BallastNW" }, + { 224, "rightCurveLarge1BallastNW" }, + { 225, "rightCurveLarge2BallastNW" }, + { 226, "rightCurveLarge3BallastNW" }, + { 227, "rightCurveLarge4BallastNW" }, + { 228, "leftCurveLarge0BallastNE" }, + { 229, "leftCurveLarge1BallastNE" }, + { 230, "leftCurveLarge2BallastNE" }, + { 231, "leftCurveLarge3BallastNE" }, + { 232, "leftCurveLarge4BallastNE" }, + { 233, "leftCurveLarge0BallastSE" }, + { 234, "leftCurveLarge1BallastSE" }, + { 235, "leftCurveLarge2BallastSE" }, + { 236, "leftCurveLarge3BallastSE" }, + { 237, "leftCurveLarge4BallastSE" }, + { 238, "leftCurveLarge0BallastSW" }, + { 239, "leftCurveLarge1BallastSW" }, + { 240, "leftCurveLarge2BallastSW" }, + { 241, "leftCurveLarge3BallastSW" }, + { 242, "leftCurveLarge4BallastSW" }, + { 243, "leftCurveLarge0BallastNW" }, + { 244, "leftCurveLarge1BallastNW" }, + { 245, "leftCurveLarge2BallastNW" }, + { 246, "leftCurveLarge3BallastNW" }, + { 247, "leftCurveLarge4BallastNW" }, + { 248, "rightCurveLarge0SleeperNE" }, + { 249, "rightCurveLarge1SleeperNE" }, + { 250, "rightCurveLarge2SleeperNE" }, + { 251, "rightCurveLarge3SleeperNE" }, + { 252, "rightCurveLarge4SleeperNE" }, + { 253, "rightCurveLarge0SleeperSE" }, + { 254, "rightCurveLarge1SleeperSE" }, + { 255, "rightCurveLarge2SleeperSE" }, + { 256, "rightCurveLarge3SleeperSE" }, + { 257, "rightCurveLarge4SleeperSE" }, + { 258, "rightCurveLarge0SleeperSW" }, + { 259, "rightCurveLarge1SleeperSW" }, + { 260, "rightCurveLarge2SleeperSW" }, + { 261, "rightCurveLarge3SleeperSW" }, + { 262, "rightCurveLarge4SleeperSW" }, + { 263, "rightCurveLarge0SleeperNW" }, + { 264, "rightCurveLarge1SleeperNW" }, + { 265, "rightCurveLarge2SleeperNW" }, + { 266, "rightCurveLarge3SleeperNW" }, + { 267, "rightCurveLarge4SleeperNW" }, + { 268, "leftCurveLarge0SleeperNE" }, + { 269, "leftCurveLarge1SleeperNE" }, + { 270, "leftCurveLarge2SleeperNE" }, + { 271, "leftCurveLarge3SleeperNE" }, + { 272, "leftCurveLarge4SleeperNE" }, + { 273, "leftCurveLarge0SleeperSE" }, + { 274, "leftCurveLarge1SleeperSE" }, + { 275, "leftCurveLarge2SleeperSE" }, + { 276, "leftCurveLarge3SleeperSE" }, + { 277, "leftCurveLarge4SleeperSE" }, + { 278, "leftCurveLarge0SleeperSW" }, + { 279, "leftCurveLarge1SleeperSW" }, + { 280, "leftCurveLarge2SleeperSW" }, + { 281, "leftCurveLarge3SleeperSW" }, + { 282, "leftCurveLarge4SleeperSW" }, + { 283, "leftCurveLarge0SleeperNW" }, + { 284, "leftCurveLarge1SleeperNW" }, + { 285, "leftCurveLarge2SleeperNW" }, + { 286, "leftCurveLarge3SleeperNW" }, + { 287, "leftCurveLarge4SleeperNW" }, + { 288, "rightCurveLarge0RailNE" }, + { 289, "rightCurveLarge1RailNE" }, + { 290, "rightCurveLarge2RailNE" }, + { 291, "rightCurveLarge3RailNE" }, + { 292, "rightCurveLarge4RailNE" }, + { 293, "rightCurveLarge0RailSE" }, + { 294, "rightCurveLarge1RailSE" }, + { 295, "rightCurveLarge2RailSE" }, + { 296, "rightCurveLarge3RailSE" }, + { 297, "rightCurveLarge4RailSE" }, + { 298, "rightCurveLarge0RailSW" }, + { 299, "rightCurveLarge1RailSW" }, + { 300, "rightCurveLarge2RailSW" }, + { 301, "rightCurveLarge3RailSW" }, + { 302, "rightCurveLarge4RailSW" }, + { 303, "rightCurveLarge0RailNW" }, + { 304, "rightCurveLarge1RailNW" }, + { 305, "rightCurveLarge2RailNW" }, + { 306, "rightCurveLarge3RailNW" }, + { 307, "rightCurveLarge4RailNW" }, + { 308, "leftCurveLarge0RailNE" }, + { 309, "leftCurveLarge1RailNE" }, + { 310, "leftCurveLarge2RailNE" }, + { 311, "leftCurveLarge3RailNE" }, + { 312, "leftCurveLarge4RailNE" }, + { 313, "leftCurveLarge0RailSE" }, + { 314, "leftCurveLarge1RailSE" }, + { 315, "leftCurveLarge2RailSE" }, + { 316, "leftCurveLarge3RailSE" }, + { 317, "leftCurveLarge4RailSE" }, + { 318, "leftCurveLarge0RailSW" }, + { 319, "leftCurveLarge1RailSW" }, + { 320, "leftCurveLarge2RailSW" }, + { 321, "leftCurveLarge3RailSW" }, + { 322, "leftCurveLarge4RailSW" }, + { 323, "leftCurveLarge0RailNW" }, + { 324, "leftCurveLarge1RailNW" }, + { 325, "leftCurveLarge2RailNW" }, + { 326, "leftCurveLarge3RailNW" }, + { 327, "leftCurveLarge4RailNW" }, + { 328, "diagonal0BallastNE" }, + { 329, "diagonal1BallastSW" }, + { 330, "diagonal1BallastNE" }, + { 331, "diagonal0BallastSW" }, + { 332, "diagonal0BallastSE" }, + { 333, "diagonal1BallastNW" }, + { 334, "diagonal1BallastSE" }, + { 335, "diagonal0BallastNW" }, + { 336, "diagonal0SleeperNE" }, + { 337, "diagonal1SleeperSW" }, + { 338, "diagonal1SleeperNE" }, + { 339, "diagonal0SleeperSW" }, + { 340, "diagonal0SleeperSE" }, + { 341, "diagonal1SleeperNW" }, + { 342, "diagonal1SleeperSE" }, + { 343, "diagonal0SleeperNW" }, + { 344, "diagonal0RailNE" }, + { 345, "diagonal1RailSW" }, + { 346, "diagonal1RailNE" }, + { 347, "diagonal0RailSW" }, + { 348, "diagonal0RailSE" }, + { 349, "diagonal1RailNW" }, + { 350, "diagonal1RailSE" }, + { 351, "diagonal0RailNW" }, + { 352, "sBendLeft0BallastNE" }, + { 353, "sBendLeft1BallastNE" }, + { 354, "sBendLeft1BallastSW" }, + { 355, "sBendLeft0BallastSW" }, + { 356, "sBendLeft0BallastSE" }, + { 357, "sBendLeft1BallastSE" }, + { 358, "sBendLeft1BallastNW" }, + { 359, "sBendLeft0BallastNW" }, + { 360, "sBendRight0BallastNE" }, + { 361, "sBendRight1BallastNE" }, + { 362, "sBendRight1BallastSW" }, + { 363, "sBendRight0BallastSW" }, + { 364, "sBendRight0BallastSE" }, + { 365, "sBendRight1BallastSE" }, + { 366, "sBendRight1BallastNW" }, + { 367, "sBendRight0BallastNW" }, + { 368, "sBendLeft0SleeperNE" }, + { 369, "sBendLeft1SleeperNE" }, + { 370, "sBendLeft1SleeperSW" }, + { 371, "sBendLeft0SleeperSW" }, + { 372, "sBendLeft0SleeperSE" }, + { 373, "sBendLeft1SleeperSE" }, + { 374, "sBendLeft1SleeperNW" }, + { 375, "sBendLeft0SleeperNW" }, + { 376, "sBendRight0SleeperNE" }, + { 377, "sBendRight1SleeperNE" }, + { 378, "sBendRight1SleeperSW" }, + { 379, "sBendRight0SleeperSW" }, + { 380, "sBendRight0SleeperSE" }, + { 381, "sBendRight1SleeperSE" }, + { 382, "sBendRight1SleeperNW" }, + { 383, "sBendRight0SleeperNW" }, + { 384, "sBendLeft0RailNE" }, + { 385, "sBendLeft1RailNE" }, + { 386, "sBendLeft1RailSW" }, + { 387, "sBendLeft0RailSW" }, + { 388, "sBendLeft0RailSE" }, + { 389, "sBendLeft1RailSE" }, + { 390, "sBendLeft1RailNW" }, + { 391, "sBendLeft0RailNW" }, + { 392, "sBendRight0RailNE" }, + { 393, "sBendRight1RailNE" }, + { 394, "sBendRight1RailSW" }, + { 395, "sBendRight0RailSW" }, + { 396, "sBendRight0RailSE" }, + { 397, "sBendRight1RailSE" }, + { 398, "sBendRight1RailNW" }, + { 399, "sBendRight0RailNW" }, + { 400, "rightCurveVerySmall0BallastNE" }, + { 401, "rightCurveVerySmall0BallastSE" }, + { 402, "rightCurveVerySmall0BallastSW" }, + { 403, "rightCurveVerySmall0BallastNW" }, + { 404, "rightCurveVerySmall0SleeperNE" }, + { 405, "rightCurveVerySmall0SleeperSE" }, + { 406, "rightCurveVerySmall0SleeperSW" }, + { 407, "rightCurveVerySmall0SleeperNW" }, + { 408, "rightCurveVerySmall0RailNE" }, + { 409, "rightCurveVerySmall0RailSE" }, + { 410, "rightCurveVerySmall0RailSW" }, + { 411, "rightCurveVerySmall0RailNW" }, + }; + } + + class RoadObjectNamer : ImageTableNamer + { + public static RoadObjectNamer Instance { get; } = new(); + + public override bool TryGetImageName(RoadObject model, int id, [MaybeNullWhen(false)] out string value) + { + if (id is >= 0 and <= 31) + { + value = $"uiPreviewImage{id}"; + return true; + } + + if (id is >= 32 and <= 33) + { + return ImageIdNameMap.TryGetValue(id, out value); + } + + // style dependent + return model.PaintStyle switch + { + 0 => ImageIdNameMap_Style0.TryGetValue(id, out value), + 1 => ImageIdNameMap_Style1.TryGetValue(id, out value), + 2 => ImageIdNameMap_Style2.TryGetValue(id, out value), + _ => throw new NotImplementedException(id.ToString()), + }; + } + + static readonly Dictionary ImageIdNameMap = new() + { + { 32, "uiPickupFromTrack" }, + { 33, "uiPlaceOnTrack" }, + }; + + static readonly Dictionary ImageIdNameMap_Style0 = new() + { + { 34, "kStraight0NE" }, + { 35, "kStraight0SE" }, + { 36, "kRightCurveVerySmall0NE" }, + { 37, "kRightCurveVerySmall0SE" }, + { 38, "kRightCurveVerySmall0SW" }, + { 39, "kRightCurveVerySmall0NW" }, + { 40, "kJunctionLeft0NE" }, + { 41, "kJunctionLeft0SE" }, + { 42, "kJunctionLeft0SW" }, + { 43, "kJunctionLeft0NW" }, + { 44, "kJunctionCrossroads0NE" }, + { 45, "kRightCurveSmall0NE" }, + { 46, "kRightCurveSmall1NE" }, + { 47, "kRightCurveSmall2NE" }, + { 48, "kRightCurveSmall3NE" }, + { 49, "kRightCurveSmall0SE" }, + { 50, "kRightCurveSmall1SE" }, + { 51, "kRightCurveSmall2SE" }, + { 52, "kRightCurveSmall3SE" }, + { 53, "kRightCurveSmall0SW" }, + { 54, "kRightCurveSmall1SW" }, + { 55, "kRightCurveSmall2SW" }, + { 56, "kRightCurveSmall3SW" }, + { 57, "kRightCurveSmall0NW" }, + { 58, "kRightCurveSmall1NW" }, + { 59, "kRightCurveSmall2NW" }, + { 60, "kRightCurveSmall3NW" }, + { 61, "kStraightSlopeUp0NE" }, + { 62, "kStraightSlopeUp1NE" }, + { 63, "kStraightSlopeUp0SE" }, + { 64, "kStraightSlopeUp1SE" }, + { 65, "kStraightSlopeUp0SW" }, + { 66, "kStraightSlopeUp1SW" }, + { 67, "kStraightSlopeUp0NW" }, + { 68, "kStraightSlopeUp1NW" }, + { 69, "kStraightSteepSlopeUp0NE" }, + { 70, "kStraightSteepSlopeUp0SE" }, + { 71, "kStraightSteepSlopeUp0SW" }, + { 72, "kStraightSteepSlopeUp0NW" }, + { 73, "kTurnaround0NE" }, + { 74, "kTurnaround0SE" }, + { 75, "kTurnaround0SW" }, + { 76, "kTurnaround0NW" }, + }; + + static readonly Dictionary ImageIdNameMap_Style1 = new() + { + { 34, "kStraight0BallastNE" }, + { 35, "kStraight0BallastSE" }, + { 36, "kStraight0SleeperNE" }, + { 37, "kStraight0SleeperSE" }, + { 38, "kStraight0RailNE" }, + { 39, "kStraight0RailSE" }, + { 40, "kRightCurveSmall0BallastNE" }, + { 41, "kRightCurveSmall1BallastNE" }, + { 42, "kRightCurveSmall2BallastNE" }, + { 43, "kRightCurveSmall3BallastNE" }, + { 44, "kRightCurveSmall0BallastSE" }, + { 45, "kRightCurveSmall1BallastSE" }, + { 46, "kRightCurveSmall2BallastSE" }, + { 47, "kRightCurveSmall3BallastSE" }, + { 48, "kRightCurveSmall0BallastSW" }, + { 49, "kRightCurveSmall1BallastSW" }, + { 50, "kRightCurveSmall2BallastSW" }, + { 51, "kRightCurveSmall3BallastSW" }, + { 52, "kRightCurveSmall0BallastNW" }, + { 53, "kRightCurveSmall1BallastNW" }, + { 54, "kRightCurveSmall2BallastNW" }, + { 55, "kRightCurveSmall3BallastNW" }, + { 56, "kRightCurveSmall0SleeperNE" }, + { 57, "kRightCurveSmall1SleeperNE" }, + { 58, "kRightCurveSmall2SleeperNE" }, + { 59, "kRightCurveSmall3SleeperNE" }, + { 60, "kRightCurveSmall0SleeperSE" }, + { 61, "kRightCurveSmall1SleeperSE" }, + { 62, "kRightCurveSmall2SleeperSE" }, + { 63, "kRightCurveSmall3SleeperSE" }, + { 64, "kRightCurveSmall0SleeperSW" }, + { 65, "kRightCurveSmall1SleeperSW" }, + { 66, "kRightCurveSmall2SleeperSW" }, + { 67, "kRightCurveSmall3SleeperSW" }, + { 68, "kRightCurveSmall0SleeperNW" }, + { 69, "kRightCurveSmall1SleeperNW" }, + { 70, "kRightCurveSmall2SleeperNW" }, + { 71, "kRightCurveSmall3SleeperNW" }, + { 72, "kRightCurveSmall0RailNE" }, + { 73, "kRightCurveSmall1RailNE" }, + { 74, "kRightCurveSmall2RailNE" }, + { 75, "kRightCurveSmall3RailNE" }, + { 76, "kRightCurveSmall0RailSE" }, + { 77, "kRightCurveSmall1RailSE" }, + { 78, "kRightCurveSmall2RailSE" }, + { 79, "kRightCurveSmall3RailSE" }, + { 80, "kRightCurveSmall0RailSW" }, + { 81, "kRightCurveSmall1RailSW" }, + { 82, "kRightCurveSmall2RailSW" }, + { 83, "kRightCurveSmall3RailSW" }, + { 84, "kRightCurveSmall0RailNW" }, + { 85, "kRightCurveSmall1RailNW" }, + { 86, "kRightCurveSmall2RailNW" }, + { 87, "kRightCurveSmall3RailNW" }, + { 88, "kStraightSlopeUp0BallastNE" }, + { 89, "kStraightSlopeUp1BallastNE" }, + { 90, "kStraightSlopeUp0BallastSE" }, + { 91, "kStraightSlopeUp1BallastSE" }, + { 92, "kStraightSlopeUp0BallastSW" }, + { 93, "kStraightSlopeUp1BallastSW" }, + { 94, "kStraightSlopeUp0BallastNW" }, + { 95, "kStraightSlopeUp1BallastNW" }, + { 96, "kStraightSlopeUp0SleeperNE" }, + { 97, "kStraightSlopeUp1SleeperNE" }, + { 98, "kStraightSlopeUp0SleeperSE" }, + { 99, "kStraightSlopeUp1SleeperSE" }, + { 100, "kStraightSlopeUp0SleeperSW" }, + { 101, "kStraightSlopeUp1SleeperSW" }, + { 102, "kStraightSlopeUp0SleeperNW" }, + { 103, "kStraightSlopeUp1SleeperNW" }, + { 104, "kStraightSlopeUp0RailNE" }, + { 105, "kStraightSlopeUp1RailNE" }, + { 106, "kStraightSlopeUp0RailSE" }, + { 107, "kStraightSlopeUp1RailSE" }, + { 108, "kStraightSlopeUp0RailSW" }, + { 109, "kStraightSlopeUp1RailSW" }, + { 110, "kStraightSlopeUp0RailNW" }, + { 111, "kStraightSlopeUp1RailNW" }, + { 112, "kStraightSteepSlopeUp0BallastNE" }, + { 113, "kStraightSteepSlopeUp0BallastSE" }, + { 114, "kStraightSteepSlopeUp0BallastSW" }, + { 115, "kStraightSteepSlopeUp0BallastNW" }, + { 116, "kStraightSteepSlopeUp0SleeperNE" }, + { 117, "kStraightSteepSlopeUp0SleeperSE" }, + { 118, "kStraightSteepSlopeUp0SleeperSW" }, + { 119, "kStraightSteepSlopeUp0SleeperNW" }, + { 120, "kStraightSteepSlopeUp0RailNE" }, + { 121, "kStraightSteepSlopeUp0RailSE" }, + { 122, "kStraightSteepSlopeUp0RailSW" }, + { 123, "kStraightSteepSlopeUp0RailNW" }, + { 124, "kRightCurveVerySmall0BallastNE" }, + { 125, "kRightCurveVerySmall0BallastSE" }, + { 126, "kRightCurveVerySmall0BallastSW" }, + { 127, "kRightCurveVerySmall0BallastNW" }, + { 128, "kRightCurveVerySmall0SleeperNE" }, + { 129, "kRightCurveVerySmall0SleeperSE" }, + { 130, "kRightCurveVerySmall0SleeperSW" }, + { 131, "kRightCurveVerySmall0SleeperNW" }, + { 132, "kRightCurveVerySmall0RailNE" }, + { 133, "kRightCurveVerySmall0RailSE" }, + { 134, "kRightCurveVerySmall0RailSW" }, + { 135, "kRightCurveVerySmall0RailNW" }, + { 136, "kTurnaround0BallastNE" }, + { 137, "kTurnaround0BallastSE" }, + { 138, "kTurnaround0BallastSW" }, + { 139, "kTurnaround0BallastNW" }, + { 140, "kTurnaround0SleeperNE" }, + { 141, "kTurnaround0SleeperSE" }, + { 142, "kTurnaround0SleeperSW" }, + { 143, "kTurnaround0SleeperNW" }, + { 144, "kTurnaround0RailNE" }, + { 145, "kTurnaround0RailSE" }, + { 146, "kTurnaround0RailSW" }, + { 147, "kTurnaround0RailNW" }, + }; + + static readonly Dictionary ImageIdNameMap_Style2 = new() + { + { 34, "kStraight0NE" }, + { 35, "kStraight0SE" }, + { 36, "kLeftCurveVerySmall0NW" }, + { 37, "kLeftCurveVerySmall0NE" }, + { 38, "kLeftCurveVerySmall0SE" }, + { 39, "kLeftCurveVerySmall0SW" }, + { 40, "kJunctionLeft0NE" }, + { 41, "kJunctionLeft0SE" }, + { 42, "kJunctionLeft0SW" }, + { 43, "kJunctionLeft0NW" }, + { 44, "kJunctionCrossroads0NE" }, + { 45, "kLeftCurveSmall3NW" }, + { 46, "kLeftCurveSmall1NW" }, + { 47, "kLeftCurveSmall2NW" }, + { 48, "kLeftCurveSmall0NW" }, + { 49, "kLeftCurveSmall3NE" }, + { 50, "kLeftCurveSmall1NE" }, + { 51, "kLeftCurveSmall2NE" }, + { 52, "kLeftCurveSmall0NE" }, + { 53, "kLeftCurveSmall3SE" }, + { 54, "kLeftCurveSmall1SE" }, + { 55, "kLeftCurveSmall2SE" }, + { 56, "kLeftCurveSmall0SE" }, + { 57, "kLeftCurveSmall3SW" }, + { 58, "kLeftCurveSmall1SW" }, + { 59, "kLeftCurveSmall2SW" }, + { 60, "kLeftCurveSmall0SW" }, + { 61, "kStraightSlopeUp0NE" }, + { 62, "kStraightSlopeUp1NE" }, + { 63, "kStraightSlopeUp0SE" }, + { 64, "kStraightSlopeUp1SE" }, + { 65, "kStraightSlopeUp0SW" }, + { 66, "kStraightSlopeUp1SW" }, + { 67, "kStraightSlopeUp0NW" }, + { 68, "kStraightSlopeUp1NW" }, + { 69, "kStraightSteepSlopeUp0NE" }, + { 70, "kStraightSteepSlopeUp0SE" }, + { 71, "kStraightSteepSlopeUp0SW" }, + { 72, "kStraightSteepSlopeUp0NW" }, + { 73, "kTurnaround0NE" }, + { 74, "kTurnaround0SE" }, + { 75, "kTurnaround0SW" }, + { 76, "kTurnaround0NW" }, + + { 85, "kStraight0SW" }, + { 86, "kStraight0NW" }, + { 87, "kRightCurveVerySmall0NE" }, + { 88, "kRightCurveVerySmall0SE" }, + { 89, "kRightCurveVerySmall0SW" }, + { 90, "kRightCurveVerySmall0NW" }, + { 91, "kJunctionRight0NE" }, + { 92, "kJunctionRight0SE" }, + { 93, "kJunctionRight0SW" }, + { 94, "kJunctionRight0NW" }, + // Must duplicate kJunctionCrossroads0NE + { 95, "kJunctionCrossroads0NE2" }, + { 96, "kRightCurveSmall0NE" }, + { 97, "kRightCurveSmall1NE" }, + { 98, "kRightCurveSmall2NE" }, + { 99, "kRightCurveSmall3NE" }, + { 100, "kRightCurveSmall0SE" }, + { 101, "kRightCurveSmall1SE" }, + { 102, "kRightCurveSmall2SE" }, + { 103, "kRightCurveSmall3SE" }, + { 104, "kRightCurveSmall0SW" }, + { 105, "kRightCurveSmall1SW" }, + { 106, "kRightCurveSmall2SW" }, + { 107, "kRightCurveSmall3SW" }, + { 108, "kRightCurveSmall0NW" }, + { 109, "kRightCurveSmall1NW" }, + { 110, "kRightCurveSmall2NW" }, + { 111, "kRightCurveSmall3NW" }, + { 112, "kStraightSlopeDown1SW" }, + { 113, "kStraightSlopeDown0SW" }, + { 114, "kStraightSlopeDown1NW" }, + { 115, "kStraightSlopeDown0NW" }, + { 116, "kStraightSlopeDown1NE" }, + { 117, "kStraightSlopeDown0NE" }, + { 118, "kStraightSlopeDown1SE" }, + { 119, "kStraightSlopeDown0SE" }, + { 120, "kStraightSteepSlopeDown0SW" }, + { 121, "kStraightSteepSlopeDown0NW" }, + { 122, "kStraightSteepSlopeDown0NE" }, + { 123, "kStraightSteepSlopeDown0SE" }, + }; + } + + public static void NameImages(ILocoStruct obj, ObjectType objectType, List imageList) + { + IImageTableNameProvider imageNamer = objectType switch + { + ObjectType.InterfaceSkin => InterfaceSkinObjectNamer.Instance, + ObjectType.Sound => DefaultImageTableNameProvider.Instance, + ObjectType.Currency => DefaultImageTableNameProvider.Instance, + ObjectType.Steam => DefaultImageTableNameProvider.Instance, + ObjectType.CliffEdge => CliffEdgeObjectNamer.Instance, + ObjectType.Water => WaterObjectNamer.Instance, + ObjectType.Land => LandObjectNamer.Instance, + ObjectType.TownNames => DefaultImageTableNameProvider.Instance, + ObjectType.Cargo => CargoObjectNamer.Instance, + ObjectType.Wall => WallObjectNamer.Instance, + ObjectType.TrackSignal => TrackSignalObjectNamer.Instance, + ObjectType.LevelCrossing => DefaultImageTableNameProvider.Instance, + ObjectType.StreetLight => StreetLightObjectNamer.Instance, + ObjectType.Tunnel => DefaultImageTableNameProvider.Instance, + ObjectType.Bridge => DefaultImageTableNameProvider.Instance, + ObjectType.TrackStation => TrackSignalObjectNamer.Instance, + ObjectType.TrackExtra => TrackExtraObjectNamer.Instance, + ObjectType.Track => TrackObjectNamer.Instance, + ObjectType.RoadStation => RoadStationObjectNamer.Instance, + ObjectType.RoadExtra => DefaultImageTableNameProvider.Instance, + ObjectType.Road => RoadObjectNamer.Instance, + ObjectType.Airport => DefaultImageTableNameProvider.Instance, + ObjectType.Dock => DefaultImageTableNameProvider.Instance, + ObjectType.Vehicle => DefaultImageTableNameProvider.Instance, + ObjectType.Tree => DefaultImageTableNameProvider.Instance, + ObjectType.Snow => DefaultImageTableNameProvider.Instance, + ObjectType.Climate => DefaultImageTableNameProvider.Instance, + ObjectType.HillShapes => HillShapesObjectNamer.Instance, + ObjectType.Building => DefaultImageTableNameProvider.Instance, + ObjectType.Scaffolding => DefaultImageTableNameProvider.Instance, + ObjectType.Industry => DefaultImageTableNameProvider.Instance, + ObjectType.Region => DefaultImageTableNameProvider.Instance, + ObjectType.Competitor => CompetitorObjectNamer.Instance, + ObjectType.ScenarioText => DefaultImageTableNameProvider.Instance, + _ => DefaultImageTableNameProvider.Instance, + }; + + for (var i = 0; i < imageList.Count; ++i) + { + imageList[i].Name = imageNamer.TryGetImageName(obj, i, out var name) + ? name! + : DefaultImageTableNameProvider.GetImageName(i); + } + } +} + +public static class ImageTableGrouper +{ + public static ImageTable CreateImageTable(ILocoStruct obj, ObjectType objectType, List imageList) + { + ImageTableNamer.NameImages(obj, objectType, imageList); + + var imageTable = new ImageTable(); + + switch (objectType) + { + case ObjectType.InterfaceSkin: + CreateInterfaceGroups(imageList, imageTable); + break; + case ObjectType.Sound: + imageTable.Groups.Add(("", imageList.ToList())); + break; + case ObjectType.Currency: + imageTable.Groups.Add(("", imageList.ToList())); + break; + case ObjectType.Steam: + imageTable.Groups.Add(("", imageList.ToList())); + break; + case ObjectType.CliffEdge: + CreateCliffEdgeGroups(imageList, imageTable); + break; + case ObjectType.Water: + imageTable.Groups.Add(("", imageList.ToList())); + break; + case ObjectType.Land: + imageTable.Groups.Add(("", imageList.ToList())); + break; + case ObjectType.TownNames: + imageTable.Groups.Add(("", imageList.ToList())); + break; + case ObjectType.Cargo: + CreateCargoGroups(imageList, imageTable); + break; + case ObjectType.Wall: + imageTable.Groups.Add(("", imageList.ToList())); + break; + case ObjectType.TrackSignal: + imageTable.Groups.Add(("", imageList.ToList())); + break; + case ObjectType.LevelCrossing: + imageTable.Groups.Add(("", imageList.ToList())); + break; + case ObjectType.StreetLight: + imageTable.Groups.Add(("", imageList.ToList())); + break; + case ObjectType.Tunnel: + imageTable.Groups.Add(("", imageList.ToList())); + break; + case ObjectType.Bridge: + CreateBridgeGroups(imageList, imageTable); + break; + case ObjectType.TrackStation: + imageTable.Groups.Add(("", imageList.ToList())); + break; + case ObjectType.TrackExtra: + imageTable.Groups.Add(("", imageList.ToList())); + break; + case ObjectType.Track: + imageTable.Groups.Add(("", imageList.ToList())); + break; + case ObjectType.RoadStation: + imageTable.Groups.Add(("", imageList.ToList())); + break; + case ObjectType.RoadExtra: + imageTable.Groups.Add(("", imageList.ToList())); + break; + case ObjectType.Road: + imageTable.Groups.Add(("", imageList.ToList())); + break; + case ObjectType.Airport: + CreateAirportGroups(imageList, imageTable); + break; + case ObjectType.Dock: + CreateDockGroups(imageList, imageTable); + break; + case ObjectType.Vehicle: + imageTable.Groups.Add(("", imageList.ToList())); + break; + case ObjectType.Tree: + imageTable.Groups.Add(("", imageList.ToList())); + break; + case ObjectType.Snow: + imageTable.Groups.Add(("", imageList.ToList())); + break; + case ObjectType.Climate: + imageTable.Groups.Add(("", imageList.ToList())); + break; + case ObjectType.HillShapes: + imageTable.Groups.Add(("", imageList.ToList())); + break; + case ObjectType.Building: + CreateBuildingGroups(imageList, imageTable); + break; + case ObjectType.Scaffolding: + CreateScaffoldingGroups(imageList, imageTable); + break; + case ObjectType.Industry: + CreateBuildingGroups(imageList, imageTable); + break; + case ObjectType.Region: + imageTable.Groups.Add(("", imageList.ToList())); + break; + case ObjectType.Competitor: + CreateCompetitorGroups(imageList, imageTable); + break; + case ObjectType.ScenarioText: + imageTable.Groups.Add(("", imageList.ToList())); + break; + default: + break; + } + + return imageTable; + } + + private static void CreateAirportGroups(List imageList, ImageTable imageTable) + { + imageTable.Groups.Add(("preview", imageList[0..1])); + + imageTable.Groups.AddRange(imageList + .Skip(1) + .Chunk(4) + .Select((x, i) => ($"Part {i}", x.ToList())) + .ToList()); + } + + private static void CreateBridgeGroups(List imageList, ImageTable imageTable) + { + imageTable.Groups.Add(("preview", imageList[0..1])); + imageTable.Groups.Add(("base plates", imageList[1..5])); + imageTable.Groups.Add(("unk", imageList[6..11])); + imageTable.Groups.Add(("", imageList[12..])); + } + + private static void CreateBuildingGroups(List imageList, ImageTable imageTable) + => imageTable.Groups = imageList + .Chunk(4) + .Select((x, i) => ($"Part {i}", x.ToList())) + .ToList(); + + private static void CreateCargoGroups(List imageList, ImageTable imageTable) + { + imageTable.Groups.Add(("preview", imageList[0..1])); + imageTable.Groups.Add(("station variations", imageList[1..])); + } + + private static void CreateCliffEdgeGroups(List imageList, ImageTable imageTable) + { + imageTable.Groups.Add(("left west", imageList[0..16])); + imageTable.Groups.Add(("right east", imageList[16..32])); + imageTable.Groups.Add(("right west", imageList[32..48])); + imageTable.Groups.Add(("left east", imageList[48..64])); + imageTable.Groups.Add(("far-side slopes", imageList[64..])); + } + + private static void CreateCompetitorGroups(List imageList, ImageTable imageTable) + { + imageTable.Groups.Add(("neutral", imageList[0..2])); + imageTable.Groups.Add(("happy", imageList[2..4])); + imageTable.Groups.Add(("worried", imageList[4..6])); + imageTable.Groups.Add(("thinking", imageList[6..8])); + imageTable.Groups.Add(("dejected", imageList[8..10])); + imageTable.Groups.Add(("surprised", imageList[10..12])); + imageTable.Groups.Add(("scared", imageList[12..14])); + imageTable.Groups.Add(("angry", imageList[14..16])); + imageTable.Groups.Add(("disgusted", imageList[16..18])); + } + + private static void CreateDockGroups(List imageList, ImageTable imageTable) + { + imageTable.Groups.Add(("preview", [imageList[0]])); + + imageTable.Groups.AddRange(imageList + .Skip(1) + .Chunk(4) + .Select((x, i) => ($"Part {i}", x.ToList())) + .ToList()); + } + + private static void CreateInterfaceGroups(List imageList, ImageTable imageTable) + { + imageTable.Groups.Add(("preview", imageList[0..1])); + imageTable.Groups.Add(("toolbar", imageList[1..31])); + imageTable.Groups.Add(("build-vehicle", imageList[31..43])); + imageTable.Groups.Add(("toolbar", imageList[43..49])); + imageTable.Groups.Add(("paint", imageList[49..55])); + imageTable.Groups.Add(("population", imageList[57..65])); + imageTable.Groups.Add(("performance-index", imageList[65..73])); + imageTable.Groups.Add(("cargo-units", imageList[73..81])); + imageTable.Groups.Add(("cargo-distance", imageList[81..89])); + imageTable.Groups.Add(("production", imageList[89..97])); + imageTable.Groups.Add(("wrench", imageList[97..113])); + imageTable.Groups.Add(("finances", imageList[113..129])); + imageTable.Groups.Add(("cup", imageList[129..145])); + imageTable.Groups.Add(("ratings", imageList[145..161])); + imageTable.Groups.Add(("transported", imageList[161..168])); + imageTable.Groups.Add(("cogs", imageList[168..172])); + imageTable.Groups.Add(("toolbar", imageList[172..203])); + imageTable.Groups.Add(("tab-train", imageList[203..211])); + imageTable.Groups.Add(("tab-aircraft", imageList[211..219])); + imageTable.Groups.Add(("tab-bus", imageList[219..227])); + imageTable.Groups.Add(("tab-tram", imageList[227..235])); + imageTable.Groups.Add(("tab-truck", imageList[235..243])); + imageTable.Groups.Add(("tab-ship", imageList[243..251])); + imageTable.Groups.Add(("build-train", imageList[251..267])); + imageTable.Groups.Add(("build-aircraft", imageList[267..283])); + imageTable.Groups.Add(("build-bus", imageList[283..299])); + imageTable.Groups.Add(("build-tram", imageList[299..315])); + imageTable.Groups.Add(("build-truck", imageList[315..331])); + imageTable.Groups.Add(("build-ship", imageList[331..347])); + imageTable.Groups.Add(("build-industry", imageList[347..363])); + imageTable.Groups.Add(("build-town", imageList[363..379])); + imageTable.Groups.Add(("build-buildings", imageList[379..395])); + imageTable.Groups.Add(("build-misc-buildings", imageList[395..411])); + imageTable.Groups.Add(("build-extra", imageList[411..418])); + imageTable.Groups.Add(("train", imageList[418..426])); + imageTable.Groups.Add(("aircraft", imageList[426..434])); + imageTable.Groups.Add(("bus", imageList[434..442])); + imageTable.Groups.Add(("tram", imageList[442..450])); + imageTable.Groups.Add(("truck", imageList[450..458])); + imageTable.Groups.Add(("ship", imageList[458..466])); + imageTable.Groups.Add(("toolbar-map", imageList[466..470])); + } + + private static void CreateScaffoldingGroups(List imageList, ImageTable imageTable) + { + imageTable.Groups.Add(("type 0", imageList[0..10])); + imageTable.Groups.Add(("type 1", imageList[10..24])); + imageTable.Groups.Add(("type 2", imageList[24..36])); + } +} diff --git a/Definitions/ObjectModels/Objects/Airport/AirportObject.cs b/Definitions/ObjectModels/Objects/Airport/AirportObject.cs index f44526e1..db502f83 100644 --- a/Definitions/ObjectModels/Objects/Airport/AirportObject.cs +++ b/Definitions/ObjectModels/Objects/Airport/AirportObject.cs @@ -3,7 +3,7 @@ namespace Definitions.ObjectModels.Objects.Airport; -public class AirportObject : ILocoStruct, IHasBuildingComponents, IImageTableNameProvider +public class AirportObject : ILocoStruct, IHasBuildingComponents { public int16_t BuildCostFactor { get; set; } public int16_t SellCostFactor { get; set; } @@ -44,7 +44,4 @@ public bool Validate() return BuildCostFactor > 0; } - - public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) - => throw new NotImplementedException(); } diff --git a/Definitions/ObjectModels/Objects/Bridge/BridgeObject.cs b/Definitions/ObjectModels/Objects/Bridge/BridgeObject.cs index 0a751259..7ea13849 100644 --- a/Definitions/ObjectModels/Objects/Bridge/BridgeObject.cs +++ b/Definitions/ObjectModels/Objects/Bridge/BridgeObject.cs @@ -3,7 +3,7 @@ namespace Definitions.ObjectModels.Objects.Bridge; -public class BridgeObject : ILocoStruct, IImageTableNameProvider +public class BridgeObject : ILocoStruct { public BridgeObjectFlags Flags { get; set; } public uint16_t ClearHeight { get; set; } @@ -67,7 +67,4 @@ public bool Validate() return true; } - - public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) - => throw new NotImplementedException(); } diff --git a/Definitions/ObjectModels/Objects/Building/BuildingObject.cs b/Definitions/ObjectModels/Objects/Building/BuildingObject.cs index 6b3040e7..4f99a8d0 100644 --- a/Definitions/ObjectModels/Objects/Building/BuildingObject.cs +++ b/Definitions/ObjectModels/Objects/Building/BuildingObject.cs @@ -1,10 +1,9 @@ using Definitions.ObjectModels.Objects.Common; using Definitions.ObjectModels.Types; -using System.Diagnostics.CodeAnalysis; namespace Definitions.ObjectModels.Objects.Building; -public class BuildingObject : ILocoStruct, IImageTableNameProvider, IHasBuildingComponents +public class BuildingObject : ILocoStruct, IHasBuildingComponents { public uint16_t DesignedYear { get; set; } public uint16_t ObsoleteYear { get; set; } @@ -36,12 +35,4 @@ public class BuildingObject : ILocoStruct, IImageTableNameProvider, IHasBuilding public bool Validate() => ProducedQuantity.Count == 2 && BuildingComponents.Validate(); - - public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) - { - var direction = (CardinalDirection)(id % 4); - var level = id / 4; - value = $"{direction} | Level {level}"; - return true; - } } diff --git a/Definitions/ObjectModels/Objects/Cargo/CargoObject.cs b/Definitions/ObjectModels/Objects/Cargo/CargoObject.cs index 1554d70d..159a995e 100644 --- a/Definitions/ObjectModels/Objects/Cargo/CargoObject.cs +++ b/Definitions/ObjectModels/Objects/Cargo/CargoObject.cs @@ -2,7 +2,7 @@ namespace Definitions.ObjectModels.Objects.Cargo; -public class CargoObject : ILocoStruct, IImageTableNameProvider +public class CargoObject : ILocoStruct { public uint16_t CargoTransferTime { get; set; } public CargoCategory CargoCategory { get; set; } @@ -21,13 +21,4 @@ public class CargoObject : ILocoStruct, IImageTableNameProvider public bool Validate() => var_02 <= 3840 && CargoTransferTime != 0; - - public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) - { - value = id == 0 - ? "kInlineSprite" - : $"kStationPlatform{id}"; - - return true; - } } diff --git a/Definitions/ObjectModels/Objects/CliffEdge/CliffEdgeObject.cs b/Definitions/ObjectModels/Objects/CliffEdge/CliffEdgeObject.cs index 0529d414..f233d8ea 100644 --- a/Definitions/ObjectModels/Objects/CliffEdge/CliffEdgeObject.cs +++ b/Definitions/ObjectModels/Objects/CliffEdge/CliffEdgeObject.cs @@ -1,38 +1,6 @@ -using System.Diagnostics.CodeAnalysis; - namespace Definitions.ObjectModels.Objects.CliffEdge; -public class CliffEdgeObject : ILocoStruct, IImageTableNameProvider +public class CliffEdgeObject : ILocoStruct { public bool Validate() => true; - - public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) - { - if (id is >= 0 and <= 63) - { - var direction = id / 16 % 2 == 0 ? "west" : "east"; - var side = id is >= 16 and <= 47 ? "right" : "left"; - var level = id % 16; - value = $"south {direction} {side} {level}"; - return true; - } - - if (id is >= 64 and <= 69) - { - return ImageIdNameMap.TryGetValue(id, out value); - } - - value = null; - return false; - } - - public static readonly Dictionary ImageIdNameMap = new() - { - { 64, "north west slope 1" }, - { 65, "north west slope 2" }, - { 66, "north west slope 3" }, - { 67, "north east slope 1" }, - { 68, "north east slope 2" }, - { 69, "north east slope 3" }, - }; } diff --git a/Definitions/ObjectModels/Objects/Climate/ClimateObject.cs b/Definitions/ObjectModels/Objects/Climate/ClimateObject.cs index 16a52763..f1ede564 100644 --- a/Definitions/ObjectModels/Objects/Climate/ClimateObject.cs +++ b/Definitions/ObjectModels/Objects/Climate/ClimateObject.cs @@ -1,8 +1,6 @@ -using System.Diagnostics.CodeAnalysis; - namespace Definitions.ObjectModels.Objects.Climate; -public class ClimateObject : ILocoStruct, IImageTableNameProvider +public class ClimateObject : ILocoStruct { public uint8_t FirstSeason { get; set; } public uint8_t SeasonLength1 { get; set; } @@ -15,6 +13,4 @@ public class ClimateObject : ILocoStruct, IImageTableNameProvider public bool Validate() => WinterSnowLine <= SummerSnowLine && FirstSeason < 4; - public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) - => throw new NotImplementedException(); } diff --git a/Definitions/ObjectModels/Objects/Competitor/CompetitorObject.cs b/Definitions/ObjectModels/Objects/Competitor/CompetitorObject.cs index e0b700d9..d61d3f8f 100644 --- a/Definitions/ObjectModels/Objects/Competitor/CompetitorObject.cs +++ b/Definitions/ObjectModels/Objects/Competitor/CompetitorObject.cs @@ -2,7 +2,7 @@ namespace Definitions.ObjectModels.Objects.Competitor; -public class CompetitorObject : ILocoStruct, IImageTableNameProvider +public class CompetitorObject : ILocoStruct { public NamePrefixFlags AvailableNamePrefixes { get; set; } // bitset public PlaystyleFlags AvailablePlaystyles { get; set; } // bitset @@ -31,29 +31,4 @@ public bool Validate() return Competitiveness is >= 1 and <= 9; } - - public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) - => ImageIdNameMap.TryGetValue(id, out value); - - public static Dictionary ImageIdNameMap = new() - { - { 0, "smallNeutral" }, - { 1, "largeNeutral" }, - { 2, "smallHappy" }, - { 3, "largeHappy" }, - { 4, "smallWorried" }, - { 5, "largeWorried" }, - { 6, "smallThinking" }, - { 7, "largeThinking" }, - { 8, "smallDejected" }, - { 9, "largeDejected" }, - { 10, "smallSurprised" }, - { 11, "largeSurprised" }, - { 12, "smallScared" }, - { 13, "largeScared" }, - { 14, "smallAngry" }, - { 15, "largeAngry" }, - { 16, "smallDisgusted" }, - { 17, "largeDisgusted" }, - }; } diff --git a/Definitions/ObjectModels/Objects/Currency/CurrencyObject.cs b/Definitions/ObjectModels/Objects/Currency/CurrencyObject.cs index c986d0f3..6b303d13 100644 --- a/Definitions/ObjectModels/Objects/Currency/CurrencyObject.cs +++ b/Definitions/ObjectModels/Objects/Currency/CurrencyObject.cs @@ -1,8 +1,6 @@ -using System.Diagnostics.CodeAnalysis; - namespace Definitions.ObjectModels.Objects.Currency; -public class CurrencyObject : ILocoStruct, IImageTableNameProvider +public class CurrencyObject : ILocoStruct { public uint8_t Separator { get; set; } public uint8_t Factor { get; set; } @@ -21,7 +19,4 @@ public bool Validate() return true; } - - public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) - => throw new NotImplementedException(); } diff --git a/Definitions/ObjectModels/Objects/Dock/DockObject.cs b/Definitions/ObjectModels/Objects/Dock/DockObject.cs index 0435d79d..2fd5d9fc 100644 --- a/Definitions/ObjectModels/Objects/Dock/DockObject.cs +++ b/Definitions/ObjectModels/Objects/Dock/DockObject.cs @@ -1,10 +1,9 @@ using Definitions.ObjectModels.Objects.Common; using Definitions.ObjectModels.Types; -using System.Diagnostics.CodeAnalysis; namespace Definitions.ObjectModels.Objects.Dock; -public class DockObject : ILocoStruct, IHasBuildingComponents, IImageTableNameProvider +public class DockObject : ILocoStruct, IHasBuildingComponents { public int16_t BuildCostFactor { get; set; } public int16_t SellCostFactor { get; set; } @@ -36,7 +35,4 @@ public bool Validate() return BuildCostFactor > 0; } - - public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) - => throw new NotImplementedException(); } diff --git a/Definitions/ObjectModels/Objects/HillShapes/HillShapesObject.cs b/Definitions/ObjectModels/Objects/HillShapes/HillShapesObject.cs index 3150305c..3dc6a7d0 100644 --- a/Definitions/ObjectModels/Objects/HillShapes/HillShapesObject.cs +++ b/Definitions/ObjectModels/Objects/HillShapes/HillShapesObject.cs @@ -1,25 +1,10 @@ -using System.Diagnostics.CodeAnalysis; - namespace Definitions.ObjectModels.Objects.HillShape; -public class HillShapesObject : ILocoStruct, IImageTableNameProvider +public class HillShapesObject : ILocoStruct { public uint8_t HillHeightMapCount { get; set; } public uint8_t MountainHeightMapCount { get; set; } public bool IsHeightMap { get; set; } public bool Validate() => true; - - public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) - => ImageIdNameMap.TryGetValue(id, out value); - - public static Dictionary ImageIdNameMap = new() - { - { 0, "hill shape 1" }, - { 1, "hill shape 2" }, - { 2, "mountain shape 1" }, - { 3, "mountain shape 2" }, - { 4, "preview image" }, - - }; } diff --git a/Definitions/ObjectModels/Objects/Industry/IndustryObject.cs b/Definitions/ObjectModels/Objects/Industry/IndustryObject.cs index 0b255b6b..54fcc88f 100644 --- a/Definitions/ObjectModels/Objects/Industry/IndustryObject.cs +++ b/Definitions/ObjectModels/Objects/Industry/IndustryObject.cs @@ -4,7 +4,7 @@ namespace Definitions.ObjectModels.Objects.Industry; -public class IndustryObject : ILocoStruct, IHasBuildingComponents, IImageTableNameProvider +public class IndustryObject : ILocoStruct, IHasBuildingComponents { public uint32_t FarmImagesPerGrowthStage { get; set; } public uint8_t MinNumBuildings { get; set; } @@ -96,7 +96,4 @@ public bool Validate() return InitialProductionRate[1].Min <= 100; } - - public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) - => throw new NotImplementedException(); } diff --git a/Definitions/ObjectModels/Objects/InterfaceSkin/InterfaceSkinObject.cs b/Definitions/ObjectModels/Objects/InterfaceSkin/InterfaceSkinObject.cs index 56b235dd..b2387d2d 100644 --- a/Definitions/ObjectModels/Objects/InterfaceSkin/InterfaceSkinObject.cs +++ b/Definitions/ObjectModels/Objects/InterfaceSkin/InterfaceSkinObject.cs @@ -3,7 +3,7 @@ namespace Definitions.ObjectModels.Objects.InterfaceSkin; -public class InterfaceSkinObject : ILocoStruct, IImageTableNameProvider +public class InterfaceSkinObject : ILocoStruct { public Colour MapTooltipObjectColour { get; set; } public Colour MapTooltipCargoColour { get; set; } @@ -25,481 +25,4 @@ public class InterfaceSkinObject : ILocoStruct, IImageTableNameProvider public Colour TimeToolbarColour { get; set; } public bool Validate() => true; - - public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) - => ImageIdNameMap.TryGetValue(id, out value); - - public static Dictionary ImageIdNameMap = new() - { - { 0, "preview_image" }, - { 1, "toolbar_pause" }, - { 2, "toolbar_pause_hover" }, - { 3, "toolbar_loadsave" }, - { 4, "toolbar_loadsave_hover" }, - { 5, "toolbar_zoom" }, - { 6, "toolbar_zoom_hover" }, - { 7, "toolbar_rotate" }, - { 8, "toolbar_rotate_hover" }, - { 9, "toolbar_terraform" }, - { 10, "toolbar_terraform_hover" }, - { 11, "toolbar_audio_active" }, - { 12, "toolbar_audio_active_hover" }, - { 13, "toolbar_audio_inactive" }, - { 14, "toolbar_audio_inactive_hover" }, - { 15, "toolbar_view" }, - { 16, "toolbar_view_hover" }, - { 17, "toolbar_towns" }, - { 18, "toolbar_towns_hover" }, - { 19, "toolbar_empty_opaque" }, - { 20, "toolbar_empty_opaque_hover" }, - { 21, "toolbar_empty_transparent" }, - { 22, "toolbar_empty_transparent_hover" }, - { 23, "toolbar_industries" }, - { 24, "toolbar_industries_hover" }, - { 25, "toolbar_airports" }, - { 26, "toolbar_airports_hover" }, - { 27, "toolbar_ports" }, - { 28, "toolbar_ports_hover" }, - { 29, "toolbar_cogwheels" }, - { 30, "toolbar_cogwheels_hover" }, - { 31, "toolbar_build_vehicle_train" }, - { 32, "toolbar_build_vehicle_train_hover" }, - { 33, "toolbar_build_vehicle_bus" }, - { 34, "toolbar_build_vehicle_bus_hover" }, - { 35, "toolbar_build_vehicle_truck" }, - { 36, "toolbar_build_vehicle_truck_hover" }, - { 37, "toolbar_build_vehicle_tram" }, - { 38, "toolbar_build_vehicle_tram_hover" }, - { 39, "toolbar_build_vehicle_airplane" }, - { 40, "toolbar_build_vehicle_airplane_hover" }, - { 41, "toolbar_build_vehicle_boat" }, - { 42, "toolbar_build_vehicle_boat_hover" }, - { 43, "toolbar_stations" }, - { 44, "toolbar_stations_hover" }, - { 45, "tab_awards" }, - { 46, "toolbar_menu_airport" }, - { 47, "toolbar_menu_ship_port" }, - { 48, "tab_cargo_ratings" }, - { 49, "tab_colour_scheme_frame0" }, - { 50, "tab_colour_scheme_frame1" }, - { 51, "tab_colour_scheme_frame2" }, - { 52, "tab_colour_scheme_frame3" }, - { 53, "tab_colour_scheme_frame4" }, - { 54, "tab_colour_scheme_frame5" }, - { 55, "tab_colour_scheme_frame6" }, - { 56, "tab_colour_scheme_frame7" }, - { 57, "tab_population_frame0" }, - { 58, "tab_population_frame1" }, - { 59, "tab_population_frame2" }, - { 60, "tab_population_frame3" }, - { 61, "tab_population_frame4" }, - { 62, "tab_population_frame5" }, - { 63, "tab_population_frame6" }, - { 64, "tab_population_frame7" }, - { 65, "tab_performance_index_frame0" }, - { 66, "tab_performance_index_frame1" }, - { 67, "tab_performance_index_frame2" }, - { 68, "tab_performance_index_frame3" }, - { 69, "tab_performance_index_frame4" }, - { 70, "tab_performance_index_frame5" }, - { 71, "tab_performance_index_frame6" }, - { 72, "tab_performance_index_frame7" }, - { 73, "tab_cargo_units_frame0" }, - { 74, "tab_cargo_units_frame1" }, - { 75, "tab_cargo_units_frame2" }, - { 76, "tab_cargo_units_frame3" }, - { 77, "tab_cargo_units_frame4" }, - { 78, "tab_cargo_units_frame5" }, - { 79, "tab_cargo_units_frame6" }, - { 80, "tab_cargo_units_frame7" }, - { 81, "tab_cargo_distance_frame0" }, - { 82, "tab_cargo_distance_frame1" }, - { 83, "tab_cargo_distance_frame2" }, - { 84, "tab_cargo_distance_frame3" }, - { 85, "tab_cargo_distance_frame4" }, - { 86, "tab_cargo_distance_frame5" }, - { 87, "tab_cargo_distance_frame6" }, - { 88, "tab_cargo_distance_frame7" }, - { 89, "tab_production_frame0" }, - { 90, "tab_production_frame1" }, - { 91, "tab_production_frame2" }, - { 92, "tab_production_frame3" }, - { 93, "tab_production_frame4" }, - { 94, "tab_production_frame5" }, - { 95, "tab_production_frame6" }, - { 96, "tab_production_frame7" }, - { 97, "tab_wrench_frame0" }, - { 98, "tab_wrench_frame1" }, - { 99, "tab_wrench_frame2" }, - { 100, "tab_wrench_frame3" }, - { 101, "tab_wrench_frame4" }, - { 102, "tab_wrench_frame5" }, - { 103, "tab_wrench_frame6" }, - { 104, "tab_wrench_frame7" }, - { 105, "tab_wrench_frame8" }, - { 106, "tab_wrench_frame9" }, - { 107, "tab_wrench_frame10" }, - { 108, "tab_wrench_frame11" }, - { 109, "tab_wrench_frame12" }, - { 110, "tab_wrench_frame13" }, - { 111, "tab_wrench_frame14" }, - { 112, "tab_wrench_frame15" }, - { 113, "tab_finances_frame0" }, - { 114, "tab_finances_frame1" }, - { 115, "tab_finances_frame2" }, - { 116, "tab_finances_frame3" }, - { 117, "tab_finances_frame4" }, - { 118, "tab_finances_frame5" }, - { 119, "tab_finances_frame6" }, - { 120, "tab_finances_frame7" }, - { 121, "tab_finances_frame8" }, - { 122, "tab_finances_frame9" }, - { 123, "tab_finances_frame10" }, - { 124, "tab_finances_frame11" }, - { 125, "tab_finances_frame12" }, - { 126, "tab_finances_frame13" }, - { 127, "tab_finances_frame14" }, - { 128, "tab_finances_frame15" }, - { 129, "tab_cup_frame0" }, - { 130, "tab_cup_frame1" }, - { 131, "tab_cup_frame2" }, - { 132, "tab_cup_frame3" }, - { 133, "tab_cup_frame4" }, - { 134, "tab_cup_frame5" }, - { 135, "tab_cup_frame6" }, - { 136, "tab_cup_frame7" }, - { 137, "tab_cup_frame8" }, - { 138, "tab_cup_frame9" }, - { 139, "tab_cup_frame10" }, - { 140, "tab_cup_frame11" }, - { 141, "tab_cup_frame12" }, - { 142, "tab_cup_frame13" }, - { 143, "tab_cup_frame14" }, - { 144, "tab_cup_frame15" }, - { 145, "tab_ratings_frame0" }, - { 146, "tab_ratings_frame1" }, - { 147, "tab_ratings_frame2" }, - { 148, "tab_ratings_frame3" }, - { 149, "tab_ratings_frame4" }, - { 150, "tab_ratings_frame5" }, - { 151, "tab_ratings_frame6" }, - { 152, "tab_ratings_frame7" }, - { 153, "tab_ratings_frame8" }, - { 154, "tab_ratings_frame9" }, - { 155, "tab_ratings_frame10" }, - { 156, "tab_ratings_frame11" }, - { 157, "tab_ratings_frame12" }, - { 158, "tab_ratings_frame13" }, - { 159, "tab_ratings_frame14" }, - { 160, "tab_ratings_frame15" }, - { 161, "tab_transported_frame0" }, - { 162, "tab_transported_frame1" }, - { 163, "tab_transported_frame2" }, - { 164, "tab_transported_frame3" }, - { 165, "tab_transported_frame4" }, - { 166, "tab_transported_frame5" }, - { 167, "tab_transported_frame6" }, - { 168, "tab_cogs_frame0" }, - { 169, "tab_cogs_frame1" }, - { 170, "tab_cogs_frame2" }, - { 171, "tab_cogs_frame3" }, - { 172, "tab_scenario_details" }, - { 173, "tab_company" }, - { 174, "tab_companies" }, - { 175, "toolbar_menu_zoom_in" }, - { 176, "toolbar_menu_zoom_out" }, - { 177, "toolbar_menu_rotate_clockwise" }, - { 178, "toolbar_menu_rotate_anti_clockwise" }, - { 179, "toolbar_menu_plant_trees" }, - { 180, "toolbar_menu_bulldozer" }, - { 181, "tab_company_details" }, - { 182, "all_stations" }, - { 183, "rail_stations" }, - { 184, "road_stations" }, - { 185, "airports" }, - { 186, "ship_ports" }, - { 187, "toolbar_menu_build_walls" }, - { 188, "phone" }, - { 189, "toolbar_menu_towns" }, - { 190, "toolbar_menu_stations" }, - { 191, "toolbar_menu_industries" }, - { 192, "tab_routes_frame_0" }, - { 193, "tab_routes_frame_1" }, - { 194, "tab_routes_frame_2" }, - { 195, "tab_routes_frame_3" }, - { 196, "tab_messages" }, - { 197, "tab_message_settings" }, - { 198, "tab_cargo_delivered_frame0" }, - { 199, "tab_cargo_delivered_frame1" }, - { 200, "tab_cargo_delivered_frame2" }, - { 201, "tab_cargo_delivered_frame3" }, - { 202, "tab_cargo_payment_rates" }, - { 203, "tab_vehicle_train_frame0" }, - { 204, "tab_vehicle_train_frame1" }, - { 205, "tab_vehicle_train_frame2" }, - { 206, "tab_vehicle_train_frame3" }, - { 207, "tab_vehicle_train_frame4" }, - { 208, "tab_vehicle_train_frame5" }, - { 209, "tab_vehicle_train_frame6" }, - { 210, "tab_vehicle_train_frame7" }, - { 211, "tab_vehicle_aircraft_frame0" }, - { 212, "tab_vehicle_aircraft_frame1" }, - { 213, "tab_vehicle_aircraft_frame2" }, - { 214, "tab_vehicle_aircraft_frame3" }, - { 215, "tab_vehicle_aircraft_frame4" }, - { 216, "tab_vehicle_aircraft_frame5" }, - { 217, "tab_vehicle_aircraft_frame6" }, - { 218, "tab_vehicle_aircraft_frame7" }, - { 219, "tab_vehicle_bus_frame0" }, - { 220, "tab_vehicle_bus_frame1" }, - { 221, "tab_vehicle_bus_frame2" }, - { 222, "tab_vehicle_bus_frame3" }, - { 223, "tab_vehicle_bus_frame4" }, - { 224, "tab_vehicle_bus_frame5" }, - { 225, "tab_vehicle_bus_frame6" }, - { 226, "tab_vehicle_bus_frame7" }, - { 227, "tab_vehicle_tram_frame0" }, - { 228, "tab_vehicle_tram_frame1" }, - { 229, "tab_vehicle_tram_frame2" }, - { 230, "tab_vehicle_tram_frame3" }, - { 231, "tab_vehicle_tram_frame4" }, - { 232, "tab_vehicle_tram_frame5" }, - { 233, "tab_vehicle_tram_frame6" }, - { 234, "tab_vehicle_tram_frame7" }, - { 235, "tab_vehicle_truck_frame0" }, - { 236, "tab_vehicle_truck_frame1" }, - { 237, "tab_vehicle_truck_frame2" }, - { 238, "tab_vehicle_truck_frame3" }, - { 239, "tab_vehicle_truck_frame4" }, - { 240, "tab_vehicle_truck_frame5" }, - { 241, "tab_vehicle_truck_frame6" }, - { 242, "tab_vehicle_truck_frame7" }, - { 243, "tab_vehicle_ship_frame0" }, - { 244, "tab_vehicle_ship_frame1" }, - { 245, "tab_vehicle_ship_frame2" }, - { 246, "tab_vehicle_ship_frame3" }, - { 247, "tab_vehicle_ship_frame4" }, - { 248, "tab_vehicle_ship_frame5" }, - { 249, "tab_vehicle_ship_frame6" }, - { 250, "tab_vehicle_ship_frame7" }, - { 251, "build_vehicle_train_frame_0" }, - { 252, "build_vehicle_train_frame_1" }, - { 253, "build_vehicle_train_frame_2" }, - { 254, "build_vehicle_train_frame_3" }, - { 255, "build_vehicle_train_frame_4" }, - { 256, "build_vehicle_train_frame_5" }, - { 257, "build_vehicle_train_frame_6" }, - { 258, "build_vehicle_train_frame_7" }, - { 259, "build_vehicle_train_frame_8" }, - { 260, "build_vehicle_train_frame_9" }, - { 261, "build_vehicle_train_frame_10" }, - { 262, "build_vehicle_train_frame_11" }, - { 263, "build_vehicle_train_frame_12" }, - { 264, "build_vehicle_train_frame_13" }, - { 265, "build_vehicle_train_frame_14" }, - { 266, "build_vehicle_train_frame_15" }, - { 267, "build_vehicle_aircraft_frame_0" }, - { 268, "build_vehicle_aircraft_frame_1" }, - { 269, "build_vehicle_aircraft_frame_2" }, - { 270, "build_vehicle_aircraft_frame_3" }, - { 271, "build_vehicle_aircraft_frame_4" }, - { 272, "build_vehicle_aircraft_frame_5" }, - { 273, "build_vehicle_aircraft_frame_6" }, - { 274, "build_vehicle_aircraft_frame_7" }, - { 275, "build_vehicle_aircraft_frame_8" }, - { 276, "build_vehicle_aircraft_frame_9" }, - { 277, "build_vehicle_aircraft_frame_10" }, - { 278, "build_vehicle_aircraft_frame_11" }, - { 279, "build_vehicle_aircraft_frame_12" }, - { 280, "build_vehicle_aircraft_frame_13" }, - { 281, "build_vehicle_aircraft_frame_14" }, - { 282, "build_vehicle_aircraft_frame_15" }, - { 283, "build_vehicle_bus_frame_0" }, - { 284, "build_vehicle_bus_frame_1" }, - { 285, "build_vehicle_bus_frame_2" }, - { 286, "build_vehicle_bus_frame_3" }, - { 287, "build_vehicle_bus_frame_4" }, - { 288, "build_vehicle_bus_frame_5" }, - { 289, "build_vehicle_bus_frame_6" }, - { 290, "build_vehicle_bus_frame_7" }, - { 291, "build_vehicle_bus_frame_8" }, - { 292, "build_vehicle_bus_frame_9" }, - { 293, "build_vehicle_bus_frame_10" }, - { 294, "build_vehicle_bus_frame_11" }, - { 295, "build_vehicle_bus_frame_12" }, - { 296, "build_vehicle_bus_frame_13" }, - { 297, "build_vehicle_bus_frame_14" }, - { 298, "build_vehicle_bus_frame_15" }, - { 299, "build_vehicle_tram_frame_0" }, - { 300, "build_vehicle_tram_frame_1" }, - { 301, "build_vehicle_tram_frame_2" }, - { 302, "build_vehicle_tram_frame_3" }, - { 303, "build_vehicle_tram_frame_4" }, - { 304, "build_vehicle_tram_frame_5" }, - { 305, "build_vehicle_tram_frame_6" }, - { 306, "build_vehicle_tram_frame_7" }, - { 307, "build_vehicle_tram_frame_8" }, - { 308, "build_vehicle_tram_frame_9" }, - { 309, "build_vehicle_tram_frame_10" }, - { 310, "build_vehicle_tram_frame_11" }, - { 311, "build_vehicle_tram_frame_12" }, - { 312, "build_vehicle_tram_frame_13" }, - { 313, "build_vehicle_tram_frame_14" }, - { 314, "build_vehicle_tram_frame_15" }, - { 315, "build_vehicle_truck_frame_0" }, - { 316, "build_vehicle_truck_frame_1" }, - { 317, "build_vehicle_truck_frame_2" }, - { 318, "build_vehicle_truck_frame_3" }, - { 319, "build_vehicle_truck_frame_4" }, - { 320, "build_vehicle_truck_frame_5" }, - { 321, "build_vehicle_truck_frame_6" }, - { 322, "build_vehicle_truck_frame_7" }, - { 323, "build_vehicle_truck_frame_8" }, - { 324, "build_vehicle_truck_frame_9" }, - { 325, "build_vehicle_truck_frame_10" }, - { 326, "build_vehicle_truck_frame_11" }, - { 327, "build_vehicle_truck_frame_12" }, - { 328, "build_vehicle_truck_frame_13" }, - { 329, "build_vehicle_truck_frame_14" }, - { 330, "build_vehicle_truck_frame_15" }, - { 331, "build_vehicle_ship_frame_0" }, - { 332, "build_vehicle_ship_frame_1" }, - { 333, "build_vehicle_ship_frame_2" }, - { 334, "build_vehicle_ship_frame_3" }, - { 335, "build_vehicle_ship_frame_4" }, - { 336, "build_vehicle_ship_frame_5" }, - { 337, "build_vehicle_ship_frame_6" }, - { 338, "build_vehicle_ship_frame_7" }, - { 339, "build_vehicle_ship_frame_8" }, - { 340, "build_vehicle_ship_frame_9" }, - { 341, "build_vehicle_ship_frame_10" }, - { 342, "build_vehicle_ship_frame_11" }, - { 343, "build_vehicle_ship_frame_12" }, - { 344, "build_vehicle_ship_frame_13" }, - { 345, "build_vehicle_ship_frame_14" }, - { 346, "build_vehicle_ship_frame_15" }, - { 347, "build_industry_frame_0" }, - { 348, "build_industry_frame_1" }, - { 349, "build_industry_frame_2" }, - { 350, "build_industry_frame_3" }, - { 351, "build_industry_frame_4" }, - { 352, "build_industry_frame_5" }, - { 353, "build_industry_frame_6" }, - { 354, "build_industry_frame_7" }, - { 355, "build_industry_frame_8" }, - { 356, "build_industry_frame_9" }, - { 357, "build_industry_frame_10" }, - { 358, "build_industry_frame_11" }, - { 359, "build_industry_frame_12" }, - { 360, "build_industry_frame_13" }, - { 361, "build_industry_frame_14" }, - { 362, "build_industry_frame_15" }, - { 363, "build_town_frame_0" }, - { 364, "build_town_frame_1" }, - { 365, "build_town_frame_2" }, - { 366, "build_town_frame_3" }, - { 367, "build_town_frame_4" }, - { 368, "build_town_frame_5" }, - { 369, "build_town_frame_6" }, - { 370, "build_town_frame_7" }, - { 371, "build_town_frame_8" }, - { 372, "build_town_frame_9" }, - { 373, "build_town_frame_10" }, - { 374, "build_town_frame_11" }, - { 375, "build_town_frame_12" }, - { 376, "build_town_frame_13" }, - { 377, "build_town_frame_14" }, - { 378, "build_town_frame_15" }, - { 379, "build_buildings_frame_0" }, - { 380, "build_buildings_frame_1" }, - { 381, "build_buildings_frame_2" }, - { 382, "build_buildings_frame_3" }, - { 383, "build_buildings_frame_4" }, - { 384, "build_buildings_frame_5" }, - { 385, "build_buildings_frame_6" }, - { 386, "build_buildings_frame_7" }, - { 387, "build_buildings_frame_8" }, - { 388, "build_buildings_frame_9" }, - { 389, "build_buildings_frame_10" }, - { 390, "build_buildings_frame_11" }, - { 391, "build_buildings_frame_12" }, - { 392, "build_buildings_frame_13" }, - { 393, "build_buildings_frame_14" }, - { 394, "build_buildings_frame_15" }, - { 395, "build_misc_buildings_frame_0" }, - { 396, "build_misc_buildings_frame_1" }, - { 397, "build_misc_buildings_frame_2" }, - { 398, "build_misc_buildings_frame_3" }, - { 399, "build_misc_buildings_frame_4" }, - { 400, "build_misc_buildings_frame_5" }, - { 401, "build_misc_buildings_frame_6" }, - { 402, "build_misc_buildings_frame_7" }, - { 403, "build_misc_buildings_frame_8" }, - { 404, "build_misc_buildings_frame_9" }, - { 405, "build_misc_buildings_frame_10" }, - { 406, "build_misc_buildings_frame_11" }, - { 407, "build_misc_buildings_frame_12" }, - { 408, "build_misc_buildings_frame_13" }, - { 409, "build_misc_buildings_frame_14" }, - { 410, "build_misc_buildings_frame_15" }, - { 411, "build_additional_train" }, - { 412, "build_additional_bus" }, - { 413, "build_additional_truck" }, - { 414, "build_additional_tram" }, - { 415, "build_additional_aircraft" }, - { 416, "build_additional_ship" }, - { 417, "build_headquarters" }, - { 418, "vehicle_train_frame_0" }, - { 419, "vehicle_train_frame_1" }, - { 420, "vehicle_train_frame_2" }, - { 421, "vehicle_train_frame_3" }, - { 422, "vehicle_train_frame_4" }, - { 423, "vehicle_train_frame_5" }, - { 424, "vehicle_train_frame_6" }, - { 425, "vehicle_train_frame_7" }, - { 426, "vehicle_aircraft_frame_0" }, - { 427, "vehicle_aircraft_frame_1" }, - { 428, "vehicle_aircraft_frame_2" }, - { 429, "vehicle_aircraft_frame_3" }, - { 430, "vehicle_aircraft_frame_4" }, - { 431, "vehicle_aircraft_frame_5" }, - { 432, "vehicle_aircraft_frame_6" }, - { 433, "vehicle_aircraft_frame_7" }, - { 434, "vehicle_buses_frame_0" }, - { 435, "vehicle_buses_frame_1" }, - { 436, "vehicle_buses_frame_2" }, - { 437, "vehicle_buses_frame_3" }, - { 438, "vehicle_buses_frame_4" }, - { 439, "vehicle_buses_frame_5" }, - { 440, "vehicle_buses_frame_6" }, - { 441, "vehicle_buses_frame_7" }, - { 442, "vehicle_trams_frame_0" }, - { 443, "vehicle_trams_frame_1" }, - { 444, "vehicle_trams_frame_2" }, - { 445, "vehicle_trams_frame_3" }, - { 446, "vehicle_trams_frame_4" }, - { 447, "vehicle_trams_frame_5" }, - { 448, "vehicle_trams_frame_6" }, - { 449, "vehicle_trams_frame_7" }, - { 450, "vehicle_trucks_frame_0" }, - { 451, "vehicle_trucks_frame_1" }, - { 452, "vehicle_trucks_frame_2" }, - { 453, "vehicle_trucks_frame_3" }, - { 454, "vehicle_trucks_frame_4" }, - { 455, "vehicle_trucks_frame_5" }, - { 456, "vehicle_trucks_frame_6" }, - { 457, "vehicle_trucks_frame_7" }, - { 458, "vehicle_ships_frame_0" }, - { 459, "vehicle_ships_frame_1" }, - { 460, "vehicle_ships_frame_2" }, - { 461, "vehicle_ships_frame_3" }, - { 462, "vehicle_ships_frame_4" }, - { 463, "vehicle_ships_frame_5" }, - { 464, "vehicle_ships_frame_6" }, - { 465, "vehicle_ships_frame_7" }, - { 466, "toolbar_menu_map_north" }, - { 467, "toolbar_menu_map_west" }, - { 468, "toolbar_menu_map_south" }, - { 469, "toolbar_menu_map_east" }, - }; } diff --git a/Definitions/ObjectModels/Objects/Land/LandObject.cs b/Definitions/ObjectModels/Objects/Land/LandObject.cs index 5594f45a..e82efe01 100644 --- a/Definitions/ObjectModels/Objects/Land/LandObject.cs +++ b/Definitions/ObjectModels/Objects/Land/LandObject.cs @@ -1,9 +1,8 @@ using Definitions.ObjectModels.Types; -using System.Diagnostics.CodeAnalysis; namespace Definitions.ObjectModels.Objects.Land; -public class LandObject : ILocoStruct, IImageTableNameProvider +public class LandObject : ILocoStruct { public uint8_t CostIndex { get; set; } public uint8_t NumGrowthStages { get; set; } @@ -42,30 +41,4 @@ public bool Validate() return NumImageAngles is 1 or 2 or 4; } - - public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) - => ImageIdNameMap.TryGetValue(id, out value); - - public static readonly Dictionary ImageIdNameMap = new() - { - { 0, "flat" }, - { 1, "west corner up" }, - { 2, "south corner up" }, - { 3, "north east slope" }, - { 4, "east corner up" }, - { 5, "west and east corner up" }, - { 6, "north west slope" }, - { 7, "north corner down" }, - { 8, "north corner up" }, - { 9, "south east slope" }, - { 10, "north and south corners up" }, - { 11, "east corner down" }, - { 12, "north west slope" }, - { 13, "south corner down" }, - { 14, "west corner down" }, - { 15, "south slope" }, - { 16, "north slope" }, - { 17, "east slope" }, - { 18, "west slope" } - }; } diff --git a/Definitions/ObjectModels/Objects/LevelCrossing/LevelCrossingObject.cs b/Definitions/ObjectModels/Objects/LevelCrossing/LevelCrossingObject.cs index 7b84e757..66cf30d8 100644 --- a/Definitions/ObjectModels/Objects/LevelCrossing/LevelCrossingObject.cs +++ b/Definitions/ObjectModels/Objects/LevelCrossing/LevelCrossingObject.cs @@ -1,8 +1,6 @@ -using System.Diagnostics.CodeAnalysis; - namespace Definitions.ObjectModels.Objects.LevelCrossing; -public class LevelCrossingObject : ILocoStruct, IImageTableNameProvider +public class LevelCrossingObject : ILocoStruct { public int16_t CostFactor { get; set; } public int16_t SellCostFactor { get; set; } @@ -31,7 +29,4 @@ public bool Validate() _ => false, }; } - - public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) - => throw new NotImplementedException(); } diff --git a/Definitions/ObjectModels/Objects/Road/RoadObject.cs b/Definitions/ObjectModels/Objects/Road/RoadObject.cs index 82dd3243..37d0a113 100644 --- a/Definitions/ObjectModels/Objects/Road/RoadObject.cs +++ b/Definitions/ObjectModels/Objects/Road/RoadObject.cs @@ -1,9 +1,8 @@ using Definitions.ObjectModels.Types; -using System.Diagnostics.CodeAnalysis; namespace Definitions.ObjectModels.Objects.Road; -public class RoadObject : ILocoStruct, IImageTableNameProvider +public class RoadObject : ILocoStruct { public RoadTraitFlags RoadPieces { get; set; } public int16_t BuildCostFactor { get; set; } @@ -62,286 +61,4 @@ public bool Validate() return true; } - - public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) - { - if (id is >= 0 and <= 31) - { - value = $"uiPreviewImage{id}"; - return true; - } - - if (id is >= 32 and <= 33) - { - return ImageIdNameMap.TryGetValue(id, out value); - } - - // style dependent - return PaintStyle switch - { - 0 => ImageIdNameMap_Style0.TryGetValue(id, out value), - 1 => ImageIdNameMap_Style1.TryGetValue(id, out value), - 2 => ImageIdNameMap_Style2.TryGetValue(id, out value), - _ => throw new NotImplementedException(id.ToString()), - }; - } - - public static Dictionary ImageIdNameMap = new() - { - { 32, "uiPickupFromTrack" }, - { 33, "uiPlaceOnTrack" }, - }; - - public static Dictionary ImageIdNameMap_Style0 = new() - { - { 34, "kStraight0NE" }, - { 35, "kStraight0SE" }, - { 36, "kRightCurveVerySmall0NE" }, - { 37, "kRightCurveVerySmall0SE" }, - { 38, "kRightCurveVerySmall0SW" }, - { 39, "kRightCurveVerySmall0NW" }, - { 40, "kJunctionLeft0NE" }, - { 41, "kJunctionLeft0SE" }, - { 42, "kJunctionLeft0SW" }, - { 43, "kJunctionLeft0NW" }, - { 44, "kJunctionCrossroads0NE" }, - { 45, "kRightCurveSmall0NE" }, - { 46, "kRightCurveSmall1NE" }, - { 47, "kRightCurveSmall2NE" }, - { 48, "kRightCurveSmall3NE" }, - { 49, "kRightCurveSmall0SE" }, - { 50, "kRightCurveSmall1SE" }, - { 51, "kRightCurveSmall2SE" }, - { 52, "kRightCurveSmall3SE" }, - { 53, "kRightCurveSmall0SW" }, - { 54, "kRightCurveSmall1SW" }, - { 55, "kRightCurveSmall2SW" }, - { 56, "kRightCurveSmall3SW" }, - { 57, "kRightCurveSmall0NW" }, - { 58, "kRightCurveSmall1NW" }, - { 59, "kRightCurveSmall2NW" }, - { 60, "kRightCurveSmall3NW" }, - { 61, "kStraightSlopeUp0NE" }, - { 62, "kStraightSlopeUp1NE" }, - { 63, "kStraightSlopeUp0SE" }, - { 64, "kStraightSlopeUp1SE" }, - { 65, "kStraightSlopeUp0SW" }, - { 66, "kStraightSlopeUp1SW" }, - { 67, "kStraightSlopeUp0NW" }, - { 68, "kStraightSlopeUp1NW" }, - { 69, "kStraightSteepSlopeUp0NE" }, - { 70, "kStraightSteepSlopeUp0SE" }, - { 71, "kStraightSteepSlopeUp0SW" }, - { 72, "kStraightSteepSlopeUp0NW" }, - { 73, "kTurnaround0NE" }, - { 74, "kTurnaround0SE" }, - { 75, "kTurnaround0SW" }, - { 76, "kTurnaround0NW" }, - }; - - public static Dictionary ImageIdNameMap_Style1 = new() - { - { 34, "kStraight0BallastNE" }, - { 35, "kStraight0BallastSE" }, - { 36, "kStraight0SleeperNE" }, - { 37, "kStraight0SleeperSE" }, - { 38, "kStraight0RailNE" }, - { 39, "kStraight0RailSE" }, - { 40, "kRightCurveSmall0BallastNE" }, - { 41, "kRightCurveSmall1BallastNE" }, - { 42, "kRightCurveSmall2BallastNE" }, - { 43, "kRightCurveSmall3BallastNE" }, - { 44, "kRightCurveSmall0BallastSE" }, - { 45, "kRightCurveSmall1BallastSE" }, - { 46, "kRightCurveSmall2BallastSE" }, - { 47, "kRightCurveSmall3BallastSE" }, - { 48, "kRightCurveSmall0BallastSW" }, - { 49, "kRightCurveSmall1BallastSW" }, - { 50, "kRightCurveSmall2BallastSW" }, - { 51, "kRightCurveSmall3BallastSW" }, - { 52, "kRightCurveSmall0BallastNW" }, - { 53, "kRightCurveSmall1BallastNW" }, - { 54, "kRightCurveSmall2BallastNW" }, - { 55, "kRightCurveSmall3BallastNW" }, - { 56, "kRightCurveSmall0SleeperNE" }, - { 57, "kRightCurveSmall1SleeperNE" }, - { 58, "kRightCurveSmall2SleeperNE" }, - { 59, "kRightCurveSmall3SleeperNE" }, - { 60, "kRightCurveSmall0SleeperSE" }, - { 61, "kRightCurveSmall1SleeperSE" }, - { 62, "kRightCurveSmall2SleeperSE" }, - { 63, "kRightCurveSmall3SleeperSE" }, - { 64, "kRightCurveSmall0SleeperSW" }, - { 65, "kRightCurveSmall1SleeperSW" }, - { 66, "kRightCurveSmall2SleeperSW" }, - { 67, "kRightCurveSmall3SleeperSW" }, - { 68, "kRightCurveSmall0SleeperNW" }, - { 69, "kRightCurveSmall1SleeperNW" }, - { 70, "kRightCurveSmall2SleeperNW" }, - { 71, "kRightCurveSmall3SleeperNW" }, - { 72, "kRightCurveSmall0RailNE" }, - { 73, "kRightCurveSmall1RailNE" }, - { 74, "kRightCurveSmall2RailNE" }, - { 75, "kRightCurveSmall3RailNE" }, - { 76, "kRightCurveSmall0RailSE" }, - { 77, "kRightCurveSmall1RailSE" }, - { 78, "kRightCurveSmall2RailSE" }, - { 79, "kRightCurveSmall3RailSE" }, - { 80, "kRightCurveSmall0RailSW" }, - { 81, "kRightCurveSmall1RailSW" }, - { 82, "kRightCurveSmall2RailSW" }, - { 83, "kRightCurveSmall3RailSW" }, - { 84, "kRightCurveSmall0RailNW" }, - { 85, "kRightCurveSmall1RailNW" }, - { 86, "kRightCurveSmall2RailNW" }, - { 87, "kRightCurveSmall3RailNW" }, - { 88, "kStraightSlopeUp0BallastNE" }, - { 89, "kStraightSlopeUp1BallastNE" }, - { 90, "kStraightSlopeUp0BallastSE" }, - { 91, "kStraightSlopeUp1BallastSE" }, - { 92, "kStraightSlopeUp0BallastSW" }, - { 93, "kStraightSlopeUp1BallastSW" }, - { 94, "kStraightSlopeUp0BallastNW" }, - { 95, "kStraightSlopeUp1BallastNW" }, - { 96, "kStraightSlopeUp0SleeperNE" }, - { 97, "kStraightSlopeUp1SleeperNE" }, - { 98, "kStraightSlopeUp0SleeperSE" }, - { 99, "kStraightSlopeUp1SleeperSE" }, - { 100, "kStraightSlopeUp0SleeperSW" }, - { 101, "kStraightSlopeUp1SleeperSW" }, - { 102, "kStraightSlopeUp0SleeperNW" }, - { 103, "kStraightSlopeUp1SleeperNW" }, - { 104, "kStraightSlopeUp0RailNE" }, - { 105, "kStraightSlopeUp1RailNE" }, - { 106, "kStraightSlopeUp0RailSE" }, - { 107, "kStraightSlopeUp1RailSE" }, - { 108, "kStraightSlopeUp0RailSW" }, - { 109, "kStraightSlopeUp1RailSW" }, - { 110, "kStraightSlopeUp0RailNW" }, - { 111, "kStraightSlopeUp1RailNW" }, - { 112, "kStraightSteepSlopeUp0BallastNE" }, - { 113, "kStraightSteepSlopeUp0BallastSE" }, - { 114, "kStraightSteepSlopeUp0BallastSW" }, - { 115, "kStraightSteepSlopeUp0BallastNW" }, - { 116, "kStraightSteepSlopeUp0SleeperNE" }, - { 117, "kStraightSteepSlopeUp0SleeperSE" }, - { 118, "kStraightSteepSlopeUp0SleeperSW" }, - { 119, "kStraightSteepSlopeUp0SleeperNW" }, - { 120, "kStraightSteepSlopeUp0RailNE" }, - { 121, "kStraightSteepSlopeUp0RailSE" }, - { 122, "kStraightSteepSlopeUp0RailSW" }, - { 123, "kStraightSteepSlopeUp0RailNW" }, - { 124, "kRightCurveVerySmall0BallastNE" }, - { 125, "kRightCurveVerySmall0BallastSE" }, - { 126, "kRightCurveVerySmall0BallastSW" }, - { 127, "kRightCurveVerySmall0BallastNW" }, - { 128, "kRightCurveVerySmall0SleeperNE" }, - { 129, "kRightCurveVerySmall0SleeperSE" }, - { 130, "kRightCurveVerySmall0SleeperSW" }, - { 131, "kRightCurveVerySmall0SleeperNW" }, - { 132, "kRightCurveVerySmall0RailNE" }, - { 133, "kRightCurveVerySmall0RailSE" }, - { 134, "kRightCurveVerySmall0RailSW" }, - { 135, "kRightCurveVerySmall0RailNW" }, - { 136, "kTurnaround0BallastNE" }, - { 137, "kTurnaround0BallastSE" }, - { 138, "kTurnaround0BallastSW" }, - { 139, "kTurnaround0BallastNW" }, - { 140, "kTurnaround0SleeperNE" }, - { 141, "kTurnaround0SleeperSE" }, - { 142, "kTurnaround0SleeperSW" }, - { 143, "kTurnaround0SleeperNW" }, - { 144, "kTurnaround0RailNE" }, - { 145, "kTurnaround0RailSE" }, - { 146, "kTurnaround0RailSW" }, - { 147, "kTurnaround0RailNW" }, - }; - - public static Dictionary ImageIdNameMap_Style2 = new() - { - { 34, "kStraight0NE" }, - { 35, "kStraight0SE" }, - { 36, "kLeftCurveVerySmall0NW" }, - { 37, "kLeftCurveVerySmall0NE" }, - { 38, "kLeftCurveVerySmall0SE" }, - { 39, "kLeftCurveVerySmall0SW" }, - { 40, "kJunctionLeft0NE" }, - { 41, "kJunctionLeft0SE" }, - { 42, "kJunctionLeft0SW" }, - { 43, "kJunctionLeft0NW" }, - { 44, "kJunctionCrossroads0NE" }, - { 45, "kLeftCurveSmall3NW" }, - { 46, "kLeftCurveSmall1NW" }, - { 47, "kLeftCurveSmall2NW" }, - { 48, "kLeftCurveSmall0NW" }, - { 49, "kLeftCurveSmall3NE" }, - { 50, "kLeftCurveSmall1NE" }, - { 51, "kLeftCurveSmall2NE" }, - { 52, "kLeftCurveSmall0NE" }, - { 53, "kLeftCurveSmall3SE" }, - { 54, "kLeftCurveSmall1SE" }, - { 55, "kLeftCurveSmall2SE" }, - { 56, "kLeftCurveSmall0SE" }, - { 57, "kLeftCurveSmall3SW" }, - { 58, "kLeftCurveSmall1SW" }, - { 59, "kLeftCurveSmall2SW" }, - { 60, "kLeftCurveSmall0SW" }, - { 61, "kStraightSlopeUp0NE" }, - { 62, "kStraightSlopeUp1NE" }, - { 63, "kStraightSlopeUp0SE" }, - { 64, "kStraightSlopeUp1SE" }, - { 65, "kStraightSlopeUp0SW" }, - { 66, "kStraightSlopeUp1SW" }, - { 67, "kStraightSlopeUp0NW" }, - { 68, "kStraightSlopeUp1NW" }, - { 69, "kStraightSteepSlopeUp0NE" }, - { 70, "kStraightSteepSlopeUp0SE" }, - { 71, "kStraightSteepSlopeUp0SW" }, - { 72, "kStraightSteepSlopeUp0NW" }, - { 73, "kTurnaround0NE" }, - { 74, "kTurnaround0SE" }, - { 75, "kTurnaround0SW" }, - { 76, "kTurnaround0NW" }, - - { 85, "kStraight0SW" }, - { 86, "kStraight0NW" }, - { 87, "kRightCurveVerySmall0NE" }, - { 88, "kRightCurveVerySmall0SE" }, - { 89, "kRightCurveVerySmall0SW" }, - { 90, "kRightCurveVerySmall0NW" }, - { 91, "kJunctionRight0NE" }, - { 92, "kJunctionRight0SE" }, - { 93, "kJunctionRight0SW" }, - { 94, "kJunctionRight0NW" }, - // Must duplicate kJunctionCrossroads0NE - { 95, "kJunctionCrossroads0NE2" }, - { 96, "kRightCurveSmall0NE" }, - { 97, "kRightCurveSmall1NE" }, - { 98, "kRightCurveSmall2NE" }, - { 99, "kRightCurveSmall3NE" }, - { 100, "kRightCurveSmall0SE" }, - { 101, "kRightCurveSmall1SE" }, - { 102, "kRightCurveSmall2SE" }, - { 103, "kRightCurveSmall3SE" }, - { 104, "kRightCurveSmall0SW" }, - { 105, "kRightCurveSmall1SW" }, - { 106, "kRightCurveSmall2SW" }, - { 107, "kRightCurveSmall3SW" }, - { 108, "kRightCurveSmall0NW" }, - { 109, "kRightCurveSmall1NW" }, - { 110, "kRightCurveSmall2NW" }, - { 111, "kRightCurveSmall3NW" }, - { 112, "kStraightSlopeDown1SW" }, - { 113, "kStraightSlopeDown0SW" }, - { 114, "kStraightSlopeDown1NW" }, - { 115, "kStraightSlopeDown0NW" }, - { 116, "kStraightSlopeDown1NE" }, - { 117, "kStraightSlopeDown0NE" }, - { 118, "kStraightSlopeDown1SE" }, - { 119, "kStraightSlopeDown0SE" }, - { 120, "kStraightSteepSlopeDown0SW" }, - { 121, "kStraightSteepSlopeDown0NW" }, - { 122, "kStraightSteepSlopeDown0NE" }, - { 123, "kStraightSteepSlopeDown0SE" }, - }; } diff --git a/Definitions/ObjectModels/Objects/RoadExtra/RoadExtraObject.cs b/Definitions/ObjectModels/Objects/RoadExtra/RoadExtraObject.cs index d5bf0877..e0537de3 100644 --- a/Definitions/ObjectModels/Objects/RoadExtra/RoadExtraObject.cs +++ b/Definitions/ObjectModels/Objects/RoadExtra/RoadExtraObject.cs @@ -1,9 +1,8 @@ using Definitions.ObjectModels.Objects.Road; -using System.Diagnostics.CodeAnalysis; namespace Definitions.ObjectModels.Objects.RoadExtra; -public class RoadExtraObject : ILocoStruct, IImageTableNameProvider +public class RoadExtraObject : ILocoStruct { public RoadTraitFlags RoadPieces { get; set; } = RoadTraitFlags.None; public uint8_t PaintStyle { get; set; } @@ -31,7 +30,4 @@ public bool Validate() return BuildCostFactor > 0; } - - public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) - => throw new NotImplementedException(); } diff --git a/Definitions/ObjectModels/Objects/RoadStation/RoadStationObject.cs b/Definitions/ObjectModels/Objects/RoadStation/RoadStationObject.cs index 0f54430d..5a7422de 100644 --- a/Definitions/ObjectModels/Objects/RoadStation/RoadStationObject.cs +++ b/Definitions/ObjectModels/Objects/RoadStation/RoadStationObject.cs @@ -1,11 +1,10 @@ using Definitions.ObjectModels.Objects.Road; using Definitions.ObjectModels.Objects.Shared; using Definitions.ObjectModels.Types; -using System.Diagnostics.CodeAnalysis; namespace Definitions.ObjectModels.Objects.RoadStation; -public class RoadStationObject : ILocoStruct, IImageTableNameProvider +public class RoadStationObject : ILocoStruct { public uint8_t PaintStyle { get; set; } public uint8_t Height { get; set; } @@ -58,29 +57,4 @@ public bool Validate() return true; } - - public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) - => ImageIdNameMap.TryGetValue(id, out value); - - public static Dictionary ImageIdNameMap = new() - { - { 0, "preview_image" }, - { 1, "preview_image_glass_overlay" }, - { 2, "North West Back Wall" }, - { 3, "North West Front Platform" }, - { 4, "North West Front Wall/Roof" }, - { 5, "North West Glass Overlay" }, - { 6, "South West Back Wall" }, - { 7, "South West Front Platform" }, - { 8, "South West Front Wall/Roof" }, - { 9, "South West Glass Overlay" }, - { 10, "South East Back Wall" }, - { 11, "South East Front Platform" }, - { 12, "South East Front Wall/Roof" }, - { 13, "South East Glass Overlay" }, - { 14, "North East Back Wall" }, - { 15, "North East Front Platform" }, - { 16, "North East Front Wall/Roof" }, - { 17, "North East Glass Overlay" }, - }; } diff --git a/Definitions/ObjectModels/Objects/Scaffolding/ScaffoldingObject.cs b/Definitions/ObjectModels/Objects/Scaffolding/ScaffoldingObject.cs index 6cf28762..0e21d344 100644 --- a/Definitions/ObjectModels/Objects/Scaffolding/ScaffoldingObject.cs +++ b/Definitions/ObjectModels/Objects/Scaffolding/ScaffoldingObject.cs @@ -1,53 +1,10 @@ using System.Diagnostics.CodeAnalysis; namespace Definitions.ObjectModels.Objects.Scaffolding; -public class ScaffoldingObject : ILocoStruct, IImageTableNameProvider +public class ScaffoldingObject : ILocoStruct { public List SegmentHeights { get; set; } = []; public List RoofHeights { get; set; } = []; public bool Validate() => true; - - public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) - => ImageIdNameMap.TryGetValue(id, out value); - - public static Dictionary ImageIdNameMap = new() - { - { 0, "type0 1x1 SegmentBack" }, - { 1, "type0 1x1 SegmentFront" }, - { 2, "type0 1x1 RoofNE" }, - { 3, "type0 1x1 RoofSE" }, - { 4, "type0 1x1 RoofSW" }, - { 5, "type0 1x1 RoofNW" }, - { 6, "type0 2x2 SegmentBack" }, - { 7, "type0 2x2 SegmentFront" }, - { 8, "type0 2x2 RoofNE" }, - { 9, "type0 2x2 RoofSE" }, - { 10, "type0 2x2 RoofSW" }, - { 11, "type0 2x2 RoofNW" }, - { 12, "type1 1x1 SegmentBack" }, - { 13, "type1 1x1 SegmentFront" }, - { 14, "type1 1x1 RoofNE" }, - { 15, "type1 1x1 RoofSE" }, - { 16, "type1 1x1 RoofSW" }, - { 17, "type1 1x1 RoofNW" }, - { 18, "type1 2x2 SegmentBack" }, - { 19, "type1 2x2 SegmentFront" }, - { 20, "type1 2x2 RoofNE" }, - { 21, "type1 2x2 RoofSE" }, - { 22, "type1 2x2 RoofSW" }, - { 23, "type1 2x2 RoofNW" }, - { 24, "type2 1x1 SegmentBack" }, - { 25, "type2 1x1 SegmentFront" }, - { 26, "type2 1x1 RoofNE" }, - { 27, "type2 1x1 RoofSE" }, - { 28, "type2 1x1 RoofSW" }, - { 29, "type2 1x1 RoofNW" }, - { 30, "type2 2x2 SegmentBack" }, - { 31, "type2 2x2 SegmentFront" }, - { 32, "type2 2x2 RoofNE" }, - { 33, "type2 2x2 RoofSE" }, - { 34, "type2 2x2 RoofSW" }, - { 35, "type2 2x2 RoofNW" }, - }; } diff --git a/Definitions/ObjectModels/Objects/Snow/SnowObject.cs b/Definitions/ObjectModels/Objects/Snow/SnowObject.cs index c80429ad..fd6ca46a 100644 --- a/Definitions/ObjectModels/Objects/Snow/SnowObject.cs +++ b/Definitions/ObjectModels/Objects/Snow/SnowObject.cs @@ -1,22 +1,8 @@ using System.Diagnostics.CodeAnalysis; namespace Definitions.ObjectModels.Objects.Snow; -public class SnowObject : ILocoStruct, IImageTableNameProvider + +public class SnowObject : ILocoStruct { public bool Validate() => true; - - public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) - => ImageIdNameMap.TryGetValue(id, out value); - - public static Dictionary ImageIdNameMap = new() - { - { 0, "surfaceEighthZoom" }, - { 10, "outlineEighthZoom" }, - { 19, "surfaceQuarterZoom" }, - { 38, "outlineQuarterZoom" }, - { 57, "surfaceHalfZoom" }, - { 76, "outlineHalfZoom" }, - { 95, "surfaceFullZoom" }, - { 114, "outlineFullZoom" }, - }; } diff --git a/Definitions/ObjectModels/Objects/Steam/SteamObject.cs b/Definitions/ObjectModels/Objects/Steam/SteamObject.cs index ab0693fc..a1ae6023 100644 --- a/Definitions/ObjectModels/Objects/Steam/SteamObject.cs +++ b/Definitions/ObjectModels/Objects/Steam/SteamObject.cs @@ -1,9 +1,8 @@ using Definitions.ObjectModels.Types; -using System.Diagnostics.CodeAnalysis; namespace Definitions.ObjectModels.Objects.Steam; -public class SteamObject : ILocoStruct, IImageTableNameProvider +public class SteamObject : ILocoStruct { public uint8_t NumStationaryTicks { get; set; } public uint8_t SpriteWidth { get; set; } @@ -18,7 +17,4 @@ public class SteamObject : ILocoStruct, IImageTableNameProvider public bool Validate() => true; - - public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) - => throw new NotImplementedException(); } diff --git a/Definitions/ObjectModels/Objects/StreetLight/StreetLightObject.cs b/Definitions/ObjectModels/Objects/StreetLight/StreetLightObject.cs index 90247e58..39254f15 100644 --- a/Definitions/ObjectModels/Objects/StreetLight/StreetLightObject.cs +++ b/Definitions/ObjectModels/Objects/StreetLight/StreetLightObject.cs @@ -1,29 +1,8 @@ -using System.Diagnostics.CodeAnalysis; - namespace Definitions.ObjectModels.Objects.Streetlight; -public class StreetLightObject : ILocoStruct, IImageTableNameProvider +public class StreetLightObject : ILocoStruct { public List DesignedYears { get; set; } = []; public bool Validate() => true; - - public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) - => ImageIdNameMap.TryGetValue(id, out value); - - public static Dictionary ImageIdNameMap = new() - { - { 0, "style0NE" }, - { 1, "style0SE" }, - { 2, "style0SW" }, - { 3, "style0NW" }, - { 4, "style1NE" }, - { 5, "style1SE" }, - { 6, "style1SW" }, - { 7, "style1NW" }, - { 8, "style2NE" }, - { 9, "style2SE" }, - { 10, "style2SW" }, - { 11, "style2NW" }, - }; } diff --git a/Definitions/ObjectModels/Objects/Track/TrackObject.cs b/Definitions/ObjectModels/Objects/Track/TrackObject.cs index da751fcb..225b6a60 100644 --- a/Definitions/ObjectModels/Objects/Track/TrackObject.cs +++ b/Definitions/ObjectModels/Objects/Track/TrackObject.cs @@ -1,9 +1,8 @@ using Definitions.ObjectModels.Types; -using System.Diagnostics.CodeAnalysis; namespace Definitions.ObjectModels.Objects.Track; -public class TrackObject : ILocoStruct, IImageTableNameProvider +public class TrackObject : ILocoStruct { public TrackTraitFlags TrackPieces { get; set; } public TrackTraitFlags StationTrackPieces { get; set; } @@ -64,425 +63,4 @@ public bool Validate() return Stations.Count <= 7; } - - public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) - => ImageIdNameMap.TryGetValue(id, out value); - - // taken from OpenLoco TrackObject.h - public static Dictionary ImageIdNameMap = new() - { - { 0, "uiPreviewImage0" }, - { 1, "uiPreviewImage1" }, - { 2, "uiPreviewImage2" }, - { 3, "uiPreviewImage3" }, - { 4, "uiPreviewImage4" }, - { 5, "uiPreviewImage5" }, - { 6, "uiPreviewImage6" }, - { 7, "uiPreviewImage7" }, - { 8, "uiPreviewImage8" }, - { 9, "uiPreviewImage9" }, - { 10, "uiPreviewImage10" }, - { 11, "uiPreviewImage11" }, - { 12, "uiPreviewImage12" }, - { 13, "uiPreviewImage13" }, - { 14, "uiPreviewImage14" }, - { 15, "uiPreviewImage15" }, - { 16, "uiPickupFromTrack" }, - { 17, "uiPlaceOnTrack" }, - // - { 18, "straight0BallastNE" }, - { 19, "straight0BallastSE" }, - { 20, "straight0SleeperNE" }, - { 21, "straight0SleeperSE" }, - { 22, "straight0RailNE" }, - { 23, "straight0RailSE" }, - { 24, "rightCurveSmall0BallastNE" }, - { 25, "rightCurveSmall1BallastNE" }, - { 26, "rightCurveSmall2BallastNE" }, - { 27, "rightCurveSmall3BallastNE" }, - { 28, "rightCurveSmall0BallastSE" }, - { 29, "rightCurveSmall1BallastSE" }, - { 30, "rightCurveSmall2BallastSE" }, - { 31, "rightCurveSmall3BallastSE" }, - { 32, "rightCurveSmall0BallastSW" }, - { 33, "rightCurveSmall1BallastSW" }, - { 34, "rightCurveSmall2BallastSW" }, - { 35, "rightCurveSmall3BallastSW" }, - { 36, "rightCurveSmall0BallastNW" }, - { 37, "rightCurveSmall1BallastNW" }, - { 38, "rightCurveSmall2BallastNW" }, - { 39, "rightCurveSmall3BallastNW" }, - { 40, "rightCurveSmall0SleeperNE" }, - { 41, "rightCurveSmall1SleeperNE" }, - { 42, "rightCurveSmall2SleeperNE" }, - { 43, "rightCurveSmall3SleeperNE" }, - { 44, "rightCurveSmall0SleeperSE" }, - { 45, "rightCurveSmall1SleeperSE" }, - { 46, "rightCurveSmall2SleeperSE" }, - { 47, "rightCurveSmall3SleeperSE" }, - { 48, "rightCurveSmall0SleeperSW" }, - { 49, "rightCurveSmall1SleeperSW" }, - { 50, "rightCurveSmall2SleeperSW" }, - { 51, "rightCurveSmall3SleeperSW" }, - { 52, "rightCurveSmall0SleeperNW" }, - { 53, "rightCurveSmall1SleeperNW" }, - { 54, "rightCurveSmall2SleeperNW" }, - { 55, "rightCurveSmall3SleeperNW" }, - { 56, "rightCurveSmall0RailNE" }, - { 57, "rightCurveSmall1RailNE" }, - { 58, "rightCurveSmall2RailNE" }, - { 59, "rightCurveSmall3RailNE" }, - { 60, "rightCurveSmall0RailSE" }, - { 61, "rightCurveSmall1RailSE" }, - { 62, "rightCurveSmall2RailSE" }, - { 63, "rightCurveSmall3RailSE" }, - { 64, "rightCurveSmall0RailSW" }, - { 65, "rightCurveSmall1RailSW" }, - { 66, "rightCurveSmall2RailSW" }, - { 67, "rightCurveSmall3RailSW" }, - { 68, "rightCurveSmall0RailNW" }, - { 69, "rightCurveSmall1RailNW" }, - { 70, "rightCurveSmall2RailNW" }, - { 71, "rightCurveSmall3RailNW" }, - { 72, "rightCurveSmallSlopeUp0NE" }, - { 73, "rightCurveSmallSlopeUp1NE" }, - { 74, "rightCurveSmallSlopeUp2NE" }, - { 75, "rightCurveSmallSlopeUp3NE" }, - { 76, "rightCurveSmallSlopeUp0SE" }, - { 77, "rightCurveSmallSlopeUp1SE" }, - { 78, "rightCurveSmallSlopeUp2SE" }, - { 79, "rightCurveSmallSlopeUp3SE" }, - { 80, "rightCurveSmallSlopeUp0SW" }, - { 81, "rightCurveSmallSlopeUp1SW" }, - { 82, "rightCurveSmallSlopeUp2SW" }, - { 83, "rightCurveSmallSlopeUp3SW" }, - { 84, "rightCurveSmallSlopeUp0NW" }, - { 85, "rightCurveSmallSlopeUp1NW" }, - { 86, "rightCurveSmallSlopeUp2NW" }, - { 87, "rightCurveSmallSlopeUp3NW" }, - { 88, "rightCurveSmallSlopeDown0NE" }, - { 89, "rightCurveSmallSlopeDown1NE" }, - { 90, "rightCurveSmallSlopeDown2NE" }, - { 91, "rightCurveSmallSlopeDown3NE" }, - { 92, "rightCurveSmallSlopeDown0SE" }, - { 93, "rightCurveSmallSlopeDown1SE" }, - { 94, "rightCurveSmallSlopeDown2SE" }, - { 95, "rightCurveSmallSlopeDown3SE" }, - { 96, "rightCurveSmallSlopeDown0SW" }, - { 97, "rightCurveSmallSlopeDown1SW" }, - { 98, "rightCurveSmallSlopeDown2SW" }, - { 99, "rightCurveSmallSlopeDown3SW" }, - { 100, "rightCurveSmallSlopeDown0NW" }, - { 101, "rightCurveSmallSlopeDown1NW" }, - { 102, "rightCurveSmallSlopeDown2NW" }, - { 103, "rightCurveSmallSlopeDown3NW" }, - { 104, "rightCurveSmallSteepSlopeUp0NE" }, - { 105, "rightCurveSmallSteepSlopeUp1NE" }, - { 106, "rightCurveSmallSteepSlopeUp2NE" }, - { 107, "rightCurveSmallSteepSlopeUp3NE" }, - { 108, "rightCurveSmallSteepSlopeUp0SE" }, - { 109, "rightCurveSmallSteepSlopeUp1SE" }, - { 110, "rightCurveSmallSteepSlopeUp2SE" }, - { 111, "rightCurveSmallSteepSlopeUp3SE" }, - { 112, "rightCurveSmallSteepSlopeUp0SW" }, - { 113, "rightCurveSmallSteepSlopeUp1SW" }, - { 114, "rightCurveSmallSteepSlopeUp2SW" }, - { 115, "rightCurveSmallSteepSlopeUp3SW" }, - { 116, "rightCurveSmallSteepSlopeUp0NW" }, - { 117, "rightCurveSmallSteepSlopeUp1NW" }, - { 118, "rightCurveSmallSteepSlopeUp2NW" }, - { 119, "rightCurveSmallSteepSlopeUp3NW" }, - { 120, "rightCurveSmallSteepSlopeDown0NE" }, - { 121, "rightCurveSmallSteepSlopeDown1NE" }, - { 122, "rightCurveSmallSteepSlopeDown2NE" }, - { 123, "rightCurveSmallSteepSlopeDown3NE" }, - { 124, "rightCurveSmallSteepSlopeDown0SE" }, - { 125, "rightCurveSmallSteepSlopeDown1SE" }, - { 126, "rightCurveSmallSteepSlopeDown2SE" }, - { 127, "rightCurveSmallSteepSlopeDown3SE" }, - { 128, "rightCurveSmallSteepSlopeDown0SW" }, - { 129, "rightCurveSmallSteepSlopeDown1SW" }, - { 130, "rightCurveSmallSteepSlopeDown2SW" }, - { 131, "rightCurveSmallSteepSlopeDown3SW" }, - { 132, "rightCurveSmallSteepSlopeDown0NW" }, - { 133, "rightCurveSmallSteepSlopeDown1NW" }, - { 134, "rightCurveSmallSteepSlopeDown2NW" }, - { 135, "rightCurveSmallSteepSlopeDown3NW" }, - { 136, "rightCurve0BallastNE" }, - { 137, "rightCurve1BallastNE" }, - { 138, "rightCurve2BallastNE" }, - { 139, "rightCurve3BallastNE" }, - { 140, "rightCurve4BallastNE" }, - { 141, "rightCurve0BallastSE" }, - { 142, "rightCurve1BallastSE" }, - { 143, "rightCurve2BallastSE" }, - { 144, "rightCurve3BallastSE" }, - { 145, "rightCurve4BallastSE" }, - { 146, "rightCurve0BallastSW" }, - { 147, "rightCurve1BallastSW" }, - { 148, "rightCurve2BallastSW" }, - { 149, "rightCurve3BallastSW" }, - { 150, "rightCurve4BallastSW" }, - { 151, "rightCurve0BallastNW" }, - { 152, "rightCurve1BallastNW" }, - { 153, "rightCurve2BallastNW" }, - { 154, "rightCurve3BallastNW" }, - { 155, "rightCurve4BallastNW" }, - { 156, "rightCurve0SleeperNE" }, - { 157, "rightCurve1SleeperNE" }, - { 158, "rightCurve2SleeperNE" }, - { 159, "rightCurve3SleeperNE" }, - { 160, "rightCurve4SleeperNE" }, - { 161, "rightCurve0SleeperSE" }, - { 162, "rightCurve1SleeperSE" }, - { 163, "rightCurve2SleeperSE" }, - { 164, "rightCurve3SleeperSE" }, - { 165, "rightCurve4SleeperSE" }, - { 166, "rightCurve0SleeperSW" }, - { 167, "rightCurve1SleeperSW" }, - { 168, "rightCurve2SleeperSW" }, - { 169, "rightCurve3SleeperSW" }, - { 170, "rightCurve4SleeperSW" }, - { 171, "rightCurve0SleeperNW" }, - { 172, "rightCurve1SleeperNW" }, - { 173, "rightCurve2SleeperNW" }, - { 174, "rightCurve3SleeperNW" }, - { 175, "rightCurve4SleeperNW" }, - { 176, "rightCurve0RailNE" }, - { 177, "rightCurve1RailNE" }, - { 178, "rightCurve2RailNE" }, - { 179, "rightCurve3RailNE" }, - { 180, "rightCurve4RailNE" }, - { 181, "rightCurve0RailSE" }, - { 182, "rightCurve1RailSE" }, - { 183, "rightCurve2RailSE" }, - { 184, "rightCurve3RailSE" }, - { 185, "rightCurve4RailSE" }, - { 186, "rightCurve0RailSW" }, - { 187, "rightCurve1RailSW" }, - { 188, "rightCurve2RailSW" }, - { 189, "rightCurve3RailSW" }, - { 190, "rightCurve4RailSW" }, - { 191, "rightCurve0RailNW" }, - { 192, "rightCurve1RailNW" }, - { 193, "rightCurve2RailNW" }, - { 194, "rightCurve3RailNW" }, - { 195, "rightCurve4RailNW" }, - { 196, "straightSlopeUp0NE" }, - { 197, "straightSlopeUp1NE" }, - { 198, "straightSlopeUp0SE" }, - { 199, "straightSlopeUp1SE" }, - { 200, "straightSlopeUp0SW" }, - { 201, "straightSlopeUp1SW" }, - { 202, "straightSlopeUp0NW" }, - { 203, "straightSlopeUp1NW" }, - { 204, "straightSteepSlopeUp0NE" }, - { 205, "straightSteepSlopeUp0SE" }, - { 206, "straightSteepSlopeUp0SW" }, - { 207, "straightSteepSlopeUp0NW" }, - { 208, "rightCurveLarge0BallastNE" }, - { 209, "rightCurveLarge1BallastNE" }, - { 210, "rightCurveLarge2BallastNE" }, - { 211, "rightCurveLarge3BallastNE" }, - { 212, "rightCurveLarge4BallastNE" }, - { 213, "rightCurveLarge0BallastSE" }, - { 214, "rightCurveLarge1BallastSE" }, - { 215, "rightCurveLarge2BallastSE" }, - { 216, "rightCurveLarge3BallastSE" }, - { 217, "rightCurveLarge4BallastSE" }, - { 218, "rightCurveLarge0BallastSW" }, - { 219, "rightCurveLarge1BallastSW" }, - { 220, "rightCurveLarge2BallastSW" }, - { 221, "rightCurveLarge3BallastSW" }, - { 222, "rightCurveLarge4BallastSW" }, - { 223, "rightCurveLarge0BallastNW" }, - { 224, "rightCurveLarge1BallastNW" }, - { 225, "rightCurveLarge2BallastNW" }, - { 226, "rightCurveLarge3BallastNW" }, - { 227, "rightCurveLarge4BallastNW" }, - { 228, "leftCurveLarge0BallastNE" }, - { 229, "leftCurveLarge1BallastNE" }, - { 230, "leftCurveLarge2BallastNE" }, - { 231, "leftCurveLarge3BallastNE" }, - { 232, "leftCurveLarge4BallastNE" }, - { 233, "leftCurveLarge0BallastSE" }, - { 234, "leftCurveLarge1BallastSE" }, - { 235, "leftCurveLarge2BallastSE" }, - { 236, "leftCurveLarge3BallastSE" }, - { 237, "leftCurveLarge4BallastSE" }, - { 238, "leftCurveLarge0BallastSW" }, - { 239, "leftCurveLarge1BallastSW" }, - { 240, "leftCurveLarge2BallastSW" }, - { 241, "leftCurveLarge3BallastSW" }, - { 242, "leftCurveLarge4BallastSW" }, - { 243, "leftCurveLarge0BallastNW" }, - { 244, "leftCurveLarge1BallastNW" }, - { 245, "leftCurveLarge2BallastNW" }, - { 246, "leftCurveLarge3BallastNW" }, - { 247, "leftCurveLarge4BallastNW" }, - { 248, "rightCurveLarge0SleeperNE" }, - { 249, "rightCurveLarge1SleeperNE" }, - { 250, "rightCurveLarge2SleeperNE" }, - { 251, "rightCurveLarge3SleeperNE" }, - { 252, "rightCurveLarge4SleeperNE" }, - { 253, "rightCurveLarge0SleeperSE" }, - { 254, "rightCurveLarge1SleeperSE" }, - { 255, "rightCurveLarge2SleeperSE" }, - { 256, "rightCurveLarge3SleeperSE" }, - { 257, "rightCurveLarge4SleeperSE" }, - { 258, "rightCurveLarge0SleeperSW" }, - { 259, "rightCurveLarge1SleeperSW" }, - { 260, "rightCurveLarge2SleeperSW" }, - { 261, "rightCurveLarge3SleeperSW" }, - { 262, "rightCurveLarge4SleeperSW" }, - { 263, "rightCurveLarge0SleeperNW" }, - { 264, "rightCurveLarge1SleeperNW" }, - { 265, "rightCurveLarge2SleeperNW" }, - { 266, "rightCurveLarge3SleeperNW" }, - { 267, "rightCurveLarge4SleeperNW" }, - { 268, "leftCurveLarge0SleeperNE" }, - { 269, "leftCurveLarge1SleeperNE" }, - { 270, "leftCurveLarge2SleeperNE" }, - { 271, "leftCurveLarge3SleeperNE" }, - { 272, "leftCurveLarge4SleeperNE" }, - { 273, "leftCurveLarge0SleeperSE" }, - { 274, "leftCurveLarge1SleeperSE" }, - { 275, "leftCurveLarge2SleeperSE" }, - { 276, "leftCurveLarge3SleeperSE" }, - { 277, "leftCurveLarge4SleeperSE" }, - { 278, "leftCurveLarge0SleeperSW" }, - { 279, "leftCurveLarge1SleeperSW" }, - { 280, "leftCurveLarge2SleeperSW" }, - { 281, "leftCurveLarge3SleeperSW" }, - { 282, "leftCurveLarge4SleeperSW" }, - { 283, "leftCurveLarge0SleeperNW" }, - { 284, "leftCurveLarge1SleeperNW" }, - { 285, "leftCurveLarge2SleeperNW" }, - { 286, "leftCurveLarge3SleeperNW" }, - { 287, "leftCurveLarge4SleeperNW" }, - { 288, "rightCurveLarge0RailNE" }, - { 289, "rightCurveLarge1RailNE" }, - { 290, "rightCurveLarge2RailNE" }, - { 291, "rightCurveLarge3RailNE" }, - { 292, "rightCurveLarge4RailNE" }, - { 293, "rightCurveLarge0RailSE" }, - { 294, "rightCurveLarge1RailSE" }, - { 295, "rightCurveLarge2RailSE" }, - { 296, "rightCurveLarge3RailSE" }, - { 297, "rightCurveLarge4RailSE" }, - { 298, "rightCurveLarge0RailSW" }, - { 299, "rightCurveLarge1RailSW" }, - { 300, "rightCurveLarge2RailSW" }, - { 301, "rightCurveLarge3RailSW" }, - { 302, "rightCurveLarge4RailSW" }, - { 303, "rightCurveLarge0RailNW" }, - { 304, "rightCurveLarge1RailNW" }, - { 305, "rightCurveLarge2RailNW" }, - { 306, "rightCurveLarge3RailNW" }, - { 307, "rightCurveLarge4RailNW" }, - { 308, "leftCurveLarge0RailNE" }, - { 309, "leftCurveLarge1RailNE" }, - { 310, "leftCurveLarge2RailNE" }, - { 311, "leftCurveLarge3RailNE" }, - { 312, "leftCurveLarge4RailNE" }, - { 313, "leftCurveLarge0RailSE" }, - { 314, "leftCurveLarge1RailSE" }, - { 315, "leftCurveLarge2RailSE" }, - { 316, "leftCurveLarge3RailSE" }, - { 317, "leftCurveLarge4RailSE" }, - { 318, "leftCurveLarge0RailSW" }, - { 319, "leftCurveLarge1RailSW" }, - { 320, "leftCurveLarge2RailSW" }, - { 321, "leftCurveLarge3RailSW" }, - { 322, "leftCurveLarge4RailSW" }, - { 323, "leftCurveLarge0RailNW" }, - { 324, "leftCurveLarge1RailNW" }, - { 325, "leftCurveLarge2RailNW" }, - { 326, "leftCurveLarge3RailNW" }, - { 327, "leftCurveLarge4RailNW" }, - { 328, "diagonal0BallastNE" }, - { 329, "diagonal1BallastSW" }, - { 330, "diagonal1BallastNE" }, - { 331, "diagonal0BallastSW" }, - { 332, "diagonal0BallastSE" }, - { 333, "diagonal1BallastNW" }, - { 334, "diagonal1BallastSE" }, - { 335, "diagonal0BallastNW" }, - { 336, "diagonal0SleeperNE" }, - { 337, "diagonal1SleeperSW" }, - { 338, "diagonal1SleeperNE" }, - { 339, "diagonal0SleeperSW" }, - { 340, "diagonal0SleeperSE" }, - { 341, "diagonal1SleeperNW" }, - { 342, "diagonal1SleeperSE" }, - { 343, "diagonal0SleeperNW" }, - { 344, "diagonal0RailNE" }, - { 345, "diagonal1RailSW" }, - { 346, "diagonal1RailNE" }, - { 347, "diagonal0RailSW" }, - { 348, "diagonal0RailSE" }, - { 349, "diagonal1RailNW" }, - { 350, "diagonal1RailSE" }, - { 351, "diagonal0RailNW" }, - { 352, "sBendLeft0BallastNE" }, - { 353, "sBendLeft1BallastNE" }, - { 354, "sBendLeft1BallastSW" }, - { 355, "sBendLeft0BallastSW" }, - { 356, "sBendLeft0BallastSE" }, - { 357, "sBendLeft1BallastSE" }, - { 358, "sBendLeft1BallastNW" }, - { 359, "sBendLeft0BallastNW" }, - { 360, "sBendRight0BallastNE" }, - { 361, "sBendRight1BallastNE" }, - { 362, "sBendRight1BallastSW" }, - { 363, "sBendRight0BallastSW" }, - { 364, "sBendRight0BallastSE" }, - { 365, "sBendRight1BallastSE" }, - { 366, "sBendRight1BallastNW" }, - { 367, "sBendRight0BallastNW" }, - { 368, "sBendLeft0SleeperNE" }, - { 369, "sBendLeft1SleeperNE" }, - { 370, "sBendLeft1SleeperSW" }, - { 371, "sBendLeft0SleeperSW" }, - { 372, "sBendLeft0SleeperSE" }, - { 373, "sBendLeft1SleeperSE" }, - { 374, "sBendLeft1SleeperNW" }, - { 375, "sBendLeft0SleeperNW" }, - { 376, "sBendRight0SleeperNE" }, - { 377, "sBendRight1SleeperNE" }, - { 378, "sBendRight1SleeperSW" }, - { 379, "sBendRight0SleeperSW" }, - { 380, "sBendRight0SleeperSE" }, - { 381, "sBendRight1SleeperSE" }, - { 382, "sBendRight1SleeperNW" }, - { 383, "sBendRight0SleeperNW" }, - { 384, "sBendLeft0RailNE" }, - { 385, "sBendLeft1RailNE" }, - { 386, "sBendLeft1RailSW" }, - { 387, "sBendLeft0RailSW" }, - { 388, "sBendLeft0RailSE" }, - { 389, "sBendLeft1RailSE" }, - { 390, "sBendLeft1RailNW" }, - { 391, "sBendLeft0RailNW" }, - { 392, "sBendRight0RailNE" }, - { 393, "sBendRight1RailNE" }, - { 394, "sBendRight1RailSW" }, - { 395, "sBendRight0RailSW" }, - { 396, "sBendRight0RailSE" }, - { 397, "sBendRight1RailSE" }, - { 398, "sBendRight1RailNW" }, - { 399, "sBendRight0RailNW" }, - { 400, "rightCurveVerySmall0BallastNE" }, - { 401, "rightCurveVerySmall0BallastSE" }, - { 402, "rightCurveVerySmall0BallastSW" }, - { 403, "rightCurveVerySmall0BallastNW" }, - { 404, "rightCurveVerySmall0SleeperNE" }, - { 405, "rightCurveVerySmall0SleeperSE" }, - { 406, "rightCurveVerySmall0SleeperSW" }, - { 407, "rightCurveVerySmall0SleeperNW" }, - { 408, "rightCurveVerySmall0RailNE" }, - { 409, "rightCurveVerySmall0RailSE" }, - { 410, "rightCurveVerySmall0RailSW" }, - { 411, "rightCurveVerySmall0RailNW" }, - }; } diff --git a/Definitions/ObjectModels/Objects/TrackExtra/TrackExtraObject.cs b/Definitions/ObjectModels/Objects/TrackExtra/TrackExtraObject.cs index 72b3fa05..262778e0 100644 --- a/Definitions/ObjectModels/Objects/TrackExtra/TrackExtraObject.cs +++ b/Definitions/ObjectModels/Objects/TrackExtra/TrackExtraObject.cs @@ -1,9 +1,8 @@ using Definitions.ObjectModels.Objects.Track; -using System.Diagnostics.CodeAnalysis; namespace Definitions.ObjectModels.Objects.TrackExtra; -public class TrackExtraObject : ILocoStruct, IImageTableNameProvider +public class TrackExtraObject : ILocoStruct { public TrackTraitFlags TrackPieces { get; set; } public uint8_t PaintStyle { get; set; } @@ -31,228 +30,4 @@ public bool Validate() return BuildCostFactor > 0; } - - public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) - => ImageIdNameMap.TryGetValue(id - 8, out value); - - // taken from OpenLoco TrackExtraObject.h - public static Dictionary ImageIdNameMap = new() - { - { -8, "previewImage0" }, - { -7, "previewImage1" }, - { -6, "previewImage2" }, - { -5, "previewImage3" }, - { -4, "previewImage4" }, - { -3, "previewImage5" }, - { -2, "previewImage6" }, - { -1, "previewImage7" }, - { 0, "kStraight0NE" }, - { 1, "kStraight0SE" }, - { 2, "kStraight0SW" }, - { 3, "kStraight0NW" }, - { 4, "kRightCurveSmall0NE" }, - { 5, "kRightCurveSmall1NE" }, - { 6, "kRightCurveSmall2NE" }, - { 7, "kRightCurveSmall3NE" }, - { 8, "kRightCurveSmall0SE" }, - { 9, "kRightCurveSmall1SE" }, - { 10, "kRightCurveSmall2SE" }, - { 11, "kRightCurveSmall3SE" }, - { 12, "kRightCurveSmall0SW" }, - { 13, "kRightCurveSmall1SW" }, - { 14, "kRightCurveSmall2SW" }, - { 15, "kRightCurveSmall3SW" }, - { 16, "kRightCurveSmall0NW" }, - { 17, "kRightCurveSmall1NW" }, - { 18, "kRightCurveSmall2NW" }, - { 19, "kRightCurveSmall3NW" }, - { 20, "kRightCurve0NE" }, - { 21, "kRightCurve1NE" }, - { 22, "kRightCurve2NE" }, - { 23, "kRightCurve3NE" }, - { 24, "kRightCurve4NE" }, - { 25, "kRightCurve0SE" }, - { 26, "kRightCurve1SE" }, - { 27, "kRightCurve2SE" }, - { 28, "kRightCurve3SE" }, - { 29, "kRightCurve4SE" }, - { 30, "kRightCurve0SW" }, - { 31, "kRightCurve1SW" }, - { 32, "kRightCurve2SW" }, - { 33, "kRightCurve3SW" }, - { 34, "kRightCurve4SW" }, - { 35, "kRightCurve0NW" }, - { 36, "kRightCurve1NW" }, - { 37, "kRightCurve2NW" }, - { 38, "kRightCurve3NW" }, - { 39, "kRightCurve4NW" }, - { 40, "kSBendLeft0NE" }, - { 41, "kSBendLeft1NE" }, - { 42, "kSBendLeft2NE" }, - { 43, "kSBendLeft3NE" }, - { 44, "kSBendLeft0SE" }, - { 45, "kSBendLeft1SE" }, - { 46, "kSBendLeft2SE" }, - { 47, "kSBendLeft3SE" }, - { 48, "kSBendLeft3SW" }, - { 49, "kSBendLeft2SW" }, - { 50, "kSBendLeft1SW" }, - { 51, "kSBendLeft0SW" }, - { 52, "kSBendLeft3NW" }, - { 53, "kSBendLeft2NW" }, - { 54, "kSBendLeft1NW" }, - { 55, "kSBendLeft0NW" }, - { 56, "kSBendRight0NE" }, - { 57, "kSBendRight1NE" }, - { 58, "kSBendRight2NE" }, - { 59, "kSBendRight3NE" }, - { 60, "kSBendRight0SE" }, - { 61, "kSBendRight1SE" }, - { 62, "kSBendRight2SE" }, - { 63, "kSBendRight3SE" }, - { 64, "kSBendRight3SW" }, - { 65, "kSBendRight2SW" }, - { 66, "kSBendRight1SW" }, - { 67, "kSBendRight0SW" }, - { 68, "kSBendRight3NW" }, - { 69, "kSBendRight2NW" }, - { 70, "kSBendRight1NW" }, - { 71, "kSBendRight0NW" }, - { 72, "kStraightSlopeUp0NE" }, - { 73, "kStraightSlopeUp1NE" }, - { 74, "kStraightSlopeUp0SE" }, - { 75, "kStraightSlopeUp1SE" }, - { 76, "kStraightSlopeUp0SW" }, - { 77, "kStraightSlopeUp1SW" }, - { 78, "kStraightSlopeUp0NW" }, - { 79, "kStraightSlopeUp1NW" }, - { 80, "kStraightSteepSlopeUp0NE" }, - { 81, "kStraightSteepSlopeUp0SE" }, - { 82, "kStraightSteepSlopeUp0SW" }, - { 83, "kStraightSteepSlopeUp0NW" }, - { 84, "kRightCurveSmallSlopeUp0NE" }, - { 85, "kRightCurveSmallSlopeUp1NE" }, - { 86, "kRightCurveSmallSlopeUp2NE" }, - { 87, "kRightCurveSmallSlopeUp3NE" }, - { 88, "kRightCurveSmallSlopeUp0SE" }, - { 89, "kRightCurveSmallSlopeUp1SE" }, - { 90, "kRightCurveSmallSlopeUp2SE" }, - { 91, "kRightCurveSmallSlopeUp3SE" }, - { 92, "kRightCurveSmallSlopeUp0SW" }, - { 93, "kRightCurveSmallSlopeUp1SW" }, - { 94, "kRightCurveSmallSlopeUp2SW" }, - { 95, "kRightCurveSmallSlopeUp3SW" }, - { 96, "kRightCurveSmallSlopeUp0NW" }, - { 97, "kRightCurveSmallSlopeUp1NW" }, - { 98, "kRightCurveSmallSlopeUp2NW" }, - { 99, "kRightCurveSmallSlopeUp3NW" }, - { 100, "kRightCurveSmallSlopeDown0NE" }, - { 101, "kRightCurveSmallSlopeDown1NE" }, - { 102, "kRightCurveSmallSlopeDown2NE" }, - { 103, "kRightCurveSmallSlopeDown3NE" }, - { 104, "kRightCurveSmallSlopeDown0SE" }, - { 105, "kRightCurveSmallSlopeDown1SE" }, - { 106, "kRightCurveSmallSlopeDown2SE" }, - { 107, "kRightCurveSmallSlopeDown3SE" }, - { 108, "kRightCurveSmallSlopeDown0SW" }, - { 109, "kRightCurveSmallSlopeDown1SW" }, - { 110, "kRightCurveSmallSlopeDown2SW" }, - { 111, "kRightCurveSmallSlopeDown3SW" }, - { 112, "kRightCurveSmallSlopeDown0NW" }, - { 113, "kRightCurveSmallSlopeDown1NW" }, - { 114, "kRightCurveSmallSlopeDown2NW" }, - { 115, "kRightCurveSmallSlopeDown3NW" }, - { 116, "kRightCurveSmallSteepSlopeUp0NE" }, - { 117, "kRightCurveSmallSteepSlopeUp1NE" }, - { 118, "kRightCurveSmallSteepSlopeUp2NE" }, - { 119, "kRightCurveSmallSteepSlopeUp3NE" }, - { 120, "kRightCurveSmallSteepSlopeUp0SE" }, - { 121, "kRightCurveSmallSteepSlopeUp1SE" }, - { 122, "kRightCurveSmallSteepSlopeUp2SE" }, - { 123, "kRightCurveSmallSteepSlopeUp3SE" }, - { 124, "kRightCurveSmallSteepSlopeUp0SW" }, - { 125, "kRightCurveSmallSteepSlopeUp1SW" }, - { 126, "kRightCurveSmallSteepSlopeUp2SW" }, - { 127, "kRightCurveSmallSteepSlopeUp3SW" }, - { 128, "kRightCurveSmallSteepSlopeUp0NW" }, - { 129, "kRightCurveSmallSteepSlopeUp1NW" }, - { 130, "kRightCurveSmallSteepSlopeUp2NW" }, - { 131, "kRightCurveSmallSteepSlopeUp3NW" }, - { 132, "kRightCurveSmallSteepSlopeDown0NE" }, - { 133, "kRightCurveSmallSteepSlopeDown1NE" }, - { 134, "kRightCurveSmallSteepSlopeDown2NE" }, - { 135, "kRightCurveSmallSteepSlopeDown3NE" }, - { 136, "kRightCurveSmallSteepSlopeDown0SE" }, - { 137, "kRightCurveSmallSteepSlopeDown1SE" }, - { 138, "kRightCurveSmallSteepSlopeDown2SE" }, - { 139, "kRightCurveSmallSteepSlopeDown3SE" }, - { 140, "kRightCurveSmallSteepSlopeDown0SW" }, - { 141, "kRightCurveSmallSteepSlopeDown1SW" }, - { 142, "kRightCurveSmallSteepSlopeDown2SW" }, - { 143, "kRightCurveSmallSteepSlopeDown3SW" }, - { 144, "kRightCurveSmallSteepSlopeDown0NW" }, - { 145, "kRightCurveSmallSteepSlopeDown1NW" }, - { 146, "kRightCurveSmallSteepSlopeDown2NW" }, - { 147, "kRightCurveSmallSteepSlopeDown3NW" }, - { 148, "kRightCurveLarge0NE" }, - { 149, "kRightCurveLarge1NE" }, - { 150, "kRightCurveLarge2NE" }, - { 151, "kRightCurveLarge3NE" }, - { 152, "kRightCurveLarge4NE" }, - { 153, "kRightCurveLarge0SE" }, - { 154, "kRightCurveLarge1SE" }, - { 155, "kRightCurveLarge2SE" }, - { 156, "kRightCurveLarge3SE" }, - { 157, "kRightCurveLarge4SE" }, - { 158, "kRightCurveLarge0SW" }, - { 159, "kRightCurveLarge1SW" }, - { 160, "kRightCurveLarge2SW" }, - { 161, "kRightCurveLarge3SW" }, - { 162, "kRightCurveLarge4SW" }, - { 163, "kRightCurveLarge0NW" }, - { 164, "kRightCurveLarge1NW" }, - { 165, "kRightCurveLarge2NW" }, - { 166, "kRightCurveLarge3NW" }, - { 167, "kRightCurveLarge4NW" }, - { 168, "kLeftCurveLarge0NE" }, - { 169, "kLeftCurveLarge1NE" }, - { 170, "kLeftCurveLarge2NE" }, - { 171, "kLeftCurveLarge3NE" }, - { 172, "kLeftCurveLarge4NE" }, - { 173, "kLeftCurveLarge0SE" }, - { 174, "kLeftCurveLarge1SE" }, - { 175, "kLeftCurveLarge2SE" }, - { 176, "kLeftCurveLarge3SE" }, - { 177, "kLeftCurveLarge4SE" }, - { 178, "kLeftCurveLarge0SW" }, - { 179, "kLeftCurveLarge1SW" }, - { 180, "kLeftCurveLarge2SW" }, - { 181, "kLeftCurveLarge3SW" }, - { 182, "kLeftCurveLarge4SW" }, - { 183, "kLeftCurveLarge0NW" }, - { 184, "kLeftCurveLarge1NW" }, - { 185, "kLeftCurveLarge2NW" }, - { 186, "kLeftCurveLarge3NW" }, - { 187, "kLeftCurveLarge4NW" }, - { 188, "kDiagonal0NE" }, - { 189, "kDiagonal2NE" }, - { 190, "kDiagonal1NE" }, - { 191, "kDiagonal3NE" }, - { 192, "kDiagonal0SE" }, - { 193, "kDiagonal2SE" }, - { 194, "kDiagonal1SE" }, - { 195, "kDiagonal3SE" }, - { 196, "kDiagonal0SW" }, - { 197, "kDiagonal2SW" }, - { 198, "kDiagonal1SW" }, - { 199, "kDiagonal3SW" }, - { 200, "kDiagonal0NW" }, - { 201, "kDiagonal2NW" }, - { 202, "kDiagonal1NW" }, - { 203, "kDiagonal3NW" }, - { 204, "kRightCurveVerySmall0NE" }, - { 205, "kRightCurveVerySmall0SE" }, - { 206, "kRightCurveVerySmall0SW" }, - { 207, "kRightCurveVerySmall0NW" }, - }; } diff --git a/Definitions/ObjectModels/Objects/TrackSignal/TrackSignalObject.cs b/Definitions/ObjectModels/Objects/TrackSignal/TrackSignalObject.cs index 3dfc1e8f..16cf390f 100644 --- a/Definitions/ObjectModels/Objects/TrackSignal/TrackSignalObject.cs +++ b/Definitions/ObjectModels/Objects/TrackSignal/TrackSignalObject.cs @@ -1,9 +1,8 @@ using Definitions.ObjectModels.Types; -using System.Diagnostics.CodeAnalysis; namespace Definitions.ObjectModels.Objects.TrackSignal; -public class TrackSignalObject : ILocoStruct, IImageTableNameProvider +public class TrackSignalObject : ILocoStruct { public TrackSignalObjectFlags Flags { get; set; } public uint8_t AnimationSpeed { get; set; } @@ -58,15 +57,4 @@ public bool Validate() return true; } - - public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) - => ImageIdNameMap.TryGetValue(id, out value); - - public static Dictionary ImageIdNameMap = new() - { - { 80, "redLights" }, - { 88, "redLights2" }, - { 96, "greenLights" }, - { 104, "greenLights2" }, - }; } diff --git a/Definitions/ObjectModels/Objects/TrackStation/TrackStationObject.cs b/Definitions/ObjectModels/Objects/TrackStation/TrackStationObject.cs index 54f1ef02..6cd81c4f 100644 --- a/Definitions/ObjectModels/Objects/TrackStation/TrackStationObject.cs +++ b/Definitions/ObjectModels/Objects/TrackStation/TrackStationObject.cs @@ -1,11 +1,10 @@ using Definitions.ObjectModels.Objects.Shared; using Definitions.ObjectModels.Objects.Track; using Definitions.ObjectModels.Types; -using System.Diagnostics.CodeAnalysis; namespace Definitions.ObjectModels.Objects.TrackStation; -public class TrackStationObject : ILocoStruct, IImageTableNameProvider +public class TrackStationObject : ILocoStruct { public uint8_t PaintStyle { get; set; } public uint8_t Height { get; set; } @@ -49,44 +48,4 @@ public bool Validate() return true; // CompatibleTrackObjects.Count <= TrackStationObjectLoader.Constants.MaxNumCompatible; } - - public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) - { - return ImageIdNameMap.TryGetValue(id, out value); - } - - public static readonly Dictionary ImageIdNameMap = new() - { - { 0, "preview_image" }, - { 1, "preview_image_glass_overlay" }, - { 2, "totalPreviewImages" }, - }; - - // These are relative to ImageOffsets - // ImageOffsets is the imageIds per sequenceIndex (for start/middle/end of the platform) - //namespace Style0 - //{ - // constexpr uint32_t straightBackNE = 0; - // constexpr uint32_t straightFrontNE = 1; - // constexpr uint32_t straightCanopyNE = 2; - // constexpr uint32_t straightCanopyTranslucentNE = 3; - // constexpr uint32_t straightBackSE = 4; - // constexpr uint32_t straightFrontSE = 5; - // constexpr uint32_t straightCanopySE = 6; - // constexpr uint32_t straightCanopyTranslucentSE = 7; - // constexpr uint32_t diagonalNE0 = 8; - // constexpr uint32_t diagonalNE3 = 9; - // constexpr uint32_t diagonalNE1 = 10; - // constexpr uint32_t diagonalCanopyNE1 = 11; - // constexpr uint32_t diagonalCanopyTranslucentNE1 = 12; - // constexpr uint32_t diagonalSE1 = 13; - // constexpr uint32_t diagonalSE2 = 14; - // constexpr uint32_t diagonalSE3 = 15; - // constexpr uint32_t diagonalCanopyTranslucentSE3 = 16; - // constexpr uint32_t totalNumImages = 17; - //} - //namespace Style1 - //{ - // constexpr uint32_t totalNumImages = 8; - //} } diff --git a/Definitions/ObjectModels/Objects/Tree/TreeObject.cs b/Definitions/ObjectModels/Objects/Tree/TreeObject.cs index 85e4fa31..87c44232 100644 --- a/Definitions/ObjectModels/Objects/Tree/TreeObject.cs +++ b/Definitions/ObjectModels/Objects/Tree/TreeObject.cs @@ -1,8 +1,6 @@ -using System.Diagnostics.CodeAnalysis; - namespace Definitions.ObjectModels.Objects.Tree; -public class TreeObject : ILocoStruct, IImageTableNameProvider +public class TreeObject : ILocoStruct { public uint8_t Clearance { get; set; } public uint8_t Height { get; set; } @@ -57,7 +55,4 @@ public bool Validate() return var_05 >= var_04; } - - public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) - => throw new NotImplementedException(); } diff --git a/Definitions/ObjectModels/Objects/Tunnel/TunnelObject.cs b/Definitions/ObjectModels/Objects/Tunnel/TunnelObject.cs index 5fcbfaf0..776b017c 100644 --- a/Definitions/ObjectModels/Objects/Tunnel/TunnelObject.cs +++ b/Definitions/ObjectModels/Objects/Tunnel/TunnelObject.cs @@ -2,18 +2,7 @@ namespace Definitions.ObjectModels.Objects.Tunnel; -public class TunnelObject : ILocoStruct, IImageTableNameProvider +public class TunnelObject : ILocoStruct { public bool Validate() => true; - - public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) - => ImageIdNameMap.TryGetValue(id, out value); - - public static readonly Dictionary ImageIdNameMap = new() - { - { 0, "south west back" }, - { 1, "south west front" }, - { 2, "south east back" }, - { 3, "south east front" }, - }; } diff --git a/Definitions/ObjectModels/Objects/Vehicle/VehicleObject.cs b/Definitions/ObjectModels/Objects/Vehicle/VehicleObject.cs index b712bb8c..c1a39565 100644 --- a/Definitions/ObjectModels/Objects/Vehicle/VehicleObject.cs +++ b/Definitions/ObjectModels/Objects/Vehicle/VehicleObject.cs @@ -1,10 +1,9 @@ using Definitions.ObjectModels.Objects.Cargo; using Definitions.ObjectModels.Types; -using System.Diagnostics.CodeAnalysis; namespace Definitions.ObjectModels.Objects.Vehicle; -public class VehicleObject : ILocoStruct, IImageTableNameProvider +public class VehicleObject : ILocoStruct { public TransportMode Mode { get; set; } public VehicleType Type { get; set; } @@ -179,7 +178,4 @@ public bool Validate() return true; } - - public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) - => throw new NotImplementedException(); } diff --git a/Definitions/ObjectModels/Objects/Wall/WallObject.cs b/Definitions/ObjectModels/Objects/Wall/WallObject.cs index bc3f6e9f..05dd813b 100644 --- a/Definitions/ObjectModels/Objects/Wall/WallObject.cs +++ b/Definitions/ObjectModels/Objects/Wall/WallObject.cs @@ -1,50 +1,12 @@ -using System.Diagnostics.CodeAnalysis; - namespace Definitions.ObjectModels.Objects.Wall; -public class WallObject : ILocoStruct, IImageTableNameProvider +public class WallObject : ILocoStruct { public uint8_t Height { get; set; } public uint8_t ToolId { get; set; } // unused in loco??? public WallObjectFlags1 Flags1 { get; set; } = WallObjectFlags1.None; public WallObjectFlags2 Flags2 { get; set; } = WallObjectFlags2.None; // unused in loco??? - public bool Validate() => true; - - public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) - { - var result = ImageIdNameMap.TryGetValue(id, out value); - - if (id is >= 0 and <= 5 && Flags1.HasFlag(WallObjectFlags1.DoubleSided)) - { - value = $"{value} back"; - } - - if (id is >= 6 and <= 11 && Flags1.HasFlag(WallObjectFlags1.HasGlass)) - { - value = $"{value} glass overlay"; - } - else - { - value = $"{value} front"; - } - - return result; - } - - public static Dictionary ImageIdNameMap = new() - { - { 0, "Flat SE" }, - { 1, "Flat NE" }, - { 2, "Sloped SE" }, - { 3, "Sloped NE" }, - { 4, "Sloped NW" }, - { 5, "Sloped SW" }, - { 6, "Flat SE" }, - { 7, "Flat NE" }, - { 8, "Sloped SE" }, - { 9, "Sloped NE" }, - { 10, "Sloped NW" }, - { 11, "Sloped SW" }, - }; + public bool Validate() + => true; } diff --git a/Definitions/ObjectModels/Objects/Water/WaterObject.cs b/Definitions/ObjectModels/Objects/Water/WaterObject.cs index b867f1e9..08b09b4f 100644 --- a/Definitions/ObjectModels/Objects/Water/WaterObject.cs +++ b/Definitions/ObjectModels/Objects/Water/WaterObject.cs @@ -1,11 +1,7 @@ -using System.Diagnostics.CodeAnalysis; -using System.Text.Json.Serialization; - namespace Definitions.ObjectModels.Objects.Water; -public class WaterObject : ILocoStruct, IImageTableNameProvider +public class WaterObject : ILocoStruct { - [JsonInclude] public uint8_t CostIndex { get; set; } public uint8_t var_03 { get; set; } public int16_t CostFactor { get; set; } @@ -24,87 +20,4 @@ public bool Validate() return true; } - - public bool TryGetImageName(int id, [MaybeNullWhen(false)] out string value) - => ImageIdNameMap.TryGetValue(id, out value); - - public static Dictionary ImageIdNameMap = new() - { - { 0, "zoom1 wave overlay full" }, - { 1, "zoom1 wave overlay west" }, - { 2, "zoom1 wave overlay east" }, - { 3, "zoom1 wave overlay north" }, - { 4, "zoom1 wave overlay south" }, - { 5, "zoom1 wave overlay full" }, - { 6, "zoom1 wave half-tile west" }, - { 7, "zoom1 wave half-tile east" }, - { 8, "zoom1 wave half-tile north" }, - { 9, "zoom1 wave half-tile south" }, - { 10, "zoom2 wave overlay full" }, - { 11, "zoom2 wave overlay west" }, - { 12, "zoom2 wave overlay east" }, - { 13, "zoom2 wave overlay north" }, - { 14, "zoom2 wave overlay south" }, - { 15, "zoom2 wave overlay full" }, - { 16, "zoom2 wave half-tile west" }, - { 17, "zoom2 wave half-tile east" }, - { 18, "zoom2 wave half-tile north" }, - { 19, "zoom2 wave half-tile south" }, - { 20, "zoom3 wave overlay full" }, - { 21, "zoom3 wave overlay west" }, - { 22, "zoom3 wave overlay east" }, - { 23, "zoom3 wave overlay north" }, - { 24, "zoom3 wave overlay south" }, - { 25, "zoom3 wave overlay full" }, - { 26, "zoom3 wave half-tile west" }, - { 27, "zoom3 wave half-tile east" }, - { 28, "zoom3 wave half-tile north" }, - { 29, "zoom3 wave half-tile south" }, - { 30, "zoom4 wave overlay full" }, - { 31, "zoom4 wave overlay west" }, - { 32, "zoom4 wave overlay east" }, - { 33, "zoom4 wave overlay north" }, - { 34, "zoom4 wave overlay south" }, - { 35, "zoom4 wave overlay full" }, - { 36, "zoom4 wave half-tile west" }, - { 37, "zoom4 wave half-tile east" }, - { 38, "zoom4 wave half-tile north" }, - { 39, "zoom4 wave half-tile south" }, - { 40, "minimap palette" }, - { 41, "water colour palette" }, - { 42, "water icon animation 0" }, - { 43, "water icon animation 1" }, - { 44, "water icon animation 2" }, - { 45, "water icon animation 3" }, - { 46, "water icon animation 4" }, - { 47, "water icon animation 5" }, - { 48, "water icon animation 6" }, - { 49, "water icon animation 7" }, - { 50, "water icon animation 8" }, - { 51, "water icon animation 9" }, - { 52, "water icon animation 10" }, - { 54, "water icon animation 11" }, - { 55, "water icon animation 12" }, - { 56, "water icon animation 13" }, - { 57, "water icon animation 14" }, - { 53, "water icon animation 15" }, - { 58, "pick up water vehicle" }, - { 59, "place down water vehicle" }, - { 60, "water animation frame 0" }, - { 61, "water animation frame 1" }, - { 62, "water animation frame 2" }, - { 63, "water animation frame 3" }, - { 64, "water animation frame 4" }, - { 65, "water animation frame 5" }, - { 66, "water animation frame 6" }, - { 67, "water animation frame 7" }, - { 68, "water animation frame 8" }, - { 69, "water animation frame 9" }, - { 70, "water animation frame 10" }, - { 71, "water animation frame 11" }, - { 72, "water animation frame 12" }, - { 73, "water animation frame 13" }, - { 74, "water animation frame 14" }, - { 75, "water animation frame 15" }, - }; } From 127d6ff0b087c1ceb1610dfb118c90576368ed85 Mon Sep 17 00:00:00 2001 From: Benjamin Sutas Date: Wed, 17 Sep 2025 01:00:04 +1000 Subject: [PATCH 16/21] add basic g1.dat groups --- Dat/Types/G1Dat.cs | 19 ++++++++++++++++++- Definitions/ObjectModels/ImageTableGrouper.cs | 1 - 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/Dat/Types/G1Dat.cs b/Dat/Types/G1Dat.cs index 81de0c3b..7e70c7d5 100644 --- a/Dat/Types/G1Dat.cs +++ b/Dat/Types/G1Dat.cs @@ -20,7 +20,24 @@ public G1Dat(G1Header g1Header, List graphicsElements) G1Header = g1Header; ImageTable = new ImageTable { - Groups = [("All", graphicsElements)] + Groups = + [ + ("terrain-masks", [.. graphicsElements[0..417], .. graphicsElements[3629..3896]]), + ("palettes", [.. graphicsElements[417..428], .. graphicsElements[2170..2304]]), + ("arrows", [.. graphicsElements[428..444], .. graphicsElements[449..457], .. graphicsElements[3492..3504]]), + ("unk", graphicsElements[444..449]), + ("supports", graphicsElements[457..1117]), + ("glyphs", graphicsElements[1117..2169]), + ("loading-bar", graphicsElements[2326..2335]), + ("interface", [.. graphicsElements[2335..2470], .. graphicsElements[3477..3479], .. graphicsElements[2305..2326], .. graphicsElements[3539..3547]]), + ("height-markers", graphicsElements[2470..3238]), + ("numerical-markers", graphicsElements[3238..3302]), + ("unk", graphicsElements[3302..3362]), + ("particles", graphicsElements[3362..3477]), + ("masks", [.. graphicsElements[3479..3492], graphicsElements[3504]]), + ("object-types", graphicsElements[3505..3539]), + ("title", graphicsElements[3547..3629]), + ] }; } diff --git a/Definitions/ObjectModels/ImageTableGrouper.cs b/Definitions/ObjectModels/ImageTableGrouper.cs index 93ce2a4f..552e7eb4 100644 --- a/Definitions/ObjectModels/ImageTableGrouper.cs +++ b/Definitions/ObjectModels/ImageTableGrouper.cs @@ -14,7 +14,6 @@ using Definitions.ObjectModels.Objects.Wall; using Definitions.ObjectModels.Objects.Water; using Definitions.ObjectModels.Types; -using System.Collections.ObjectModel; using System.Diagnostics.CodeAnalysis; namespace Definitions.ObjectModels; From 8460b4090eef046b2238c03541b38c1bd257347b Mon Sep 17 00:00:00 2001 From: Benjamin Sutas Date: Wed, 17 Sep 2025 01:14:18 +1000 Subject: [PATCH 17/21] add more image groups --- Dat/Loaders/ClimateObjectLoader.cs | 8 ++--- Definitions/ObjectModels/ImageTableGrouper.cs | 30 +++++++++++++++++-- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/Dat/Loaders/ClimateObjectLoader.cs b/Dat/Loaders/ClimateObjectLoader.cs index 86dad4c0..32450bad 100644 --- a/Dat/Loaders/ClimateObjectLoader.cs +++ b/Dat/Loaders/ClimateObjectLoader.cs @@ -46,12 +46,10 @@ public static LocoObject Load(Stream stream) // N/A // image table - var imageList = SawyerStreamReader.ReadImageTable(br).Table; + // N/A but Climate has an empty image table for some reason + _ = SawyerStreamReader.ReadImageTable(br).Table; - // define groups - var imageTable = ImageTableGrouper.CreateImageTable(model, ObjectType, imageList); - - return new LocoObject(ObjectType, model, stringTable, imageTable); + return new LocoObject(ObjectType, model, stringTable); } } diff --git a/Definitions/ObjectModels/ImageTableGrouper.cs b/Definitions/ObjectModels/ImageTableGrouper.cs index 552e7eb4..5ca8e021 100644 --- a/Definitions/ObjectModels/ImageTableGrouper.cs +++ b/Definitions/ObjectModels/ImageTableGrouper.cs @@ -1889,7 +1889,7 @@ public static ImageTable CreateImageTable(ILocoStruct obj, ObjectType objectType CreateCliffEdgeGroups(imageList, imageTable); break; case ObjectType.Water: - imageTable.Groups.Add(("", imageList.ToList())); + CreateWaterGroups(imageList, imageTable); break; case ObjectType.Land: imageTable.Groups.Add(("", imageList.ToList())); @@ -1910,7 +1910,7 @@ public static ImageTable CreateImageTable(ILocoStruct obj, ObjectType objectType imageTable.Groups.Add(("", imageList.ToList())); break; case ObjectType.StreetLight: - imageTable.Groups.Add(("", imageList.ToList())); + CreateStreetLightGroups(imageList, imageTable); break; case ObjectType.Tunnel: imageTable.Groups.Add(("", imageList.ToList())); @@ -1946,7 +1946,7 @@ public static ImageTable CreateImageTable(ILocoStruct obj, ObjectType objectType imageTable.Groups.Add(("", imageList.ToList())); break; case ObjectType.Tree: - imageTable.Groups.Add(("", imageList.ToList())); + CreateTreeGroups(imageList, imageTable); break; case ObjectType.Snow: imageTable.Groups.Add(("", imageList.ToList())); @@ -2097,4 +2097,28 @@ private static void CreateScaffoldingGroups(List imageList, Ima imageTable.Groups.Add(("type 1", imageList[10..24])); imageTable.Groups.Add(("type 2", imageList[24..36])); } + + private static void CreateStreetLightGroups(List imageList, ImageTable imageTable) + => imageTable.Groups.AddRange(imageList + .Chunk(4) + .Select((x, i) => ($"Year group {i}", x.ToList())) + .ToList()); + + private static void CreateTreeGroups(List imageList, ImageTable imageTable) + => imageTable.Groups.AddRange(imageList + .Chunk(4) + .Select((x, i) => ($"Variation {i}", x.ToList())) + .ToList()); + + private static void CreateWaterGroups(List imageList, ImageTable imageTable) + { + imageTable.Groups.Add(("zoom 1", imageList[0..10])); + imageTable.Groups.Add(("zoom 2", imageList[10..20])); + imageTable.Groups.Add(("zoom 3", imageList[20..30])); + imageTable.Groups.Add(("zoom 4", imageList[30..40])); + imageTable.Groups.Add(("palettes", imageList[40..42])); + imageTable.Groups.Add(("icon-animation", imageList[42..58])); + imageTable.Groups.Add(("icon-interaction", imageList[58..60])); + imageTable.Groups.Add(("animation", imageList[60..76])); + } } From 0a8179e487ad4a288b640745448a10f833739c64 Mon Sep 17 00:00:00 2001 From: Benjamin Sutas Date: Wed, 17 Sep 2025 23:40:31 +1000 Subject: [PATCH 18/21] fixing unit test failures --- Dat/Loaders/ClimateObjectLoader.cs | 2 +- Dat/Loaders/RegionObjectLoader.cs | 6 ++-- Dat/Loaders/ScenarioTextObjectLoader.cs | 2 +- Definitions/ObjectModels/ImageTableGrouper.cs | 14 +++++---- .../ObjectModels/Types/GraphicsElement.cs | 7 +++++ Tests/IdempotenceTests.cs | 31 ++++++++++--------- Tests/LoadSaveTests.cs | 9 +++--- 7 files changed, 43 insertions(+), 28 deletions(-) diff --git a/Dat/Loaders/ClimateObjectLoader.cs b/Dat/Loaders/ClimateObjectLoader.cs index 32450bad..92ccc8df 100644 --- a/Dat/Loaders/ClimateObjectLoader.cs +++ b/Dat/Loaders/ClimateObjectLoader.cs @@ -80,7 +80,7 @@ public static void Save(Stream stream, LocoObject obj) // N/A // image table - SawyerStreamWriter.WriteImageTable(stream, obj.ImageTable.GraphicsElements); + SawyerStreamWriter.WriteImageTable(stream, new ImageTable().GraphicsElements); } } } diff --git a/Dat/Loaders/RegionObjectLoader.cs b/Dat/Loaders/RegionObjectLoader.cs index 391b41ed..f24e82c6 100644 --- a/Dat/Loaders/RegionObjectLoader.cs +++ b/Dat/Loaders/RegionObjectLoader.cs @@ -55,9 +55,11 @@ public static LocoObject Load(Stream stream) // image table // N/A but Region has an empty image table for some reason - _ = SawyerStreamReader.ReadImageTable(br).Table; + var imageList = SawyerStreamReader.ReadImageTable(br).Table; - return new LocoObject(ObjectType, model, stringTable); + var imageTable = ImageTableGrouper.CreateImageTable(model, ObjectType, imageList); + + return new LocoObject(ObjectType, model, stringTable, imageTable); } } diff --git a/Dat/Loaders/ScenarioTextObjectLoader.cs b/Dat/Loaders/ScenarioTextObjectLoader.cs index 0df0cc36..8ce507bc 100644 --- a/Dat/Loaders/ScenarioTextObjectLoader.cs +++ b/Dat/Loaders/ScenarioTextObjectLoader.cs @@ -69,7 +69,7 @@ public static void Save(Stream stream, LocoObject obj) // N/A // image table - SawyerStreamWriter.WriteImageTable(stream, obj.ImageTable.GraphicsElements); + SawyerStreamWriter.WriteImageTable(stream, new ImageTable().GraphicsElements); } } } diff --git a/Definitions/ObjectModels/ImageTableGrouper.cs b/Definitions/ObjectModels/ImageTableGrouper.cs index 5ca8e021..f1fb3a4a 100644 --- a/Definitions/ObjectModels/ImageTableGrouper.cs +++ b/Definitions/ObjectModels/ImageTableGrouper.cs @@ -14,6 +14,7 @@ using Definitions.ObjectModels.Objects.Wall; using Definitions.ObjectModels.Objects.Water; using Definitions.ObjectModels.Types; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; namespace Definitions.ObjectModels; @@ -1867,6 +1868,8 @@ public static class ImageTableGrouper { public static ImageTable CreateImageTable(ILocoStruct obj, ObjectType objectType, List imageList) { + var originalCount = imageList.Count; + ImageTableNamer.NameImages(obj, objectType, imageList); var imageTable = new ImageTable(); @@ -1960,9 +1963,6 @@ public static ImageTable CreateImageTable(ILocoStruct obj, ObjectType objectType case ObjectType.Building: CreateBuildingGroups(imageList, imageTable); break; - case ObjectType.Scaffolding: - CreateScaffoldingGroups(imageList, imageTable); - break; case ObjectType.Industry: CreateBuildingGroups(imageList, imageTable); break; @@ -1979,6 +1979,8 @@ public static ImageTable CreateImageTable(ILocoStruct obj, ObjectType objectType break; } + Debug.Assert(imageTable.GraphicsElements.Count == originalCount, "Image grouping lost or gained images"); + return imageTable; } @@ -1996,8 +1998,8 @@ private static void CreateAirportGroups(List imageList, ImageTa private static void CreateBridgeGroups(List imageList, ImageTable imageTable) { imageTable.Groups.Add(("preview", imageList[0..1])); - imageTable.Groups.Add(("base plates", imageList[1..5])); - imageTable.Groups.Add(("unk", imageList[6..11])); + imageTable.Groups.Add(("base plates", imageList[1..6])); + imageTable.Groups.Add(("unk", imageList[6..12])); imageTable.Groups.Add(("", imageList[12..])); } @@ -2052,7 +2054,7 @@ private static void CreateInterfaceGroups(List imageList, Image imageTable.Groups.Add(("toolbar", imageList[1..31])); imageTable.Groups.Add(("build-vehicle", imageList[31..43])); imageTable.Groups.Add(("toolbar", imageList[43..49])); - imageTable.Groups.Add(("paint", imageList[49..55])); + imageTable.Groups.Add(("paint", imageList[49..57])); imageTable.Groups.Add(("population", imageList[57..65])); imageTable.Groups.Add(("performance-index", imageList[65..73])); imageTable.Groups.Add(("cargo-units", imageList[73..81])); diff --git a/Definitions/ObjectModels/Types/GraphicsElement.cs b/Definitions/ObjectModels/Types/GraphicsElement.cs index db000928..44c7633d 100644 --- a/Definitions/ObjectModels/Types/GraphicsElement.cs +++ b/Definitions/ObjectModels/Types/GraphicsElement.cs @@ -1,5 +1,6 @@ using SixLabors.ImageSharp; using SixLabors.ImageSharp.PixelFormats; +using System.Text.Json.Serialization; namespace Definitions.ObjectModels.Types; @@ -26,7 +27,13 @@ public class GraphicsElement // follows G1Element32, except XOffset and YOffset public short ZoomOffset { get; set; } public byte[] ImageData { get; set; } = []; + [JsonIgnore] public Image? Image { get; set; } + // todo: use indexed image in SixLabors + //[JsonIgnore] + //public IndexedImageFrame? Image { get; set; } + + [JsonIgnore] // this is calculated based on object type, its not really a part of the object definition itself public string Name { get; set; } // taken from IImageNameProvider } diff --git a/Tests/IdempotenceTests.cs b/Tests/IdempotenceTests.cs index 5978d176..c6723b80 100644 --- a/Tests/IdempotenceTests.cs +++ b/Tests/IdempotenceTests.cs @@ -65,23 +65,26 @@ public void LoadSaveLoad(string filename) Assert.That(JsonSerializer.Serialize((object)actual.Object), Is.EqualTo(JsonSerializer.Serialize((object)expected.Object))); Assert.That(JsonSerializer.Serialize(actual.StringTable), Is.EqualTo(JsonSerializer.Serialize(expected.StringTable))); - var pm = new PaletteMap("C:\\Users\\bigba\\source\\repos\\OpenLoco\\ObjectEditor\\Gui\\Assets\\palette.png"); - var i = 0; - foreach (var ae in actual.ImageTable.GraphicsElements.Zip(expected.ImageTable.GraphicsElements)) + if (actual.ImageTable != null && expected.ImageTable != null) { - var ac = JsonSerializer.Serialize(ae.First); - var ex = JsonSerializer.Serialize(ae.Second); - - if (ac != ex) + var pm = new PaletteMap("C:\\Users\\bigba\\source\\repos\\OpenLoco\\ObjectEditor\\Gui\\Assets\\palette.png"); + var i = 0; + foreach (var ae in actual.ImageTable.GraphicsElements.Zip(expected.ImageTable.GraphicsElements)) { - _ = pm.TryConvertG1ToRgba32Bitmap(ae.First, ColourRemapSwatch.PrimaryRemap, ColourRemapSwatch.SecondaryRemap, out var img1); - _ = pm.TryConvertG1ToRgba32Bitmap(ae.Second, ColourRemapSwatch.PrimaryRemap, ColourRemapSwatch.SecondaryRemap, out var img2); - img1.SaveAsBmp($"{Path.GetFileName(filename)}-{i}-actual.bmp"); - img2.SaveAsBmp($"{Path.GetFileName(filename)}-{i}-expected.bmp"); + var ac = JsonSerializer.Serialize(ae.First); + var ex = JsonSerializer.Serialize(ae.Second); + + if (ac != ex) + { + _ = pm.TryConvertG1ToRgba32Bitmap(ae.First, ColourRemapSwatch.PrimaryRemap, ColourRemapSwatch.SecondaryRemap, out var img1); + _ = pm.TryConvertG1ToRgba32Bitmap(ae.Second, ColourRemapSwatch.PrimaryRemap, ColourRemapSwatch.SecondaryRemap, out var img2); + img1.SaveAsBmp($"{Path.GetFileName(filename)}-{i}-actual.bmp"); + img2.SaveAsBmp($"{Path.GetFileName(filename)}-{i}-expected.bmp"); + } + + Assert.That(ac, Is.EqualTo(ex)); + i++; } - - Assert.That(ac, Is.EqualTo(ex)); - i++; } } } diff --git a/Tests/LoadSaveTests.cs b/Tests/LoadSaveTests.cs index 992dc95c..13cedb57 100644 --- a/Tests/LoadSaveTests.cs +++ b/Tests/LoadSaveTests.cs @@ -287,7 +287,7 @@ void assertFunc(LocoObject obj, ClimateObject struc) => Assert.Multiple(() => Assert.That(struc.WinterSnowLine, Is.EqualTo(48), nameof(struc.WinterSnowLine)); Assert.That(struc.SummerSnowLine, Is.EqualTo(76), nameof(struc.SummerSnowLine)); - Assert.That(obj.ImageTable.GraphicsElements, Is.Empty); + Assert.That(obj.ImageTable, Is.Null); }); LoadSaveGenericTest(objectName, assertFunc); } @@ -806,7 +806,8 @@ void assertFunc(LocoObject obj, ScaffoldingObject struc) => Assert.Multiple(() = [TestCase("STEX000.DAT")] public void ScenarioTextObject(string objectName) { - void assertFunc(LocoObject obj, ScenarioTextObject struc) => Assert.Multiple(() => Assert.That(obj.ImageTable.GraphicsElements, Is.Empty)); + void assertFunc(LocoObject obj, ScenarioTextObject struc) + => Assert.Multiple(() => Assert.That(obj.ImageTable, Is.Null)); LoadSaveGenericTest(objectName, assertFunc); } @@ -845,7 +846,7 @@ void assertFunc(LocoObject obj, SoundObject struc) => Assert.Multiple(() => Assert.That(struc.SoundObjectData.PcmHeader.SampleRate, Is.EqualTo(22050), nameof(struc.SoundObjectData.PcmHeader.SampleRate)); Assert.That(struc.SoundObjectData.PcmHeader.WaveFormatTag, Is.EqualTo(1), nameof(struc.SoundObjectData.PcmHeader.WaveFormatTag)); - Assert.That(obj.ImageTable.GraphicsElements, Is.Empty); + Assert.That(obj.ImageTable, Is.Null); }); LoadSaveGenericTest(objectName, assertFunc); } @@ -926,7 +927,7 @@ void assertFunc(LocoObject obj, TownNamesObject struc) => Assert.Multiple(() => Assert.That(obj.StringTable["Name"][LanguageId.English_UK], Is.EqualTo("North-American style town names")); Assert.That(obj.StringTable["Name"][LanguageId.English_US], Is.EqualTo("North-American style town names")); - Assert.That(obj.ImageTable.GraphicsElements, Is.Empty); + Assert.That(obj.ImageTable, Is.Null); }); LoadSaveGenericTest(objectName, assertFunc); } From c127f40e57d31e6edc78b75e0933b545e82abc3b Mon Sep 17 00:00:00 2001 From: Benjamin Sutas Date: Thu, 18 Sep 2025 12:38:48 +1000 Subject: [PATCH 19/21] add proper validation --- Dat/FileParsing/SawyerStreamReader.cs | 5 +- Dat/FileParsing/SawyerStreamWriter.cs | 6 +- Dat/Loaders/SoundObjectLoader.cs | 12 ++- Dat/Loaders/TownNamesObjectLoader.cs | 24 +---- Dat/Types/Audio/DatMusicWaveFormat.cs | 17 ++-- Dat/Types/Audio/DatSoundEffectWaveFormat.cs | 5 +- Dat/Types/DatG1Element32.cs | 4 +- Dat/Types/G1Header.cs | 4 +- Dat/Types/SCV5/Animation.cs | 5 +- Dat/Types/SCV5/Company.cs | 4 +- Dat/Types/SCV5/Entity.cs | 5 +- Dat/Types/SCV5/GameState.cs | 13 ++- Dat/Types/SCV5/Industry.cs | 4 +- Dat/Types/SCV5/Message.cs | 4 +- Dat/Types/SCV5/S5File.cs | 5 +- Dat/Types/SCV5/S5FileHeader.cs | 5 +- Dat/Types/SCV5/SaveDetails.cs | 5 +- Dat/Types/SCV5/ScenarioObjective.cs | 4 +- Dat/Types/SCV5/ScenarioOptions.cs | 5 +- Dat/Types/SCV5/Station.cs | 4 +- Dat/Types/SCV5/Town.cs | 4 +- Dat/Types/SCV5/Wave.cs | 4 +- Dat/Types/Typedefs.cs | 7 +- Definitions/ObjectModels/ILocoStruct.cs | 4 +- Definitions/ObjectModels/ImageTableGrouper.cs | 3 + .../Objects/Airport/AirportBuilding.cs | 4 +- .../Objects/Airport/AirportObject.cs | 18 ++-- .../Objects/Airport/MovementEdge.cs | 4 +- .../Objects/Airport/MovementNode.cs | 4 +- .../Objects/Bridge/BridgeObject.cs | 33 ++++--- .../Objects/Building/BuildingObject.cs | 17 +++- .../ObjectModels/Objects/Cargo/CargoObject.cs | 16 +++- .../Objects/CliffEdge/CliffEdgeObject.cs | 5 +- .../Objects/Climate/ClimateObject.cs | 17 +++- .../Objects/Common/BuildingComponentsModel.cs | 31 +++++-- .../Objects/Competitor/CompetitorObject.cs | 16 ++-- .../Objects/Currency/CurrencyObject.cs | 10 +-- .../ObjectModels/Objects/Dock/DockObject.cs | 17 ++-- .../Objects/HillShapes/HillShapesObject.cs | 5 +- .../Objects/Industry/IndustryObject.cs | 37 ++++---- .../InterfaceSkin/InterfaceSkinObject.cs | 4 +- .../ObjectModels/Objects/Land/LandObject.cs | 16 ++-- .../LevelCrossing/LevelCrossingObject.cs | 15 ++-- .../Objects/Region/RegionObject.cs | 5 +- .../ObjectModels/Objects/Road/RoadObject.cs | 22 +++-- .../Objects/RoadExtra/RoadExtraObject.cs | 15 ++-- .../Objects/RoadStation/RoadStationObject.cs | 17 ++-- .../Objects/Scaffolding/ScaffoldingObject.cs | 4 +- .../ScenarioText/ScenarioTextObject.cs | 5 +- .../ObjectModels/Objects/Snow/SnowObject.cs | 4 +- .../ObjectModels/Objects/Sound/SoundObject.cs | 10 ++- .../ObjectModels/Objects/Steam/SteamObject.cs | 5 +- .../Objects/StreetLight/StreetLightObject.cs | 6 +- .../Objects/TownNames/TownNamesObject.cs | 5 +- .../ObjectModels/Objects/Track/TrackObject.cs | 25 +++--- .../Objects/TrackExtra/TrackExtraObject.cs | 14 +-- .../Objects/TrackSignal/TrackSignalObject.cs | 33 +++---- .../TrackStation/TrackStationObject.cs | 16 ++-- .../ObjectModels/Objects/Tree/TreeObject.cs | 26 +++--- .../Objects/Tunnel/TunnelObject.cs | 4 +- .../Objects/Vehicle/BodySprite.cs | 32 ++++++- .../Objects/Vehicle/BogieSprite.cs | 12 ++- .../Objects/Vehicle/SimpleAnimation.cs | 4 +- .../Objects/Vehicle/VehicleObject.cs | 90 ++++--------------- .../Objects/Vehicle/VehicleObjectCar.cs | 4 +- .../ObjectModels/Objects/Wall/WallObject.cs | 6 +- .../ObjectModels/Objects/Water/WaterObject.cs | 10 +-- .../LocoTypes/LocoObjectViewModel.cs | 3 +- .../Objects/Building/BuildingViewModel.cs | 26 +++--- Tests/G1Tests.cs | 28 +++--- 70 files changed, 495 insertions(+), 367 deletions(-) diff --git a/Dat/FileParsing/SawyerStreamReader.cs b/Dat/FileParsing/SawyerStreamReader.cs index a0504a5c..d69926b7 100644 --- a/Dat/FileParsing/SawyerStreamReader.cs +++ b/Dat/FileParsing/SawyerStreamReader.cs @@ -7,6 +7,7 @@ using Definitions.ObjectModels; using Definitions.ObjectModels.Objects.Sound; using Definitions.ObjectModels.Types; +using System.ComponentModel.DataAnnotations; using System.Text; namespace Dat.FileParsing; @@ -137,9 +138,9 @@ static void ValidateLocoStruct(S5Header s5Header, ILocoStruct locoStruct, ILogge } } - if (!locoStruct.Validate()) + foreach (var failedValidation in locoStruct.Validate(new ValidationContext(locoStruct))) { - warnings.Add($"\"{s5Header.Name}\" failed validation"); + warnings.Add($"\"{s5Header.Name}\" failed validation: {failedValidation}"); } if (warnings.Count != 0) diff --git a/Dat/FileParsing/SawyerStreamWriter.cs b/Dat/FileParsing/SawyerStreamWriter.cs index 7cf637ae..ab010809 100644 --- a/Dat/FileParsing/SawyerStreamWriter.cs +++ b/Dat/FileParsing/SawyerStreamWriter.cs @@ -7,6 +7,7 @@ using Definitions.ObjectModels; using Definitions.ObjectModels.Objects.Sound; using Definitions.ObjectModels.Types; +using System.ComponentModel.DataAnnotations; using System.Text; namespace Dat.FileParsing; @@ -490,9 +491,10 @@ public static void WriteLocoObject(Stream stream, LocoObject obj) public static MemoryStream WriteLocoObject(string objName, ObjectType objectType, ObjectSource objectSource, SawyerEncoding encoding, ILogger logger, LocoObject obj, bool allowWritingAsVanilla) { - if (!obj.Object.Validate()) + var validationResults = new List(); + if (!Validator.TryValidateObject(obj.Object, new ValidationContext(obj.Object), validationResults)) { - throw new ArgumentException($"{objName} was invalid", nameof(obj)); + throw new ArgumentException($"{objName} was invalid: {string.Join(", ", validationResults.Select(r => r.ErrorMessage))}", nameof(obj)); } using var rawObjStream = new MemoryStream(); diff --git a/Dat/Loaders/SoundObjectLoader.cs b/Dat/Loaders/SoundObjectLoader.cs index b9b689e7..9e2f7d37 100644 --- a/Dat/Loaders/SoundObjectLoader.cs +++ b/Dat/Loaders/SoundObjectLoader.cs @@ -133,8 +133,13 @@ internal record DatSoundObjectData( public DatSoundObjectData() : this(0, 0, 0, new DatSoundEffectWaveFormat()) { } - public bool Validate() - => Offset >= 0; + public IEnumerable Validate(ValidationContext validationContext) + { + if (Length > 0 && Offset < 0) + { + yield return new ValidationResult("If Length is greater than 0, Offset must be non-negative.", [nameof(Offset), nameof(Length)]); + } + } } [LocoStructSize(0x0C)] @@ -196,5 +201,6 @@ public ReadOnlySpan SaveVariable() } } - public bool Validate() => true; + public IEnumerable Validate(ValidationContext validationContext) + => []; } diff --git a/Dat/Loaders/TownNamesObjectLoader.cs b/Dat/Loaders/TownNamesObjectLoader.cs index df26ee79..004986e7 100644 --- a/Dat/Loaders/TownNamesObjectLoader.cs +++ b/Dat/Loaders/TownNamesObjectLoader.cs @@ -5,6 +5,7 @@ using Definitions.ObjectModels.Objects.TownNames; using Definitions.ObjectModels.Types; using System.ComponentModel; +using System.ComponentModel.DataAnnotations; namespace Dat.Loaders; @@ -83,26 +84,3 @@ public static void Save(Stream stream, LocoObject obj) } } - -[LocoStructSize(0x1A)] -[LocoStructType(DatObjectType.TownNames)] -internal record DatTownNamesObject( - [property: LocoStructOffset(0x00), LocoString, Browsable(false)] string_id Name, - [property: LocoStructOffset(0x02), LocoArrayLength(6)] Category[] Categories -) : ILocoStruct, ILocoStructVariableData -{ - byte[] tempUnkVariableData; - - public ReadOnlySpan LoadVariable(ReadOnlySpan remainingData) - { - // town names is interesting - loco has not RE'd the whole object and there are no graphics, so we just - // skip the rest of the data/object - tempUnkVariableData = remainingData.ToArray(); - return remainingData[remainingData.Length..]; - } - - public ReadOnlySpan SaveVariable() - => tempUnkVariableData; - - public bool Validate() => true; -} diff --git a/Dat/Types/Audio/DatMusicWaveFormat.cs b/Dat/Types/Audio/DatMusicWaveFormat.cs index 3e73c419..d235ba7a 100644 --- a/Dat/Types/Audio/DatMusicWaveFormat.cs +++ b/Dat/Types/Audio/DatMusicWaveFormat.cs @@ -1,5 +1,6 @@ using Dat.FileParsing; using Definitions.ObjectModels; +using System.ComponentModel.DataAnnotations; namespace Dat.Types.Audio; @@ -47,38 +48,36 @@ public ReadOnlySpan Write() return ((MemoryStream)bs.BaseStream).ToArray(); } - public bool Validate() + public IEnumerable Validate(ValidationContext validationContext) { if (Signature != 0x46464952) // "RIFF" { - return false; + yield return new ValidationResult("Signature must be 'RIFF'", [nameof(Signature)]); } if (RiffType != 0x45564157) // "WAVE" { - return false; + yield return new ValidationResult("RiffType must be 'WAVE'", [nameof(RiffType)]); } if (FormatMarker is not 0x20746d66 and not 0x00746d66) // "fmt\0" or "fmt" { - return false; + yield return new ValidationResult("FormatMarker must be 'fmt ' or 'fmt\\0'", [nameof(FormatMarker)]); } if (FormatType != 1) // expected PCM { - return false; + yield return new ValidationResult("FormatType must be 1 (PCM)", [nameof(FormatType)]); } if (BitsPerSample != 16) { - return false; + yield return new ValidationResult("BitsPerSample must be 16", [nameof(BitsPerSample)]); } if (DataMarker != 0x61746164) { - return false; + yield return new ValidationResult("DataMarker must be 'data'", [nameof(DataMarker)]); } - - return true; } } diff --git a/Dat/Types/Audio/DatSoundEffectWaveFormat.cs b/Dat/Types/Audio/DatSoundEffectWaveFormat.cs index 36f79ca3..aa386249 100644 --- a/Dat/Types/Audio/DatSoundEffectWaveFormat.cs +++ b/Dat/Types/Audio/DatSoundEffectWaveFormat.cs @@ -1,6 +1,7 @@ using Dat.FileParsing; using Definitions.ObjectModels; using System.ComponentModel; +using System.ComponentModel.DataAnnotations; namespace Dat.Types.Audio; @@ -35,6 +36,6 @@ public ReadOnlySpan Write() return ((MemoryStream)bs.BaseStream).ToArray(); } - public bool Validate() - => true; + public IEnumerable Validate(ValidationContext validationContext) + => []; } diff --git a/Dat/Types/DatG1Element32.cs b/Dat/Types/DatG1Element32.cs index a38f8ee7..6804461b 100644 --- a/Dat/Types/DatG1Element32.cs +++ b/Dat/Types/DatG1Element32.cs @@ -1,6 +1,7 @@ using Dat.FileParsing; using Definitions.ObjectModels; using System.ComponentModel; +using System.ComponentModel.DataAnnotations; namespace Dat.Types; @@ -67,5 +68,6 @@ public static byte[] GetImageDataForSave(DatG1ElementFlags flags, byte[] imageDa ? SawyerStreamWriter.EncodeRLEImageData(flags, imageData, width, height) : imageData; - public bool Validate() => true; + public IEnumerable Validate(ValidationContext validationContext) + => []; } diff --git a/Dat/Types/G1Header.cs b/Dat/Types/G1Header.cs index e286e2a5..a2cc238b 100644 --- a/Dat/Types/G1Header.cs +++ b/Dat/Types/G1Header.cs @@ -1,5 +1,6 @@ using Dat.FileParsing; using Definitions.ObjectModels; +using System.ComponentModel.DataAnnotations; namespace Dat.Types; @@ -12,5 +13,6 @@ public record G1Header( public static int StructLength => 0x08; public byte[] ImageData = []; - public bool Validate() => true; + public IEnumerable Validate(ValidationContext validationContext) + => []; } diff --git a/Dat/Types/SCV5/Animation.cs b/Dat/Types/SCV5/Animation.cs index 23eb5a5f..400e0257 100644 --- a/Dat/Types/SCV5/Animation.cs +++ b/Dat/Types/SCV5/Animation.cs @@ -1,5 +1,6 @@ using Dat.FileParsing; using Definitions.ObjectModels; +using System.ComponentModel.DataAnnotations; namespace Dat.Types.SCV5; @@ -8,6 +9,6 @@ public class Animation : ILocoStruct { [LocoArrayLength(0x06)] public uint8_t[] var_0 { get; set; } = []; - public bool Validate() - => throw new NotImplementedException(); + public IEnumerable Validate(ValidationContext validationContext) + => []; } diff --git a/Dat/Types/SCV5/Company.cs b/Dat/Types/SCV5/Company.cs index 7d0a668a..95b540e2 100644 --- a/Dat/Types/SCV5/Company.cs +++ b/Dat/Types/SCV5/Company.cs @@ -1,5 +1,6 @@ using Dat.FileParsing; using Definitions.ObjectModels; +using System.ComponentModel.DataAnnotations; namespace Dat.Types.SCV5; @@ -17,5 +18,6 @@ public class Company : ILocoStruct public uint8_t ChallengeProgress { get; set; } // 0x8C4E [LocoArrayLength(0x8FA8 - 0x8C4F)] public uint8_t[] var_8C4F { get; set; } - public bool Validate() => true; + public IEnumerable Validate(ValidationContext validationContext) + => []; } diff --git a/Dat/Types/SCV5/Entity.cs b/Dat/Types/SCV5/Entity.cs index b917fe18..2fc93306 100644 --- a/Dat/Types/SCV5/Entity.cs +++ b/Dat/Types/SCV5/Entity.cs @@ -1,6 +1,7 @@ using Dat.FileParsing; using Definitions.ObjectModels; +using System.ComponentModel.DataAnnotations; namespace Dat.Types.SCV5; [LocoStructSize(0x80)] @@ -8,6 +9,6 @@ public class Entity : ILocoStruct { [LocoArrayLength(0x80)] public uint8_t[] var_0 { get; set; } = []; - public bool Validate() - => throw new NotImplementedException(); + public IEnumerable Validate(ValidationContext validationContext) + => []; } diff --git a/Dat/Types/SCV5/GameState.cs b/Dat/Types/SCV5/GameState.cs index d99d8945..44464ffe 100644 --- a/Dat/Types/SCV5/GameState.cs +++ b/Dat/Types/SCV5/GameState.cs @@ -2,6 +2,7 @@ using Dat.FileParsing; using Definitions.ObjectModels; using System.ComponentModel; +using System.ComponentModel.DataAnnotations; namespace Dat.Types.SCV5; @@ -149,7 +150,8 @@ public record GameStateScenarioA( { //public const int StructLength = 0x4A0644; - public bool Validate() => true; + public IEnumerable Validate(ValidationContext validationContext) + => []; } [TypeConverter(typeof(ExpandableObjectConverter))] @@ -162,7 +164,8 @@ public record GameStateScenarioB( //[property: LocoStructOffset(0x123480), LocoArrayLength((int)Limits.kMaxEntities)] Entity[] Entities // this isn't actually part of the data chunk in a scenario! ) : ILocoStruct { - public bool Validate() => true; + public IEnumerable Validate(ValidationContext validationContext) + => []; } [TypeConverter(typeof(ExpandableObjectConverter))] @@ -175,7 +178,8 @@ public record GameStateScenarioC( [property: LocoStructOffset(0x3B580), LocoArrayLength((int)Limits.kMaxWaves), Browsable(false)] uint8_t[] Orders ) : ILocoStruct { - public bool Validate() => true; + public IEnumerable Validate(ValidationContext validationContext) + => []; } [TypeConverter(typeof(ExpandableObjectConverter))] @@ -312,5 +316,6 @@ public record GameStateSave([property: LocoStructOffset(0x00), LocoArrayLength(2 ) : ILocoStruct, IGameState { - public bool Validate() => true; + public IEnumerable Validate(ValidationContext validationContext) + => []; } diff --git a/Dat/Types/SCV5/Industry.cs b/Dat/Types/SCV5/Industry.cs index 2d5c8636..ab0d4ed8 100644 --- a/Dat/Types/SCV5/Industry.cs +++ b/Dat/Types/SCV5/Industry.cs @@ -1,5 +1,6 @@ using Dat.FileParsing; using Definitions.ObjectModels; +using System.ComponentModel.DataAnnotations; namespace Dat.Types.SCV5; @@ -8,5 +9,6 @@ public class Industry : ILocoStruct { [LocoArrayLength(0x453)] public uint8_t[] var_0 { get; set; } - public bool Validate() => throw new NotImplementedException(); + public IEnumerable Validate(ValidationContext validationContext) + => []; } diff --git a/Dat/Types/SCV5/Message.cs b/Dat/Types/SCV5/Message.cs index c459b9e4..b4a11e3c 100644 --- a/Dat/Types/SCV5/Message.cs +++ b/Dat/Types/SCV5/Message.cs @@ -1,6 +1,7 @@ using Dat.FileParsing; using Definitions.ObjectModels; using System.ComponentModel; +using System.ComponentModel.DataAnnotations; namespace Dat.Types.SCV5; @@ -10,5 +11,6 @@ public class Message : ILocoStruct { [LocoArrayLength(0xD4)] public uint8_t[] var_0 { get; set; } - public bool Validate() => throw new NotImplementedException(); + public IEnumerable Validate(ValidationContext validationContext) + => []; } diff --git a/Dat/Types/SCV5/S5File.cs b/Dat/Types/SCV5/S5File.cs index 93a8d0cf..7015cc99 100644 --- a/Dat/Types/SCV5/S5File.cs +++ b/Dat/Types/SCV5/S5File.cs @@ -2,6 +2,7 @@ using Dat.FileParsing; using Definitions.ObjectModels; using System.ComponentModel; +using System.ComponentModel.DataAnnotations; namespace Dat.Types.SCV5; @@ -96,7 +97,6 @@ uint32_t Checksum ) : ILocoStruct { - public bool Validate() => true; public const int StructLength = 0x20; public const int RequiredObjectsCount = 859; @@ -104,6 +104,9 @@ uint32_t Checksum public List[,]? TileElementMap { get; set; } byte[] OriginalTileElementData { get; set; } = []; + public IEnumerable Validate(ValidationContext validationContext) + => []; + public byte[] Write() { var hdr = SawyerStreamWriter.WriteChunk(Header, SawyerEncoding.Rotate); diff --git a/Dat/Types/SCV5/S5FileHeader.cs b/Dat/Types/SCV5/S5FileHeader.cs index 53d64008..938d5039 100644 --- a/Dat/Types/SCV5/S5FileHeader.cs +++ b/Dat/Types/SCV5/S5FileHeader.cs @@ -1,6 +1,7 @@ using Dat.FileParsing; using Definitions.ObjectModels; using System.ComponentModel; +using System.ComponentModel.DataAnnotations; namespace Dat.Types.SCV5; @@ -16,5 +17,7 @@ public record S5FileHeader( : ILocoStruct { public const int StructLength = 0x20; - public bool Validate() => true; + + public IEnumerable Validate(ValidationContext validationContext) + => []; } diff --git a/Dat/Types/SCV5/SaveDetails.cs b/Dat/Types/SCV5/SaveDetails.cs index 0021f702..9fdfa533 100644 --- a/Dat/Types/SCV5/SaveDetails.cs +++ b/Dat/Types/SCV5/SaveDetails.cs @@ -1,6 +1,7 @@ using Dat.FileParsing; using Definitions.ObjectModels; using System.ComponentModel; +using System.ComponentModel.DataAnnotations; namespace Dat.Types.SCV5; @@ -20,5 +21,7 @@ public record SaveDetails( : ILocoStruct { public const int StructLength = 0xC618; - public bool Validate() => true; + + public IEnumerable Validate(ValidationContext validationContext) + => []; } diff --git a/Dat/Types/SCV5/ScenarioObjective.cs b/Dat/Types/SCV5/ScenarioObjective.cs index 72d36e71..0398f9b3 100644 --- a/Dat/Types/SCV5/ScenarioObjective.cs +++ b/Dat/Types/SCV5/ScenarioObjective.cs @@ -1,6 +1,7 @@ using Dat.FileParsing; using Definitions.ObjectModels; using System.ComponentModel; +using System.ComponentModel.DataAnnotations; namespace Dat.Types.SCV5; @@ -17,5 +18,6 @@ public record ScenarioObjective( [property: LocoStructOffset(0x10)] uint8_t TimeLimitYears) : ILocoStruct { - public bool Validate() => true; + public IEnumerable Validate(ValidationContext validationContext) + => []; } diff --git a/Dat/Types/SCV5/ScenarioOptions.cs b/Dat/Types/SCV5/ScenarioOptions.cs index cea34edb..7f58ff61 100644 --- a/Dat/Types/SCV5/ScenarioOptions.cs +++ b/Dat/Types/SCV5/ScenarioOptions.cs @@ -1,6 +1,7 @@ using Dat.FileParsing; using Definitions.ObjectModels; using System.ComponentModel; +using System.ComponentModel.DataAnnotations; namespace Dat.Types.SCV5; @@ -50,5 +51,7 @@ public record ScenarioOptions( : ILocoStruct { public const int StructLength = 0x431A; - public bool Validate() => true; + + public IEnumerable Validate(ValidationContext validationContext) + => []; } diff --git a/Dat/Types/SCV5/Station.cs b/Dat/Types/SCV5/Station.cs index e84f1f9b..00b63d1b 100644 --- a/Dat/Types/SCV5/Station.cs +++ b/Dat/Types/SCV5/Station.cs @@ -1,5 +1,6 @@ using Dat.FileParsing; using Definitions.ObjectModels; +using System.ComponentModel.DataAnnotations; namespace Dat.Types.SCV5; @@ -8,5 +9,6 @@ public class Station : ILocoStruct { [LocoArrayLength(0x3D2)] public uint8_t[] var_0 { get; set; } - public bool Validate() => throw new NotImplementedException(); + public IEnumerable Validate(ValidationContext validationContext) + => []; } diff --git a/Dat/Types/SCV5/Town.cs b/Dat/Types/SCV5/Town.cs index 8b492aae..5101ac78 100644 --- a/Dat/Types/SCV5/Town.cs +++ b/Dat/Types/SCV5/Town.cs @@ -1,5 +1,6 @@ using Dat.FileParsing; using Definitions.ObjectModels; +using System.ComponentModel.DataAnnotations; namespace Dat.Types.SCV5; @@ -8,5 +9,6 @@ public class Town : ILocoStruct { [LocoArrayLength(0x270)] public uint8_t[] var_0 { get; set; } - public bool Validate() => throw new NotImplementedException(); + public IEnumerable Validate(ValidationContext validationContext) + => []; } diff --git a/Dat/Types/SCV5/Wave.cs b/Dat/Types/SCV5/Wave.cs index be0d10d4..6fd53976 100644 --- a/Dat/Types/SCV5/Wave.cs +++ b/Dat/Types/SCV5/Wave.cs @@ -1,5 +1,6 @@ using Dat.FileParsing; using Definitions.ObjectModels; +using System.ComponentModel.DataAnnotations; namespace Dat.Types.SCV5; @@ -8,5 +9,6 @@ public class Wave : ILocoStruct { [LocoArrayLength(0x06)] public uint8_t[] var_0 { get; set; } - public bool Validate() => throw new NotImplementedException(); + public IEnumerable Validate(ValidationContext validationContext) + => []; } diff --git a/Dat/Types/Typedefs.cs b/Dat/Types/Typedefs.cs index 1a397169..97febc31 100644 --- a/Dat/Types/Typedefs.cs +++ b/Dat/Types/Typedefs.cs @@ -1,5 +1,6 @@ using Dat.FileParsing; using Definitions.ObjectModels; +using System.ComponentModel.DataAnnotations; namespace Dat.Types; @@ -9,7 +10,8 @@ public record DatPos2( [property: LocoStructOffset(0x02)] coord_t Y = 0 ) : ILocoStruct { - public bool Validate() => true; + public IEnumerable Validate(ValidationContext validationContext) + => []; } [LocoStructSize(0x06)] @@ -19,5 +21,6 @@ public record DatPos3( [property: LocoStructOffset(0x04)] coord_t Z = 0 ) : ILocoStruct { - public bool Validate() => true; + public IEnumerable Validate(ValidationContext validationContext) + => []; } diff --git a/Definitions/ObjectModels/ILocoStruct.cs b/Definitions/ObjectModels/ILocoStruct.cs index ba8bf38d..f0f7f18d 100644 --- a/Definitions/ObjectModels/ILocoStruct.cs +++ b/Definitions/ObjectModels/ILocoStruct.cs @@ -1,6 +1,8 @@ +using System.ComponentModel.DataAnnotations; + namespace Definitions.ObjectModels; public interface ILocoStruct { - bool Validate(); + IEnumerable Validate(ValidationContext validationContext); } diff --git a/Definitions/ObjectModels/ImageTableGrouper.cs b/Definitions/ObjectModels/ImageTableGrouper.cs index f1fb3a4a..73eb0994 100644 --- a/Definitions/ObjectModels/ImageTableGrouper.cs +++ b/Definitions/ObjectModels/ImageTableGrouper.cs @@ -1975,6 +1975,9 @@ public static ImageTable CreateImageTable(ILocoStruct obj, ObjectType objectType case ObjectType.ScenarioText: imageTable.Groups.Add(("", imageList.ToList())); break; + case ObjectType.Scaffolding: + CreateScaffoldingGroups(imageList, imageTable); + break; default: break; } diff --git a/Definitions/ObjectModels/Objects/Airport/AirportBuilding.cs b/Definitions/ObjectModels/Objects/Airport/AirportBuilding.cs index 5293e40f..95cf5f0c 100644 --- a/Definitions/ObjectModels/Objects/Airport/AirportBuilding.cs +++ b/Definitions/ObjectModels/Objects/Airport/AirportBuilding.cs @@ -1,4 +1,5 @@ using System.ComponentModel; +using System.ComponentModel.DataAnnotations; namespace Definitions.ObjectModels.Objects.Airport; @@ -10,5 +11,6 @@ public class AirportBuilding : ILocoStruct public int8_t X { get; set; } public int8_t Y { get; set; } - public bool Validate() => true; + public IEnumerable Validate(ValidationContext validationContext) + => []; } diff --git a/Definitions/ObjectModels/Objects/Airport/AirportObject.cs b/Definitions/ObjectModels/Objects/Airport/AirportObject.cs index db502f83..ebb60698 100644 --- a/Definitions/ObjectModels/Objects/Airport/AirportObject.cs +++ b/Definitions/ObjectModels/Objects/Airport/AirportObject.cs @@ -1,5 +1,5 @@ using Definitions.ObjectModels.Objects.Common; -using System.Diagnostics.CodeAnalysis; +using System.ComponentModel.DataAnnotations; namespace Definitions.ObjectModels.Objects.Airport; @@ -25,23 +25,27 @@ public class AirportObject : ILocoStruct, IHasBuildingComponents public List MovementEdges { get; set; } = []; public uint8_t[] var_B6 { get; set; } = []; - public bool Validate() + public IEnumerable Validate(ValidationContext validationContext) { - if (!BuildingComponents.Validate()) + var bcValidationContext = new ValidationContext(BuildingComponents); + foreach (var result in BuildingComponents.Validate(bcValidationContext)) { - return false; + yield return result; } if (CostIndex > 32) { - return false; + yield return new ValidationResult($"{nameof(CostIndex)} must be between 0 and 32.", [nameof(CostIndex)]); } if (-SellCostFactor > BuildCostFactor) { - return false; + yield return new ValidationResult($"-{nameof(SellCostFactor)} must be less than or equal to {nameof(BuildCostFactor)}.", [nameof(SellCostFactor), nameof(BuildCostFactor)]); } - return BuildCostFactor > 0; + if (BuildCostFactor <= 0) + { + yield return new ValidationResult($"{nameof(BuildCostFactor)} must be greater than 0.", [nameof(BuildCostFactor)]); + } } } diff --git a/Definitions/ObjectModels/Objects/Airport/MovementEdge.cs b/Definitions/ObjectModels/Objects/Airport/MovementEdge.cs index 5190db85..e18aba29 100644 --- a/Definitions/ObjectModels/Objects/Airport/MovementEdge.cs +++ b/Definitions/ObjectModels/Objects/Airport/MovementEdge.cs @@ -1,4 +1,5 @@ using System.ComponentModel; +using System.ComponentModel.DataAnnotations; namespace Definitions.ObjectModels.Objects.Airport; @@ -12,5 +13,6 @@ public class MovementEdge : ILocoStruct public uint32_t MustBeClearEdges { get; set; } // Which edges must be clear to use the transition edge. should probably be some kind of flags? public uint32_t AtLeastOneClearEdges { get; set; } // Which edges must have at least one clear to use transition edge. should probably be some kind of flags? - public bool Validate() => true; + public IEnumerable Validate(ValidationContext validationContext) + => []; } diff --git a/Definitions/ObjectModels/Objects/Airport/MovementNode.cs b/Definitions/ObjectModels/Objects/Airport/MovementNode.cs index f0f44fec..0a93fea6 100644 --- a/Definitions/ObjectModels/Objects/Airport/MovementNode.cs +++ b/Definitions/ObjectModels/Objects/Airport/MovementNode.cs @@ -1,5 +1,6 @@ using Definitions.ObjectModels.Types; using System.ComponentModel; +using System.ComponentModel.DataAnnotations; namespace Definitions.ObjectModels.Objects.Airport; @@ -9,5 +10,6 @@ public class MovementNode : ILocoStruct public Pos3 Position { get; set; } public AirportMovementNodeFlags Flags { get; set; } - public bool Validate() => true; + public IEnumerable Validate(ValidationContext validationContext) + => []; } diff --git a/Definitions/ObjectModels/Objects/Bridge/BridgeObject.cs b/Definitions/ObjectModels/Objects/Bridge/BridgeObject.cs index 7ea13849..732b7175 100644 --- a/Definitions/ObjectModels/Objects/Bridge/BridgeObject.cs +++ b/Definitions/ObjectModels/Objects/Bridge/BridgeObject.cs @@ -1,4 +1,5 @@ using Definitions.ObjectModels.Types; +using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; namespace Definitions.ObjectModels.Objects.Bridge; @@ -23,48 +24,46 @@ public class BridgeObject : ILocoStruct public List CompatibleTrackObjects { get; set; } = []; public List CompatibleRoadObjects { get; set; } = []; - public bool Validate() + public IEnumerable Validate(ValidationContext validationContext) { if (CostIndex > 32) { - return false; + yield return new ValidationResult($"{nameof(CostIndex)} must be between 0 and 32 inclusive.", [nameof(CostIndex)]); } if (-SellCostFactor > BaseCostFactor) { - return false; + yield return new ValidationResult($"The negative of {nameof(SellCostFactor)} must be less than or equal to {nameof(BaseCostFactor)}.", [nameof(SellCostFactor), nameof(BaseCostFactor)]); } if (BaseCostFactor <= 0) { - return false; + yield return new ValidationResult($"{nameof(BaseCostFactor)} must be positive.", [nameof(BaseCostFactor)]); } if (HeightCostFactor < 0) { - return false; + yield return new ValidationResult($"{nameof(HeightCostFactor)} must be non-negative.", [nameof(HeightCostFactor)]); } if (DeckDepth is not 16 and not 32) { - return false; + yield return new ValidationResult($"{nameof(DeckDepth)} must be either 16 or 32.", [nameof(DeckDepth)]); } if (SpanLength is not 1 and not 2 and not 4) { - return false; + yield return new ValidationResult($"{nameof(SpanLength)} must be either 1, 2, or 4.", [nameof(SpanLength)]); } - //if (CompatibleTrackObjectCount > 7) - //{ - // return false; - //} - - //if (CompatibleRoadObjectCount > 7) - //{ - // return false; - //} + if (CompatibleTrackObjects.Count > 7) + { + yield return new ValidationResult($"{nameof(CompatibleTrackObjects)} must contain at most 7 entries.", [nameof(CompatibleTrackObjects)]); + } - return true; + if (CompatibleRoadObjects.Count > 7) + { + yield return new ValidationResult($"{nameof(CompatibleRoadObjects)} must contain at most 7 entries.", [nameof(CompatibleRoadObjects)]); + } } } diff --git a/Definitions/ObjectModels/Objects/Building/BuildingObject.cs b/Definitions/ObjectModels/Objects/Building/BuildingObject.cs index 4f99a8d0..65725e66 100644 --- a/Definitions/ObjectModels/Objects/Building/BuildingObject.cs +++ b/Definitions/ObjectModels/Objects/Building/BuildingObject.cs @@ -1,5 +1,6 @@ using Definitions.ObjectModels.Objects.Common; using Definitions.ObjectModels.Types; +using System.ComponentModel.DataAnnotations; namespace Definitions.ObjectModels.Objects.Building; @@ -32,7 +33,17 @@ public class BuildingObject : ILocoStruct, IHasBuildingComponents public List ElevatorHeightSequences { get; set; } = []; - public bool Validate() - => ProducedQuantity.Count == 2 - && BuildingComponents.Validate(); + public IEnumerable Validate(ValidationContext validationContext) + { + var bcValidationContext = new ValidationContext(BuildingComponents); + foreach (var result in BuildingComponents.Validate(bcValidationContext)) + { + yield return result; + } + + if (ProducedQuantity.Count != 2) + { + yield return new ValidationResult($"{nameof(ProducedQuantity)} must have exactly 2 entries.", [nameof(ProducedQuantity)]); + } + } } diff --git a/Definitions/ObjectModels/Objects/Cargo/CargoObject.cs b/Definitions/ObjectModels/Objects/Cargo/CargoObject.cs index 159a995e..c3ac4839 100644 --- a/Definitions/ObjectModels/Objects/Cargo/CargoObject.cs +++ b/Definitions/ObjectModels/Objects/Cargo/CargoObject.cs @@ -1,3 +1,4 @@ +using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; namespace Definitions.ObjectModels.Objects.Cargo; @@ -18,7 +19,16 @@ public class CargoObject : ILocoStruct public uint8_t UnitSize { get; set; } public uint16_t var_02 { get; set; } - public bool Validate() - => var_02 <= 3840 - && CargoTransferTime != 0; + public IEnumerable Validate(ValidationContext validationContext) + { + if (var_02 > 3840) + { + yield return new ValidationResult($"{nameof(var_02)} must be less than or equal to 3840."); + } + + if (CargoTransferTime == 0) + { + yield return new ValidationResult($"{nameof(CargoTransferTime)} must be non-zero."); + } + } } diff --git a/Definitions/ObjectModels/Objects/CliffEdge/CliffEdgeObject.cs b/Definitions/ObjectModels/Objects/CliffEdge/CliffEdgeObject.cs index f233d8ea..b01b66ee 100644 --- a/Definitions/ObjectModels/Objects/CliffEdge/CliffEdgeObject.cs +++ b/Definitions/ObjectModels/Objects/CliffEdge/CliffEdgeObject.cs @@ -1,6 +1,9 @@ +using System.ComponentModel.DataAnnotations; + namespace Definitions.ObjectModels.Objects.CliffEdge; public class CliffEdgeObject : ILocoStruct { - public bool Validate() => true; + public IEnumerable Validate(ValidationContext validationContext) + => []; } diff --git a/Definitions/ObjectModels/Objects/Climate/ClimateObject.cs b/Definitions/ObjectModels/Objects/Climate/ClimateObject.cs index f1ede564..4d251c71 100644 --- a/Definitions/ObjectModels/Objects/Climate/ClimateObject.cs +++ b/Definitions/ObjectModels/Objects/Climate/ClimateObject.cs @@ -1,3 +1,5 @@ +using System.ComponentModel.DataAnnotations; + namespace Definitions.ObjectModels.Objects.Climate; public class ClimateObject : ILocoStruct @@ -10,7 +12,16 @@ public class ClimateObject : ILocoStruct public uint8_t WinterSnowLine { get; set; } public uint8_t SummerSnowLine { get; set; } - public bool Validate() - => WinterSnowLine <= SummerSnowLine - && FirstSeason < 4; + public IEnumerable Validate(ValidationContext validationContext) + { + if (WinterSnowLine > SummerSnowLine) + { + yield return new ValidationResult("WinterSnowLine must be less than or equal to SummerSnowLine", [nameof(WinterSnowLine), nameof(SummerSnowLine)]); + } + + if (FirstSeason >= 4) + { + yield return new ValidationResult("FirstSeason must be less than 4", [nameof(FirstSeason)]); + } + } } diff --git a/Definitions/ObjectModels/Objects/Common/BuildingComponentsModel.cs b/Definitions/ObjectModels/Objects/Common/BuildingComponentsModel.cs index 35df97dc..eba18d1b 100644 --- a/Definitions/ObjectModels/Objects/Common/BuildingComponentsModel.cs +++ b/Definitions/ObjectModels/Objects/Common/BuildingComponentsModel.cs @@ -1,3 +1,5 @@ +using System.ComponentModel.DataAnnotations; + namespace Definitions.ObjectModels.Objects.Common; public interface IHasBuildingComponents @@ -5,15 +7,32 @@ public interface IHasBuildingComponents BuildingComponentsModel BuildingComponents { get; set; } } -public class BuildingComponentsModel +public class BuildingComponentsModel : ILocoStruct { public List BuildingHeights { get; set; } = []; public List BuildingAnimations { get; set; } = []; public List> BuildingVariations { get; set; } = []; - public bool Validate() - => BuildingHeights.Count is not 0 and not > 63 - && BuildingAnimations.Count is not 0 and not > 63 - && BuildingHeights.Count == BuildingAnimations.Count - && BuildingVariations.Count is not 0 and <= 31; + public IEnumerable Validate(ValidationContext validationContext) + { + if (BuildingHeights.Count is not 0 and not > 63) + { + yield return new ValidationResult($"{nameof(BuildingHeights)} must contain between 1 and 63 entries.", [nameof(BuildingHeights)]); + } + + if (BuildingAnimations.Count is not 0 and not > 63) + { + yield return new ValidationResult($"{nameof(BuildingAnimations)} must contain between 1 and 63 entries.", [nameof(BuildingAnimations)]); + } + + if (BuildingHeights.Count == BuildingAnimations.Count) + { + yield return new ValidationResult($"{nameof(BuildingHeights)} and {nameof(BuildingAnimations)} must contain the same number of entries.", [nameof(BuildingHeights), nameof(BuildingAnimations)]); + } + + if (BuildingVariations.Count is not 0 and <= 31) + { + yield return new ValidationResult($"{nameof(BuildingVariations)} must contain between 1 and 31 entries.", [nameof(BuildingVariations)]); + } + } } diff --git a/Definitions/ObjectModels/Objects/Competitor/CompetitorObject.cs b/Definitions/ObjectModels/Objects/Competitor/CompetitorObject.cs index d61d3f8f..d0573c5f 100644 --- a/Definitions/ObjectModels/Objects/Competitor/CompetitorObject.cs +++ b/Definitions/ObjectModels/Objects/Competitor/CompetitorObject.cs @@ -1,5 +1,4 @@ -using System.Diagnostics.CodeAnalysis; - +using System.ComponentModel.DataAnnotations; namespace Definitions.ObjectModels.Objects.Competitor; public class CompetitorObject : ILocoStruct @@ -12,23 +11,26 @@ public class CompetitorObject : ILocoStruct public uint8_t Competitiveness { get; set; } public uint8_t var_37 { get; set; } - public bool Validate() + public IEnumerable Validate(ValidationContext validationContext) { if (!Emotions.HasFlag(EmotionFlags.Neutral)) { - return false; + yield return new ValidationResult($"{nameof(Emotions)} must include {nameof(EmotionFlags.Neutral)}.", [nameof(Emotions)]); } if (Intelligence is < 1 or > 9) { - return false; + yield return new ValidationResult($"{nameof(Intelligence)} must be between 1 and 9 inclusive.", [nameof(Intelligence)]); } if (Aggressiveness is < 1 or > 9) { - return false; + yield return new ValidationResult($"{nameof(Aggressiveness)} must be between 1 and 9 inclusive.", [nameof(Aggressiveness)]); } - return Competitiveness is >= 1 and <= 9; + if (Competitiveness is < 1 or > 9) + { + yield return new ValidationResult($"{nameof(Competitiveness)} must be between 1 and 9 inclusive.", [nameof(Competitiveness)]); + } } } diff --git a/Definitions/ObjectModels/Objects/Currency/CurrencyObject.cs b/Definitions/ObjectModels/Objects/Currency/CurrencyObject.cs index 6b303d13..c565326e 100644 --- a/Definitions/ObjectModels/Objects/Currency/CurrencyObject.cs +++ b/Definitions/ObjectModels/Objects/Currency/CurrencyObject.cs @@ -1,3 +1,5 @@ +using System.ComponentModel.DataAnnotations; + namespace Definitions.ObjectModels.Objects.Currency; public class CurrencyObject : ILocoStruct @@ -5,18 +7,16 @@ public class CurrencyObject : ILocoStruct public uint8_t Separator { get; set; } public uint8_t Factor { get; set; } - public bool Validate() + public IEnumerable Validate(ValidationContext validationContext) { if (Separator > 4) { - return false; + yield return new ValidationResult("Separator must be between 0 and 4", [nameof(Separator)]); } if (Factor > 3) { - return false; + yield return new ValidationResult("Factor must be between 0 and 3", [nameof(Factor)]); } - - return true; } } diff --git a/Definitions/ObjectModels/Objects/Dock/DockObject.cs b/Definitions/ObjectModels/Objects/Dock/DockObject.cs index 2fd5d9fc..2f5ce690 100644 --- a/Definitions/ObjectModels/Objects/Dock/DockObject.cs +++ b/Definitions/ObjectModels/Objects/Dock/DockObject.cs @@ -1,5 +1,6 @@ using Definitions.ObjectModels.Objects.Common; using Definitions.ObjectModels.Types; +using System.ComponentModel.DataAnnotations; namespace Definitions.ObjectModels.Objects.Dock; @@ -16,23 +17,27 @@ public class DockObject : ILocoStruct, IHasBuildingComponents public BuildingComponentsModel BuildingComponents { get; set; } = new(); - public bool Validate() + public IEnumerable Validate(ValidationContext validationContext) { - if (BuildingComponents.Validate()) + var bcValidationContext = new ValidationContext(BuildingComponents); + foreach (var result in BuildingComponents.Validate(bcValidationContext)) { - return false; + yield return result; } if (CostIndex > 32) { - return false; + yield return new ValidationResult($"{nameof(CostIndex)} must be between 0 and 32 (inclusive).", [nameof(CostIndex)]); } if (-SellCostFactor > BuildCostFactor) { - return false; + yield return new ValidationResult($"{nameof(SellCostFactor)} must be between 0 and -{BuildCostFactor} (inclusive).", [nameof(SellCostFactor), nameof(BuildCostFactor)]); } - return BuildCostFactor > 0; + if (BuildCostFactor <= 0) + { + yield return new ValidationResult($"{nameof(BuildCostFactor)} must be greater than 0.", [nameof(BuildCostFactor)]); + } } } diff --git a/Definitions/ObjectModels/Objects/HillShapes/HillShapesObject.cs b/Definitions/ObjectModels/Objects/HillShapes/HillShapesObject.cs index 3dc6a7d0..ed9dfd79 100644 --- a/Definitions/ObjectModels/Objects/HillShapes/HillShapesObject.cs +++ b/Definitions/ObjectModels/Objects/HillShapes/HillShapesObject.cs @@ -1,3 +1,5 @@ +using System.ComponentModel.DataAnnotations; + namespace Definitions.ObjectModels.Objects.HillShape; public class HillShapesObject : ILocoStruct @@ -6,5 +8,6 @@ public class HillShapesObject : ILocoStruct public uint8_t MountainHeightMapCount { get; set; } public bool IsHeightMap { get; set; } - public bool Validate() => true; + public IEnumerable Validate(ValidationContext validationContext) + => []; } diff --git a/Definitions/ObjectModels/Objects/Industry/IndustryObject.cs b/Definitions/ObjectModels/Objects/Industry/IndustryObject.cs index 54fcc88f..3a667db4 100644 --- a/Definitions/ObjectModels/Objects/Industry/IndustryObject.cs +++ b/Definitions/ObjectModels/Objects/Industry/IndustryObject.cs @@ -1,6 +1,6 @@ using Definitions.ObjectModels.Objects.Common; using Definitions.ObjectModels.Types; -using System.Diagnostics.CodeAnalysis; +using System.ComponentModel.DataAnnotations; namespace Definitions.ObjectModels.Objects.Industry; @@ -41,59 +41,58 @@ public class IndustryObject : ILocoStruct, IHasBuildingComponents public List UnkBuildingData { get; set; } = []; public List var_38 { get; set; } = []; // Access with getUnk38 helper method - public bool Validate() + public IEnumerable Validate(ValidationContext validationContext) { - if (!BuildingComponents.Validate()) + var bcValidationContext = new ValidationContext(BuildingComponents); + foreach (var result in BuildingComponents.Validate(bcValidationContext)) { - return false; + yield return result; } if (MaxNumBuildings < MinNumBuildings) { - return false; + yield return new ValidationResult("MaxNumBuildings must be greater than or equal to MinNumBuildings", [nameof(MaxNumBuildings), nameof(MinNumBuildings)]); } if (TotalOfTypeInScenario is 0 or > 32) { - return false; + yield return new ValidationResult("TotalOfTypeInScenario must be between 1 and 32", [nameof(TotalOfTypeInScenario)]); } // 230/256 = ~90% if (-SellCostFactor > BuildCostFactor * 230 / 256) { - return false; + yield return new ValidationResult("SellCostFactor must be at least -90% of BuildCostFactor", [nameof(SellCostFactor), nameof(BuildCostFactor)]); } if (var_E8 > 8) { - return false; + yield return new ValidationResult("var_E8 must be between 0 and 8", [nameof(var_E8)]); } - switch (FarmTileNumImageAngles) + if (FarmTileNumImageAngles is not 1 or 2 or 4) { - case 1: - case 2: - case 4: - break; - default: - return false; + yield return new ValidationResult("FarmTileNumImageAngles must be 1, 2, or 4", [nameof(FarmTileNumImageAngles)]); } if (FarmGrowthStageWithNoProduction is not 0xFF and > 7) { - return false; + yield return new ValidationResult("FarmGrowthStageWithNoProduction must be between 0 and 7, or 0xFF", [nameof(FarmGrowthStageWithNoProduction)]); } if (FarmNumStagesOfGrowth > 8) { - return false; + yield return new ValidationResult("FarmNumStagesOfGrowth must be between 1 and 8", [nameof(FarmNumStagesOfGrowth)]); } if (InitialProductionRate[0].Min > 100) { - return false; + yield return new ValidationResult("InitialProductionRate[0].Min must be less than or equal to 100", [nameof(InitialProductionRate)]); } - return InitialProductionRate[1].Min <= 100; + if (InitialProductionRate[1].Min > 100) + { + yield return new ValidationResult("InitialProductionRate[1].Min must be less than or equal to 100", [nameof(InitialProductionRate)]); + } } } diff --git a/Definitions/ObjectModels/Objects/InterfaceSkin/InterfaceSkinObject.cs b/Definitions/ObjectModels/Objects/InterfaceSkin/InterfaceSkinObject.cs index b2387d2d..2077751b 100644 --- a/Definitions/ObjectModels/Objects/InterfaceSkin/InterfaceSkinObject.cs +++ b/Definitions/ObjectModels/Objects/InterfaceSkin/InterfaceSkinObject.cs @@ -1,4 +1,5 @@ using Definitions.ObjectModels.Types; +using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; namespace Definitions.ObjectModels.Objects.InterfaceSkin; @@ -24,5 +25,6 @@ public class InterfaceSkinObject : ILocoStruct public Colour PlayerInfoToolbarColour { get; set; } public Colour TimeToolbarColour { get; set; } - public bool Validate() => true; + public IEnumerable Validate(ValidationContext validationContext) + => []; } diff --git a/Definitions/ObjectModels/Objects/Land/LandObject.cs b/Definitions/ObjectModels/Objects/Land/LandObject.cs index e82efe01..d542f19d 100644 --- a/Definitions/ObjectModels/Objects/Land/LandObject.cs +++ b/Definitions/ObjectModels/Objects/Land/LandObject.cs @@ -1,4 +1,5 @@ using Definitions.ObjectModels.Types; +using System.ComponentModel.DataAnnotations; namespace Definitions.ObjectModels.Objects.Land; @@ -17,28 +18,31 @@ public class LandObject : ILocoStruct public ObjectModelHeader CliffEdgeHeader { get; set; } public ObjectModelHeader? UnkObjectHeader { get; set; } - public bool Validate() + public IEnumerable Validate(ValidationContext validationContext) { if (CostIndex > 32) { - return false; + yield return new ValidationResult($"{nameof(CostIndex)} must be in the range 0-32.", [nameof(CostIndex)]); } if (CostFactor <= 0) { - return false; + yield return new ValidationResult($"{nameof(CostFactor)} must be positive.", [nameof(CostFactor)]); } if (NumGrowthStages < 1) { - return false; + yield return new ValidationResult($"{nameof(NumGrowthStages)} must be at least 1.", [nameof(NumGrowthStages)]); } if (NumGrowthStages > 8) { - return false; + yield return new ValidationResult($"{nameof(NumGrowthStages)} must be at most 8.", [nameof(NumGrowthStages)]); } - return NumImageAngles is 1 or 2 or 4; + if (NumImageAngles is not 1 or 2 or 4) + { + yield return new ValidationResult($"{nameof(NumImageAngles)} must be 1, 2, or 4.", [nameof(NumImageAngles)]); + } } } diff --git a/Definitions/ObjectModels/Objects/LevelCrossing/LevelCrossingObject.cs b/Definitions/ObjectModels/Objects/LevelCrossing/LevelCrossingObject.cs index 66cf30d8..d94316d7 100644 --- a/Definitions/ObjectModels/Objects/LevelCrossing/LevelCrossingObject.cs +++ b/Definitions/ObjectModels/Objects/LevelCrossing/LevelCrossingObject.cs @@ -1,3 +1,5 @@ +using System.ComponentModel.DataAnnotations; + namespace Definitions.ObjectModels.Objects.LevelCrossing; public class LevelCrossingObject : ILocoStruct @@ -11,22 +13,21 @@ public class LevelCrossingObject : ILocoStruct public uint8_t var_0A { get; set; } // something like IdleAnimationFrames or something public uint16_t DesignedYear { get; set; } - public bool Validate() + public IEnumerable Validate(ValidationContext validationContext) { if (-SellCostFactor > CostFactor) { - return false; + yield return new ValidationResult("SellCostFactor must not be greater than CostFactor", [nameof(SellCostFactor), nameof(CostFactor)]); } if (CostFactor <= 0) { - return false; + yield return new ValidationResult("CostFactor must be positive", [nameof(CostFactor)]); } - return ClosingFrames switch + if (ClosingFrames is not 1 or 2 or 4 or 8 or 16 or 32) { - 1 or 2 or 4 or 8 or 16 or 32 => true, - _ => false, - }; + yield return new ValidationResult("ClosingFrames must be a power of two between 1 and 32 (inclusive)", [nameof(ClosingFrames)]); + } } } diff --git a/Definitions/ObjectModels/Objects/Region/RegionObject.cs b/Definitions/ObjectModels/Objects/Region/RegionObject.cs index 6919a447..159ba960 100644 --- a/Definitions/ObjectModels/Objects/Region/RegionObject.cs +++ b/Definitions/ObjectModels/Objects/Region/RegionObject.cs @@ -1,4 +1,5 @@ using Definitions.ObjectModels.Types; +using System.ComponentModel.DataAnnotations; namespace Definitions.ObjectModels.Objects.Region; @@ -10,6 +11,6 @@ public class RegionObject : ILocoStruct public List DependentObjects { get; set; } = []; public List CargoInfluenceTownFilter { get; set; } = []; - public bool Validate() - => true; + public IEnumerable Validate(ValidationContext validationContext) + => []; } diff --git a/Definitions/ObjectModels/Objects/Road/RoadObject.cs b/Definitions/ObjectModels/Objects/Road/RoadObject.cs index 37d0a113..a16c4e14 100644 --- a/Definitions/ObjectModels/Objects/Road/RoadObject.cs +++ b/Definitions/ObjectModels/Objects/Road/RoadObject.cs @@ -1,4 +1,5 @@ using Definitions.ObjectModels.Types; +using System.ComponentModel.DataAnnotations; namespace Definitions.ObjectModels.Objects.Road; @@ -21,44 +22,41 @@ public class RoadObject : ILocoStruct public List Bridges { get; set; } = []; public List Stations { get; set; } = []; - public bool Validate() + public IEnumerable Validate(ValidationContext validationContext) { - // check missing in vanilla if (CostIndex >= 32) { - return false; + yield return new ValidationResult("CostIndex must be less than 32", [nameof(CostIndex)]); } if (-SellCostFactor > BuildCostFactor) { - return false; + yield return new ValidationResult("SellCostFactor must not be less than negative BuildCostFactor", [nameof(SellCostFactor), nameof(BuildCostFactor)]); } if (BuildCostFactor <= 0) { - return false; + yield return new ValidationResult("BuildCostFactor must be greater than 0", [nameof(BuildCostFactor)]); } if (TunnelCostFactor <= 0) { - return false; + yield return new ValidationResult("TunnelCostFactor must be greater than 0", [nameof(TunnelCostFactor)]); } if (Bridges.Count > 7) { - return false; + yield return new ValidationResult("Bridges.Count must be 7 or less", [nameof(Bridges)]); } if (RoadMods.Count > 2) { - return false; + yield return new ValidationResult("RoadMods.Count must be 2 or less", [nameof(RoadMods)]); } - if (Flags.HasFlag(RoadObjectFlags.unk_03)) + if (Flags.HasFlag(RoadObjectFlags.unk_03) && RoadMods.Count != 0) { - return RoadMods.Count == 0; + yield return new ValidationResult("If unk_03 flag is set, RoadMods.Count must be 0", [nameof(Flags), nameof(RoadMods)]); } - - return true; } } diff --git a/Definitions/ObjectModels/Objects/RoadExtra/RoadExtraObject.cs b/Definitions/ObjectModels/Objects/RoadExtra/RoadExtraObject.cs index e0537de3..95ea453c 100644 --- a/Definitions/ObjectModels/Objects/RoadExtra/RoadExtraObject.cs +++ b/Definitions/ObjectModels/Objects/RoadExtra/RoadExtraObject.cs @@ -1,4 +1,5 @@ using Definitions.ObjectModels.Objects.Road; +using System.ComponentModel.DataAnnotations; namespace Definitions.ObjectModels.Objects.RoadExtra; @@ -10,24 +11,28 @@ public class RoadExtraObject : ILocoStruct public int16_t BuildCostFactor { get; set; } public int16_t SellCostFactor { get; set; } - public bool Validate() + public IEnumerable Validate(ValidationContext validationContext) { if (PaintStyle >= 2) { - return false; + yield return new ValidationResult("PaintStyle must be 0 or 1", [nameof(PaintStyle)]); } // This check missing from vanilla if (CostIndex >= 32) { - return false; + yield return new ValidationResult("CostIndex must be less than 32", [nameof(CostIndex)]); } if (-SellCostFactor > BuildCostFactor) { - return false; + yield return new ValidationResult("SellCostFactor must be greater than or equal to -BuildCostFactor", [nameof(SellCostFactor), nameof(BuildCostFactor)]); + } - return BuildCostFactor > 0; + if (BuildCostFactor <= 0) + { + yield return new ValidationResult("BuildCostFactor must be greater than 0", [nameof(BuildCostFactor)]); + } } } diff --git a/Definitions/ObjectModels/Objects/RoadStation/RoadStationObject.cs b/Definitions/ObjectModels/Objects/RoadStation/RoadStationObject.cs index 5a7422de..9633dd8c 100644 --- a/Definitions/ObjectModels/Objects/RoadStation/RoadStationObject.cs +++ b/Definitions/ObjectModels/Objects/RoadStation/RoadStationObject.cs @@ -1,6 +1,7 @@ using Definitions.ObjectModels.Objects.Road; using Definitions.ObjectModels.Objects.Shared; using Definitions.ObjectModels.Types; +using System.ComponentModel.DataAnnotations; namespace Definitions.ObjectModels.Objects.RoadStation; @@ -23,38 +24,36 @@ public class RoadStationObject : ILocoStruct //public uint8_t[][][] CargoOffsetBytes { get; set; } public CargoOffset[][][] CargoOffsets { get; set; } - public bool Validate() + public IEnumerable Validate(ValidationContext validationContext) { if (CostIndex >= 32) { - return false; + yield return new ValidationResult($"{nameof(CostIndex)} must be between 0 and 31 inclusive.", [nameof(CostIndex)]); } if (-SellCostFactor > BuildCostFactor) { - return false; + yield return new ValidationResult($"The negative of {nameof(SellCostFactor)} must be less than or equal to {nameof(BuildCostFactor)}.", [nameof(SellCostFactor), nameof(BuildCostFactor)]); } if (BuildCostFactor <= 0) { - return false; + yield return new ValidationResult($"{nameof(BuildCostFactor)} must be positive.", [nameof(BuildCostFactor)]); } if (PaintStyle >= 1) { - return false; + yield return new ValidationResult($"{nameof(PaintStyle)} must be 0.", [nameof(PaintStyle)]); } if (CompatibleRoadObjects.Count > 7) { - return false; + yield return new ValidationResult($"{nameof(CompatibleRoadObjects)} must have at most 7 entries.", [nameof(CompatibleRoadObjects)]); } if (Flags.HasFlag(RoadStationObjectFlags.Passenger) && Flags.HasFlag(RoadStationObjectFlags.Freight)) { - return false; + yield return new ValidationResult($"Only one of {nameof(RoadStationObjectFlags.Passenger)} or {nameof(RoadStationObjectFlags.Freight)} can be set.", [nameof(Flags)]); } - - return true; } } diff --git a/Definitions/ObjectModels/Objects/Scaffolding/ScaffoldingObject.cs b/Definitions/ObjectModels/Objects/Scaffolding/ScaffoldingObject.cs index 0e21d344..528b5b71 100644 --- a/Definitions/ObjectModels/Objects/Scaffolding/ScaffoldingObject.cs +++ b/Definitions/ObjectModels/Objects/Scaffolding/ScaffoldingObject.cs @@ -1,3 +1,4 @@ +using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; namespace Definitions.ObjectModels.Objects.Scaffolding; @@ -6,5 +7,6 @@ public class ScaffoldingObject : ILocoStruct public List SegmentHeights { get; set; } = []; public List RoofHeights { get; set; } = []; - public bool Validate() => true; + public IEnumerable Validate(ValidationContext validationContext) + => []; } diff --git a/Definitions/ObjectModels/Objects/ScenarioText/ScenarioTextObject.cs b/Definitions/ObjectModels/Objects/ScenarioText/ScenarioTextObject.cs index 861f557f..06c5c8d3 100644 --- a/Definitions/ObjectModels/Objects/ScenarioText/ScenarioTextObject.cs +++ b/Definitions/ObjectModels/Objects/ScenarioText/ScenarioTextObject.cs @@ -1,8 +1,9 @@ +using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; namespace Definitions.ObjectModels.Objects.ScenarioText; public class ScenarioTextObject : ILocoStruct { - public bool Validate() - => true; + public IEnumerable Validate(ValidationContext validationContext) + => []; } diff --git a/Definitions/ObjectModels/Objects/Snow/SnowObject.cs b/Definitions/ObjectModels/Objects/Snow/SnowObject.cs index fd6ca46a..64b6db1d 100644 --- a/Definitions/ObjectModels/Objects/Snow/SnowObject.cs +++ b/Definitions/ObjectModels/Objects/Snow/SnowObject.cs @@ -1,8 +1,10 @@ +using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; namespace Definitions.ObjectModels.Objects.Snow; public class SnowObject : ILocoStruct { - public bool Validate() => true; + public IEnumerable Validate(ValidationContext validationContext) + => []; } diff --git a/Definitions/ObjectModels/Objects/Sound/SoundObject.cs b/Definitions/ObjectModels/Objects/Sound/SoundObject.cs index 69776203..ded371dc 100644 --- a/Definitions/ObjectModels/Objects/Sound/SoundObject.cs +++ b/Definitions/ObjectModels/Objects/Sound/SoundObject.cs @@ -1,4 +1,5 @@ using System.ComponentModel; +using System.ComponentModel.DataAnnotations; namespace Definitions.ObjectModels.Objects.Sound; @@ -13,6 +14,11 @@ public class SoundObject : ILocoStruct public uint32_t NumUnkStructs { get; set; } [Browsable(false)] public byte[] UnkData { get; set; } - public bool Validate() - => SoundObjectData?.Offset >= 0; + public IEnumerable Validate(ValidationContext validationContext) + { + if (SoundObjectData?.Offset < -1) // todo: move validation into SoundObjectData + { + yield return new ValidationResult($"{nameof(SoundObjectData.Offset)} must be -1 or non-negative.", [nameof(SoundObjectData), nameof(SoundObjectData.Offset)]); + } + } } diff --git a/Definitions/ObjectModels/Objects/Steam/SteamObject.cs b/Definitions/ObjectModels/Objects/Steam/SteamObject.cs index a1ae6023..5686d987 100644 --- a/Definitions/ObjectModels/Objects/Steam/SteamObject.cs +++ b/Definitions/ObjectModels/Objects/Steam/SteamObject.cs @@ -1,4 +1,5 @@ using Definitions.ObjectModels.Types; +using System.ComponentModel.DataAnnotations; namespace Definitions.ObjectModels.Objects.Steam; @@ -15,6 +16,6 @@ public class SteamObject : ILocoStruct public List FrameInfoType1 { get; set; } = []; public List SoundEffects { get; set; } = []; - public bool Validate() - => true; + public IEnumerable Validate(ValidationContext validationContext) + => []; } diff --git a/Definitions/ObjectModels/Objects/StreetLight/StreetLightObject.cs b/Definitions/ObjectModels/Objects/StreetLight/StreetLightObject.cs index 39254f15..d5fa87d4 100644 --- a/Definitions/ObjectModels/Objects/StreetLight/StreetLightObject.cs +++ b/Definitions/ObjectModels/Objects/StreetLight/StreetLightObject.cs @@ -1,8 +1,10 @@ +using System.ComponentModel.DataAnnotations; + namespace Definitions.ObjectModels.Objects.Streetlight; public class StreetLightObject : ILocoStruct { public List DesignedYears { get; set; } = []; - public bool Validate() - => true; + public IEnumerable Validate(ValidationContext validationContext) + => []; } diff --git a/Definitions/ObjectModels/Objects/TownNames/TownNamesObject.cs b/Definitions/ObjectModels/Objects/TownNames/TownNamesObject.cs index 05f29ecf..eebf7299 100644 --- a/Definitions/ObjectModels/Objects/TownNames/TownNamesObject.cs +++ b/Definitions/ObjectModels/Objects/TownNames/TownNamesObject.cs @@ -1,8 +1,11 @@ +using System.ComponentModel.DataAnnotations; + namespace Definitions.ObjectModels.Objects.TownNames; public class TownNamesObject : ILocoStruct { public List Categories { get; set; } = []; - public bool Validate() => true; + public IEnumerable Validate(ValidationContext validationContext) + => []; } diff --git a/Definitions/ObjectModels/Objects/Track/TrackObject.cs b/Definitions/ObjectModels/Objects/Track/TrackObject.cs index 225b6a60..877b553d 100644 --- a/Definitions/ObjectModels/Objects/Track/TrackObject.cs +++ b/Definitions/ObjectModels/Objects/Track/TrackObject.cs @@ -1,4 +1,5 @@ using Definitions.ObjectModels.Types; +using System.ComponentModel.DataAnnotations; namespace Definitions.ObjectModels.Objects.Track; @@ -22,45 +23,47 @@ public class TrackObject : ILocoStruct public List Bridges { get; set; } = []; public List Stations { get; set; } = []; - public bool Validate() + public IEnumerable Validate(ValidationContext validationContext) { if (var_06 >= 3) { - return false; + yield return new ValidationResult($"{nameof(var_06)} must be 0, 1, or 2.", [nameof(var_06)]); } // vanilla missed this check if (CostIndex > 32) { - return false; + yield return new ValidationResult($"{nameof(CostIndex)} must be between 0 and 32 inclusive.", [nameof(CostIndex)]); } if (-SellCostFactor > BuildCostFactor) { - return false; + yield return new ValidationResult($"The negative of {nameof(SellCostFactor)} must be less than or equal to {nameof(BuildCostFactor)}.", [nameof(SellCostFactor), nameof(BuildCostFactor)]); } if (BuildCostFactor <= 0) { - return false; + yield return new ValidationResult($"{nameof(BuildCostFactor)} must be positive.", [nameof(BuildCostFactor)]); } if (TunnelCostFactor <= 0) { - return false; + yield return new ValidationResult($"{nameof(TunnelCostFactor)} must be positive.", [nameof(TunnelCostFactor)]); } - if (TrackPieces.HasFlag(TrackTraitFlags.Diagonal | TrackTraitFlags.LargeCurve) - && TrackPieces.HasFlag(TrackTraitFlags.OneSided | TrackTraitFlags.VerySmallCurve)) + if (TrackPieces.HasFlag(TrackTraitFlags.Diagonal | TrackTraitFlags.LargeCurve) && TrackPieces.HasFlag(TrackTraitFlags.OneSided | TrackTraitFlags.VerySmallCurve)) { - return false; + yield return new ValidationResult($"{nameof(TrackPieces)} cannot include both {TrackTraitFlags.Diagonal} or {TrackTraitFlags.LargeCurve} and {TrackTraitFlags.OneSided} or {TrackTraitFlags.VerySmallCurve}.", [nameof(TrackPieces)]); } if (Bridges.Count > 7) { - return false; + yield return new ValidationResult($"{nameof(Bridges)} can contain at most 7 entries.", [nameof(Bridges)]); } - return Stations.Count <= 7; + if (Stations.Count > 7) + { + yield return new ValidationResult($"{nameof(Stations)} can contain at most 7 entries.", [nameof(Stations)]); + } } } diff --git a/Definitions/ObjectModels/Objects/TrackExtra/TrackExtraObject.cs b/Definitions/ObjectModels/Objects/TrackExtra/TrackExtraObject.cs index 262778e0..132c2413 100644 --- a/Definitions/ObjectModels/Objects/TrackExtra/TrackExtraObject.cs +++ b/Definitions/ObjectModels/Objects/TrackExtra/TrackExtraObject.cs @@ -1,4 +1,5 @@ using Definitions.ObjectModels.Objects.Track; +using System.ComponentModel.DataAnnotations; namespace Definitions.ObjectModels.Objects.TrackExtra; @@ -10,24 +11,27 @@ public class TrackExtraObject : ILocoStruct public int16_t BuildCostFactor { get; set; } public int16_t SellCostFactor { get; set; } - public bool Validate() + public IEnumerable Validate(ValidationContext validationContext) { if (PaintStyle >= 2) { - return false; + yield return new ValidationResult($"{nameof(PaintStyle)} must be either 0 (normal) or 1 (fancy).", [nameof(PaintStyle)]); } // This check missing from vanilla if (CostIndex > 32) { - return false; + yield return new ValidationResult($"{nameof(CostIndex)} must be between 0 and 32 inclusive.", [nameof(CostIndex)]); } if (-SellCostFactor > BuildCostFactor) { - return false; + yield return new ValidationResult($"The negative of {nameof(SellCostFactor)} must be less than or equal to {nameof(BuildCostFactor)}.", [nameof(SellCostFactor), nameof(BuildCostFactor)]); } - return BuildCostFactor > 0; + if (BuildCostFactor > 0) + { + yield return new ValidationResult($"{nameof(BuildCostFactor)} must be greater than 0.", [nameof(BuildCostFactor)]); + } } } diff --git a/Definitions/ObjectModels/Objects/TrackSignal/TrackSignalObject.cs b/Definitions/ObjectModels/Objects/TrackSignal/TrackSignalObject.cs index 16cf390f..b18a4aa3 100644 --- a/Definitions/ObjectModels/Objects/TrackSignal/TrackSignalObject.cs +++ b/Definitions/ObjectModels/Objects/TrackSignal/TrackSignalObject.cs @@ -1,4 +1,5 @@ using Definitions.ObjectModels.Types; +using System.ComponentModel.DataAnnotations; namespace Definitions.ObjectModels.Objects.TrackSignal; @@ -15,46 +16,32 @@ public class TrackSignalObject : ILocoStruct public uint16_t ObsoleteYear { get; set; } public List CompatibleTrackObjects { get; set; } = []; - public bool Validate() + public IEnumerable Validate(ValidationContext validationContext) { - // animationSpeed must be 1 less than a power of 2 (its a mask) - switch (AnimationSpeed) + if (AnimationSpeed is not 0 and not 1 and not 3 and not 7 and not 15) { - case 0: - case 1: - case 3: - case 7: - case 15: - break; - default: - return false; + // animationSpeed must be 1 less than a power of 2 (its a mask) + yield return new ValidationResult($"{nameof(AnimationSpeed)} must be 0, 1, 3, 7, or 15.", [nameof(AnimationSpeed)]); } - switch (NumFrames) + if (NumFrames is not 4 or 7 or 10) { - case 4: - case 7: - case 10: - break; - default: - return false; + yield return new ValidationResult($"{nameof(NumFrames)} must be 4, 7, or 10.", [nameof(NumFrames)]); } if (CostIndex > 32) { - return false; + yield return new ValidationResult($"{nameof(CostIndex)} must be between 0 and 32 inclusive.", [nameof(CostIndex)]); } if (-SellCostFactor > BuildCostFactor) { - return false; + yield return new ValidationResult($"The negative of {nameof(SellCostFactor)} must be less than or equal to {nameof(BuildCostFactor)}.", [nameof(SellCostFactor), nameof(BuildCostFactor)]); } if (CompatibleTrackObjects.Count > 7) { - return false; + yield return new ValidationResult($"{nameof(CompatibleTrackObjects)} must have at most 7 entries.", [nameof(CompatibleTrackObjects)]); } - - return true; } } diff --git a/Definitions/ObjectModels/Objects/TrackStation/TrackStationObject.cs b/Definitions/ObjectModels/Objects/TrackStation/TrackStationObject.cs index 6cd81c4f..42b5a57f 100644 --- a/Definitions/ObjectModels/Objects/TrackStation/TrackStationObject.cs +++ b/Definitions/ObjectModels/Objects/TrackStation/TrackStationObject.cs @@ -1,6 +1,7 @@ using Definitions.ObjectModels.Objects.Shared; using Definitions.ObjectModels.Objects.Track; using Definitions.ObjectModels.Types; +using System.ComponentModel.DataAnnotations; namespace Definitions.ObjectModels.Objects.TrackStation; @@ -24,28 +25,31 @@ public class TrackStationObject : ILocoStruct public uint8_t[][] var_6E { get; set; } - public bool Validate() + public IEnumerable Validate(ValidationContext validationContext) { if (CostIndex >= 32) { - return false; + yield return new ValidationResult($"{nameof(CostIndex)} must be between 0 and 31 inclusive.", [nameof(CostIndex)]); } if (-SellCostFactor > BuildCostFactor) { - return false; + yield return new ValidationResult($"The negative of {nameof(SellCostFactor)} must be less than or equal to {nameof(BuildCostFactor)}.", [nameof(SellCostFactor), nameof(BuildCostFactor)]); } if (BuildCostFactor <= 0) { - return false; + yield return new ValidationResult($"{nameof(BuildCostFactor)} must be positive.", [nameof(BuildCostFactor)]); } if (PaintStyle >= 1) { - return false; + yield return new ValidationResult($"{nameof(PaintStyle)} must be 0.", [nameof(PaintStyle)]); } - return true; // CompatibleTrackObjects.Count <= TrackStationObjectLoader.Constants.MaxNumCompatible; + if (CompatibleTrackObjects.Count > 7 /*TrackStationObjectLoader.Constants.MaxNumCompatible*/) + { + yield return new ValidationResult($"{nameof(CompatibleTrackObjects)} must have at most 7 entries.", [nameof(CompatibleTrackObjects)]); + } } } diff --git a/Definitions/ObjectModels/Objects/Tree/TreeObject.cs b/Definitions/ObjectModels/Objects/Tree/TreeObject.cs index 87c44232..568e04d5 100644 --- a/Definitions/ObjectModels/Objects/Tree/TreeObject.cs +++ b/Definitions/ObjectModels/Objects/Tree/TreeObject.cs @@ -1,3 +1,5 @@ +using System.ComponentModel.DataAnnotations; + namespace Definitions.ObjectModels.Objects.Tree; public class TreeObject : ILocoStruct @@ -20,39 +22,37 @@ public class TreeObject : ILocoStruct public int16_t DemolishRatingReduction { get; set; } public TreeFlagsUnk var_3C { get; set; } // something with images - public bool Validate() + public IEnumerable Validate(ValidationContext validationContext) { if (CostIndex > 32) { - return false; + yield return new ValidationResult($"{nameof(CostIndex)} must be between 0 and 32 inclusive.", [nameof(CostIndex)]); } // 230/256 = ~90% if (-ClearCostFactor > BuildCostFactor * 230 / 256) { - return false; + yield return new ValidationResult($"The negative of {nameof(ClearCostFactor)} must be less than or equal to ~90% of {nameof(BuildCostFactor)}.", [nameof(ClearCostFactor), nameof(BuildCostFactor)]); } - switch (NumRotations) + if (NumRotations is not 1 or 2 or 4) { - default: - return false; - case 1: - case 2: - case 4: - break; + yield return new ValidationResult($"{nameof(NumRotations)} must be either 1, 2, or 4.", [nameof(NumRotations)]); } if (NumGrowthStages is < 1 or > 8) { - return false; + yield return new ValidationResult($"{nameof(NumGrowthStages)} must be between 1 and 8 inclusive.", [nameof(NumGrowthStages)]); } if (Height < Clearance) { - return false; + yield return new ValidationResult($"{nameof(Height)} must be greater than or equal to {nameof(Clearance)}.", [nameof(Height), nameof(Clearance)]); } - return var_05 >= var_04; + if (var_05 < var_04) + { + yield return new ValidationResult($"{nameof(var_05)} must be greater than or equal to {nameof(var_04)}.", [nameof(var_05), nameof(var_04)]); + } } } diff --git a/Definitions/ObjectModels/Objects/Tunnel/TunnelObject.cs b/Definitions/ObjectModels/Objects/Tunnel/TunnelObject.cs index 776b017c..49ed9eed 100644 --- a/Definitions/ObjectModels/Objects/Tunnel/TunnelObject.cs +++ b/Definitions/ObjectModels/Objects/Tunnel/TunnelObject.cs @@ -1,8 +1,10 @@ +using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; namespace Definitions.ObjectModels.Objects.Tunnel; public class TunnelObject : ILocoStruct { - public bool Validate() => true; + public IEnumerable Validate(ValidationContext validationContext) + => []; } diff --git a/Definitions/ObjectModels/Objects/Vehicle/BodySprite.cs b/Definitions/ObjectModels/Objects/Vehicle/BodySprite.cs index 5c46842c..5782ccae 100644 --- a/Definitions/ObjectModels/Objects/Vehicle/BodySprite.cs +++ b/Definitions/ObjectModels/Objects/Vehicle/BodySprite.cs @@ -1,4 +1,5 @@ using System.ComponentModel; +using System.ComponentModel.DataAnnotations; namespace Definitions.ObjectModels.Objects.Vehicle; @@ -27,5 +28,34 @@ public class BodySprite : ILocoStruct //public Dictionary> ImageIds { get; set; } = []; //public int NumImages { get; set; } - public bool Validate() => true; + public IEnumerable Validate(ValidationContext validationContext) + { + if (Flags.HasFlag(BodySpriteFlags.HasSprites)) + { + if (NumFlatRotationFrames is not 8 or 16 or 32 or 64 or 128) + { + yield return new ValidationResult($"{nameof(NumFlatRotationFrames)} must be one of the following values: 8, 16, 32, 64, 128.", [nameof(NumFlatRotationFrames)]); + } + + if (NumSlopedRotationFrames is not 4 or 8 or 16 or 32) + { + yield return new ValidationResult($"{nameof(NumSlopedRotationFrames)} must be one of the following values: 8, 16, 32, 64, 128.", [nameof(NumSlopedRotationFrames)]); + } + + if (NumAnimationFrames is not 1 or 2 or 4) + { + yield return new ValidationResult($"{nameof(NumAnimationFrames)} must be one of the following values: 1, 2, 4.", [nameof(NumAnimationFrames)]); + } + + if (NumCargoLoadFrames is < 1 or > 5) + { + yield return new ValidationResult($"{nameof(NumCargoLoadFrames)} must be between 1 and 5 inclusive.", [nameof(NumCargoLoadFrames)]); + } + + if (NumRollFrames is not 1 or 3) + { + yield return new ValidationResult($"{nameof(NumRollFrames)} must be one of the following values: 1, 3.", [nameof(NumRollFrames)]); + } + } + } } diff --git a/Definitions/ObjectModels/Objects/Vehicle/BogieSprite.cs b/Definitions/ObjectModels/Objects/Vehicle/BogieSprite.cs index dc9992df..daf35c07 100644 --- a/Definitions/ObjectModels/Objects/Vehicle/BogieSprite.cs +++ b/Definitions/ObjectModels/Objects/Vehicle/BogieSprite.cs @@ -1,4 +1,5 @@ using System.ComponentModel; +using System.ComponentModel.DataAnnotations; namespace Definitions.ObjectModels.Objects.Vehicle; @@ -19,5 +20,14 @@ public class BogieSprite : ILocoStruct public Dictionary> ImageIds { get; set; } = new(); public int NumImages { get; set; } - public bool Validate() => true; + public IEnumerable Validate(ValidationContext validationContext) + { + if (Flags.HasFlag(BogieSpriteFlags.HasSprites)) + { + if (RollStates is not 1 or 2 or 4) + { + yield return new ValidationResult($"{nameof(RollStates)} must be either 1, 2, or 4 when {nameof(Flags)} includes {nameof(BogieSpriteFlags.HasSprites)}.", [nameof(RollStates), nameof(Flags)]); + } + } + } } diff --git a/Definitions/ObjectModels/Objects/Vehicle/SimpleAnimation.cs b/Definitions/ObjectModels/Objects/Vehicle/SimpleAnimation.cs index d440283d..e6cd670f 100644 --- a/Definitions/ObjectModels/Objects/Vehicle/SimpleAnimation.cs +++ b/Definitions/ObjectModels/Objects/Vehicle/SimpleAnimation.cs @@ -1,4 +1,5 @@ using System.ComponentModel; +using System.ComponentModel.DataAnnotations; namespace Definitions.ObjectModels.Objects.Vehicle; @@ -9,5 +10,6 @@ public class SimpleAnimation : ILocoStruct public uint8_t Height { get; set; } public SimpleAnimationType Type { get; set; } - public bool Validate() => true; + public IEnumerable Validate(ValidationContext validationContext) + => []; } diff --git a/Definitions/ObjectModels/Objects/Vehicle/VehicleObject.cs b/Definitions/ObjectModels/Objects/Vehicle/VehicleObject.cs index c1a39565..41a632bd 100644 --- a/Definitions/ObjectModels/Objects/Vehicle/VehicleObject.cs +++ b/Definitions/ObjectModels/Objects/Vehicle/VehicleObject.cs @@ -1,5 +1,6 @@ using Definitions.ObjectModels.Objects.Cargo; using Definitions.ObjectModels.Types; +using System.ComponentModel.DataAnnotations; namespace Definitions.ObjectModels.Objects.Vehicle; @@ -48,134 +49,75 @@ public class VehicleObject : ILocoStruct public uint8_t[] var_135 { get; set; } = []; - public bool Validate() + public IEnumerable Validate(ValidationContext validationContext) { if (CostIndex > 32) { - return false; + yield return new ValidationResult($"{nameof(CostIndex)} must be between 0 and 32 inclusive.", [nameof(CostIndex)]); } if (RunCostIndex > 32) { - return false; + yield return new ValidationResult($"{nameof(RunCostIndex)} must be between 0 and 32 inclusive.", [nameof(RunCostIndex)]); } if (CostFactor <= 0) { - return false; + yield return new ValidationResult($"{nameof(CostFactor)} must be positive.", [nameof(CostFactor)]); } if (RunCostFactor < 0) { - return false; + yield return new ValidationResult($"{nameof(RunCostFactor)} must be non-negative.", [nameof(RunCostFactor)]); } if (Flags.HasFlag(VehicleObjectFlags.AnyRoadType)) { if (RequiredTrackExtras.Length != 0) { - return false; + yield return new ValidationResult($"{nameof(RequiredTrackExtras)} must be empty if {nameof(VehicleObjectFlags.AnyRoadType)} is set.", [nameof(RequiredTrackExtras), nameof(Flags)]); } if (Flags.HasFlag(VehicleObjectFlags.RackRail)) { - return false; + yield return new ValidationResult($"{nameof(Flags)} cannot have both {nameof(VehicleObjectFlags.AnyRoadType)} and {nameof(VehicleObjectFlags.RackRail)} set.", [nameof(Flags)]); } } if (RequiredTrackExtras.Length > 4) { - return false; + yield return new ValidationResult($"{nameof(RequiredTrackExtras)} must have at most 4 entries.", [nameof(RequiredTrackExtras)]); } if (NumSimultaneousCargoTypes > 2) { - return false; + yield return new ValidationResult($"{nameof(NumSimultaneousCargoTypes)} must be between 0 and 2 inclusive.", [nameof(NumSimultaneousCargoTypes)]); } if (CompatibleVehicles.Length > 8) { - return false; + yield return new ValidationResult($"{nameof(CompatibleVehicles)} must have at most 8 entries.", [nameof(CompatibleVehicles)]); } if (RackSpeed > Speed) { - return false; + yield return new ValidationResult($"{nameof(RackSpeed)} must be less than or equal to {nameof(Speed)}.", [nameof(RackSpeed), nameof(Speed)]); } foreach (var bodySprite in BodySprites) { - if (!bodySprite.Flags.HasFlag(BodySpriteFlags.HasSprites)) + foreach (var result in bodySprite.Validate(validationContext)) { - continue; - } - - switch (bodySprite.NumFlatRotationFrames) - { - case 8: - case 16: - case 32: - case 64: - case 128: - break; - default: - return false; - } - - switch (bodySprite.NumSlopedRotationFrames) - { - case 4: - case 8: - case 16: - case 32: - break; - default: - return false; - } - - switch (bodySprite.NumAnimationFrames) - { - case 1: - case 2: - case 4: - break; - default: - return false; - } - - if (bodySprite.NumCargoLoadFrames is < 1 or > 5) - { - return false; - } - - switch (bodySprite.NumRollFrames) - { - case 1: - case 3: - break; - default: - return false; + yield return result; } } foreach (var bogieSprite in BogieSprites) { - if (!bogieSprite.Flags.HasFlag(BogieSpriteFlags.HasSprites)) + foreach (var result in bogieSprite.Validate(validationContext)) { - continue; - } - - switch (bogieSprite.RollStates) - { - case 1: - case 2: - case 4: - break; - default: - return false; + yield return result; } } - - return true; } } diff --git a/Definitions/ObjectModels/Objects/Vehicle/VehicleObjectCar.cs b/Definitions/ObjectModels/Objects/Vehicle/VehicleObjectCar.cs index 13807ffd..9c0dba5f 100644 --- a/Definitions/ObjectModels/Objects/Vehicle/VehicleObjectCar.cs +++ b/Definitions/ObjectModels/Objects/Vehicle/VehicleObjectCar.cs @@ -1,4 +1,5 @@ using System.ComponentModel; +using System.ComponentModel.DataAnnotations; namespace Definitions.ObjectModels.Objects.Vehicle; @@ -12,5 +13,6 @@ public class VehicleObjectCar : ILocoStruct public uint8_t BodySpriteIndex { get; set; } public uint8_t var_05 { get; set; } - public bool Validate() => true; + public IEnumerable Validate(ValidationContext validationContext) + => []; } diff --git a/Definitions/ObjectModels/Objects/Wall/WallObject.cs b/Definitions/ObjectModels/Objects/Wall/WallObject.cs index 05dd813b..95968ac0 100644 --- a/Definitions/ObjectModels/Objects/Wall/WallObject.cs +++ b/Definitions/ObjectModels/Objects/Wall/WallObject.cs @@ -1,3 +1,5 @@ +using System.ComponentModel.DataAnnotations; + namespace Definitions.ObjectModels.Objects.Wall; public class WallObject : ILocoStruct @@ -7,6 +9,6 @@ public class WallObject : ILocoStruct public WallObjectFlags1 Flags1 { get; set; } = WallObjectFlags1.None; public WallObjectFlags2 Flags2 { get; set; } = WallObjectFlags2.None; // unused in loco??? - public bool Validate() - => true; + public IEnumerable Validate(ValidationContext validationContext) + => []; } diff --git a/Definitions/ObjectModels/Objects/Water/WaterObject.cs b/Definitions/ObjectModels/Objects/Water/WaterObject.cs index 08b09b4f..4fa59a96 100644 --- a/Definitions/ObjectModels/Objects/Water/WaterObject.cs +++ b/Definitions/ObjectModels/Objects/Water/WaterObject.cs @@ -1,3 +1,5 @@ +using System.ComponentModel.DataAnnotations; + namespace Definitions.ObjectModels.Objects.Water; public class WaterObject : ILocoStruct @@ -6,18 +8,16 @@ public class WaterObject : ILocoStruct public uint8_t var_03 { get; set; } public int16_t CostFactor { get; set; } - public bool Validate() + public IEnumerable Validate(ValidationContext validationContext) { if (CostIndex > 32) { - return false; + yield return new ValidationResult($"{nameof(CostIndex)} must be between 0 and 32.", [nameof(CostIndex)]); } if (CostFactor <= 0) { - return false; + yield return new ValidationResult($"{nameof(CostFactor)} must be positive.", [nameof(CostFactor)]); } - - return true; } } diff --git a/Gui/ViewModels/LocoTypes/LocoObjectViewModel.cs b/Gui/ViewModels/LocoTypes/LocoObjectViewModel.cs index 8821bf3e..68157f96 100644 --- a/Gui/ViewModels/LocoTypes/LocoObjectViewModel.cs +++ b/Gui/ViewModels/LocoTypes/LocoObjectViewModel.cs @@ -7,5 +7,6 @@ public abstract class LocoObjectViewModel : ReactiveObject, IObjectViewModel< { public abstract T GetAsModel(); - ILocoStruct IObjectViewModel.GetAsModel() => GetAsModel(); + ILocoStruct IObjectViewModel.GetAsModel() + => GetAsModel(); } diff --git a/Gui/ViewModels/LocoTypes/Objects/Building/BuildingViewModel.cs b/Gui/ViewModels/LocoTypes/Objects/Building/BuildingViewModel.cs index 0e99a988..1c1de5de 100644 --- a/Gui/ViewModels/LocoTypes/Objects/Building/BuildingViewModel.cs +++ b/Gui/ViewModels/LocoTypes/Objects/Building/BuildingViewModel.cs @@ -1,5 +1,6 @@ using Dat.Loaders; using Definitions.ObjectModels.Objects.Building; +using Definitions.ObjectModels.Objects.Common; using Definitions.ObjectModels.Types; using PropertyModels.ComponentModel.DataAnnotations; using ReactiveUI.Fody.Helpers; @@ -28,10 +29,9 @@ public class BuildingViewModel : LocoObjectViewModel [Category("Production"), Length(0, BuildingObjectLoader.Constants.MaxProducedCargoType)] public ObservableCollection RequiredCargo { get; set; } [Category("Production"), Length(1, BuildingObjectLoader.Constants.MaxProducedCargoType)] public ObservableCollection ProducedQuantity { get; set; } - //[Category("Building"), Length(1, BuildingObjectLoader.Constants.BuildingVariationCount)] public List> BuildingVariations { get; set; } // NumBuildingVariations - //[Category("Building"), Length(1, BuildingObjectLoader.Constants.BuildingHeightCount)] public List BuildingHeights { get; set; } // NumBuildingParts - //[Category("Building"), Length(1, BuildingObjectLoader.Constants.BuildingAnimationCount)] public List BuildingAnimations { get; set; } // NumBuildingParts - //[Browsable(false)] public BuildingComponentsViewModel BuildingComponents { get; set; } + [Category("Building"), Length(1, AirportObjectLoader.Constants.BuildingVariationCount)] public ObservableCollection> BuildingVariations { get; set; } // NumBuildingVariations + [Category("Building"), Length(1, AirportObjectLoader.Constants.BuildingHeightCount)] public ObservableCollection BuildingHeights { get; set; } // NumBuildingParts + [Category("Building"), Length(1, AirportObjectLoader.Constants.BuildingAnimationCount)] public ObservableCollection BuildingAnimations { get; set; } // NumBuildingParts // note: these height sequences are massive. BLDCTY28 has 2 sequences, 512 in length and 1024 in length. Avalonia PropertyGrid takes 30+ seconds to render this. todo: don't use property grid in future //[Reactive, Category("Building"), Length(1, BuildingObject.MaxElevatorHeightSequences), Browsable(false)] public BindingList> ElevatorHeightSequences { get; set; } // NumElevatorSequences @@ -63,9 +63,9 @@ public BuildingViewModel(BuildingObject bo) ProducedCargo = [.. bo.ProducedCargo.ConvertAll(x => new ObjectModelHeaderViewModel(x))]; RequiredCargo = [.. bo.RequiredCargo.ConvertAll(x => new ObjectModelHeaderViewModel(x))]; ProducedQuantity = [.. bo.ProducedQuantity]; - //BuildingAnimations = [.. bo.BuildingComponents.BuildingAnimations]; - //BuildingHeights = [.. bo.BuildingComponents.BuildingHeights]; - //BuildingVariations = [.. bo.BuildingComponents.BuildingVariations.Select(x => new List(x))]; + BuildingHeights = new(bo.BuildingComponents.BuildingHeights); + BuildingAnimations = new(bo.BuildingComponents.BuildingAnimations); + BuildingVariations = new(bo.BuildingComponents.BuildingVariations.Select(x => new ObservableCollection(x))); ElevatorSequence1 = bo.ElevatorHeightSequences.Count > 0 ? bo.ElevatorHeightSequences[0] : null; ElevatorSequence2 = bo.ElevatorHeightSequences.Count > 1 ? bo.ElevatorHeightSequences[1] : null; ElevatorSequence3 = bo.ElevatorHeightSequences.Count > 2 ? bo.ElevatorHeightSequences[2] : null; @@ -83,12 +83,12 @@ public BuildingViewModel(BuildingObject bo) public override BuildingObject GetAsModel() => new() { - //BuildingComponents = new BuildingComponentsModel() - //{ - // BuildingHeights = [.. BuildingHeights], - // BuildingAnimations = [.. BuildingAnimations], - // BuildingVariations = BuildingVariations.ToList().ConvertAll(x => x.ToList()), - //}, + BuildingComponents = new BuildingComponentsModel() + { + BuildingHeights = [.. BuildingHeights], + BuildingAnimations = [.. BuildingAnimations], + BuildingVariations = BuildingVariations.ToList().ConvertAll(x => x.ToList()), + }, Flags = Flags, Colours = Colours, ScaffoldingColour = ScaffoldingColour, diff --git a/Tests/G1Tests.cs b/Tests/G1Tests.cs index 54086cc1..e318031a 100644 --- a/Tests/G1Tests.cs +++ b/Tests/G1Tests.cs @@ -11,10 +11,12 @@ namespace Dat.Tests; public class G1Tests { readonly ILogger Logger = new Logger(); - const string g1File = "Q:\\Games\\Locomotion\\G1\\g1.dat"; + const string Steam_G1 = "Q:\\Games\\Locomotion\\G1\\steam-g1.dat"; // todo: check both steam and gog + const string GoG_G1 = "Q:\\Games\\Locomotion\\G1\\steam-g1.dat"; // todo: check both steam and gog - [Test] - public void LoadSaveLoadG1() + [TestCase(Steam_G1)] + [TestCase(GoG_G1)] + public void LoadSaveLoadG1(string g1File) { var g1 = SawyerStreamReader.LoadG1(g1File, Logger); ArgumentNullException.ThrowIfNull(g1); @@ -43,13 +45,19 @@ public void LoadSaveLoadG1() // These images have RLE runs/segment lengths > 127, which require special handling in the encode // method. I split these out to initially debug why they weren't working. The code now works but // I will leave these tests in as they serve as a kind of documentation of this quirk of the g1 encoding. - [TestCase(3539)] - [TestCase(3540)] - [TestCase(3541)] - [TestCase(3542)] - [TestCase(3618)] - [TestCase(3619)] - public void LoadSaveLoadG1_RLERunsGreaterThan127(int element) + [TestCase(Steam_G1, 3539)] + [TestCase(Steam_G1, 3540)] + [TestCase(Steam_G1, 3541)] + [TestCase(Steam_G1, 3542)] + [TestCase(Steam_G1, 3618)] + [TestCase(Steam_G1, 3619)] + [TestCase(GoG_G1, 3539)] + [TestCase(GoG_G1, 3540)] + [TestCase(GoG_G1, 3541)] + [TestCase(GoG_G1, 3542)] + [TestCase(GoG_G1, 3618)] + [TestCase(GoG_G1, 3619)] + public void LoadSaveLoadG1_RLERunsGreaterThan127(string g1File, int element) { var g1 = SawyerStreamReader.LoadG1(g1File, Logger); var d1 = g1!.ImageTable.GraphicsElements[element]; From 31a8b0dbcd119daea5c1d0bc89d099612a413529 Mon Sep 17 00:00:00 2001 From: Benjamin Sutas Date: Thu, 18 Sep 2025 12:46:55 +1000 Subject: [PATCH 20/21] ensure unit tests run for gog and steam g1.dat --- Tests/G1Tests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/G1Tests.cs b/Tests/G1Tests.cs index e318031a..0f0122ff 100644 --- a/Tests/G1Tests.cs +++ b/Tests/G1Tests.cs @@ -12,7 +12,7 @@ public class G1Tests { readonly ILogger Logger = new Logger(); const string Steam_G1 = "Q:\\Games\\Locomotion\\G1\\steam-g1.dat"; // todo: check both steam and gog - const string GoG_G1 = "Q:\\Games\\Locomotion\\G1\\steam-g1.dat"; // todo: check both steam and gog + const string GoG_G1 = "Q:\\Games\\Locomotion\\G1\\gog-g1.dat"; // todo: check both steam and gog [TestCase(Steam_G1)] [TestCase(GoG_G1)] From 02b463b6213e680014a9d58e6ca7cc806cf675d3 Mon Sep 17 00:00:00 2001 From: Benjamin Sutas Date: Thu, 18 Sep 2025 13:16:12 +1000 Subject: [PATCH 21/21] add costindex query --- DataSanitiser/Program.cs | 155 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 145 insertions(+), 10 deletions(-) diff --git a/DataSanitiser/Program.cs b/DataSanitiser/Program.cs index ed983674..d1d41893 100644 --- a/DataSanitiser/Program.cs +++ b/DataSanitiser/Program.cs @@ -1,19 +1,154 @@ -using Microsoft.EntityFrameworkCore; using Common.Logging; +using Dat.Data; using Dat.FileParsing; using Definitions.Database; +using Definitions.ObjectModels; +using Definitions.ObjectModels.Objects.Airport; +using Definitions.ObjectModels.Objects.Bridge; +using Definitions.ObjectModels.Objects.Building; +using Definitions.ObjectModels.Objects.Cargo; +using Definitions.ObjectModels.Objects.CliffEdge; +using Definitions.ObjectModels.Objects.Climate; +using Definitions.ObjectModels.Objects.Competitor; +using Definitions.ObjectModels.Objects.Currency; +using Definitions.ObjectModels.Objects.Dock; +using Definitions.ObjectModels.Objects.HillShape; +using Definitions.ObjectModels.Objects.Industry; +using Definitions.ObjectModels.Objects.InterfaceSkin; +using Definitions.ObjectModels.Objects.Land; +using Definitions.ObjectModels.Objects.LevelCrossing; +using Definitions.ObjectModels.Objects.Region; +using Definitions.ObjectModels.Objects.Road; +using Definitions.ObjectModels.Objects.RoadExtra; +using Definitions.ObjectModels.Objects.RoadStation; +using Definitions.ObjectModels.Objects.Scaffolding; +using Definitions.ObjectModels.Objects.ScenarioText; +using Definitions.ObjectModels.Objects.Snow; +using Definitions.ObjectModels.Objects.Sound; +using Definitions.ObjectModels.Objects.Steam; +using Definitions.ObjectModels.Objects.Streetlight; +using Definitions.ObjectModels.Objects.TownNames; +using Definitions.ObjectModels.Objects.Track; +using Definitions.ObjectModels.Objects.TrackExtra; +using Definitions.ObjectModels.Objects.TrackSignal; +using Definitions.ObjectModels.Objects.TrackStation; +using Definitions.ObjectModels.Objects.Tree; +using Definitions.ObjectModels.Objects.Tunnel; +using Definitions.ObjectModels.Objects.Vehicle; +using Definitions.ObjectModels.Objects.Wall; +using Definitions.ObjectModels.Objects.Water; +using Definitions.ObjectModels.Types; +using Index; +using Microsoft.EntityFrameworkCore; using System.IO.Hashing; using System.Reflection; -using Index; -using Definitions.ObjectModels.Types; -using Definitions.ObjectModels.Objects.Industry; using IndustryObject = Definitions.ObjectModels.Objects.Industry.IndustryObject; -using Definitions.ObjectModels.Objects.Vehicle; using VehicleObject = Definitions.ObjectModels.Objects.Vehicle.VehicleObject; -using Dat.Data; -using Definitions.ObjectModels.Objects.Cargo; -using Definitions.ObjectModels.Objects.TrackStation; -using Definitions.ObjectModels.Objects.Track; + +static ObjectType TypeToStruct(Type type) + => type switch + { + var t when t == typeof(AirportObject) => ObjectType.Airport, + var t when t == typeof(BridgeObject) => ObjectType.Bridge, + var t when t == typeof(BuildingObject) => ObjectType.Building, + var t when t == typeof(CargoObject) => ObjectType.Cargo, + var t when t == typeof(CliffEdgeObject) => ObjectType.CliffEdge, + var t when t == typeof(ClimateObject) => ObjectType.Climate, + var t when t == typeof(CompetitorObject) => ObjectType.Competitor, + var t when t == typeof(CurrencyObject) => ObjectType.Currency, + var t when t == typeof(DockObject) => ObjectType.Dock, + var t when t == typeof(HillShapesObject) => ObjectType.HillShapes, + var t when t == typeof(IndustryObject) => ObjectType.Industry, + var t when t == typeof(InterfaceSkinObject) => ObjectType.InterfaceSkin, + var t when t == typeof(LandObject) => ObjectType.Land, + var t when t == typeof(LevelCrossingObject) => ObjectType.LevelCrossing, + var t when t == typeof(RegionObject) => ObjectType.Region, + var t when t == typeof(RoadExtraObject) => ObjectType.RoadExtra, + var t when t == typeof(RoadObject) => ObjectType.Road, + var t when t == typeof(RoadStationObject) => ObjectType.RoadStation, + var t when t == typeof(ScaffoldingObject) => ObjectType.Scaffolding, + var t when t == typeof(ScenarioTextObject) => ObjectType.ScenarioText, + var t when t == typeof(SnowObject) => ObjectType.Snow, + var t when t == typeof(SoundObject) => ObjectType.Sound, + var t when t == typeof(SteamObject) => ObjectType.Steam, + var t when t == typeof(StreetLightObject) => ObjectType.StreetLight, + var t when t == typeof(TownNamesObject) => ObjectType.TownNames, + var t when t == typeof(TrackExtraObject) => ObjectType.TrackExtra, + var t when t == typeof(TrackObject) => ObjectType.Track, + var t when t == typeof(TrackSignalObject) => ObjectType.TrackSignal, + var t when t == typeof(TrackStationObject) => ObjectType.TrackStation, + var t when t == typeof(TreeObject) => ObjectType.Tree, + var t when t == typeof(TunnelObject) => ObjectType.Tunnel, + var t when t == typeof(VehicleObject) => ObjectType.Vehicle, + var t when t == typeof(WallObject) => ObjectType.Wall, + var t when t == typeof(WaterObject) => ObjectType.Water, + _ => throw new ArgumentOutOfRangeException(nameof(type), $"unknown struct type {type.FullName}") + }; + +static void QueryCostIndex() +{ + var dir = "Q:\\Games\\Locomotion\\Server\\Objects"; + var logger = new Logger(); + var index = ObjectIndex.LoadOrCreateIndex(dir, logger); + //var index = ObjectIndex.CreateIndex(dir, logger); + //index.SaveIndexAsync(Path.Combine(dir, "objectIndex.json")).Wait(); + + var results = new List<(ObjectIndexEntry Obj, ObjectSource ObjectSource, byte CostIndex)>(); + + // Pseudocode plan: + // 1. Get all loaded assemblies (or just the relevant ones, e.g., Assembly.GetExecutingAssembly()). + // 2. For each type, check if it's a class, not abstract, implements ILocoStruct, and has a property named "CostIndex". + // 3. Iterate over these types in a foreach loop. + + var locoStructTypesWithCostIndex = AppDomain.CurrentDomain.GetAssemblies() + .SelectMany(a => a.GetTypes()) + .Where(t => + t.IsClass && + !t.IsAbstract && + typeof(ILocoStruct).IsAssignableFrom(t) && + t.GetProperty("CostIndex", BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase) != null) + .Select(TypeToStruct) + .ToHashSet(); + + foreach (var type in locoStructTypesWithCostIndex) + { + Console.WriteLine($"Type: {type} implements ILocoStruct and has a CostIndex property."); + } + + //foreach (var obj in index.Objects.Where(x => x.ObjectSource is ObjectSource.LocomotionSteam or ObjectSource.LocomotionGoG && locoStructTypesWithCostIndex.Contains(x.ObjectType))) + + foreach (var obj in index.Objects.Where(x => locoStructTypesWithCostIndex.Contains(x.ObjectType))) + { + try + { + var o = SawyerStreamReader.LoadFullObject(Path.Combine(dir, obj.FileName), logger); + if (o.LocoObject != null) + { + var type = o.LocoObject.Object.GetType(); + var costIndexProperty = type.GetProperty("CostIndex", BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance); + + var costIndex = (byte)costIndexProperty.GetValue(o.LocoObject.Object); + + var header = o.DatFileInfo.S5Header; + var source = OriginalObjectFiles.GetFileSource(header.Name, header.Checksum); + + results.Add((obj, source, costIndex)); + } + } + catch (Exception ex) + { + Console.WriteLine($"{obj.FileName} - {ex.Message}"); + } + } + + Console.WriteLine(results.Count); + foreach (var result in results.OrderBy(x => x.CostIndex)) + { + // Print object index entry and its enabled flags + Console.WriteLine($"{result.Obj.DisplayName} - {result.ObjectSource} - CostIndex={result.CostIndex}"); + } +} +QueryCostIndex(); static void QueryTrackStationOneSidedTrack() { @@ -75,7 +210,7 @@ static void QueryTrackStationOneSidedTrack() Console.WriteLine($"{result.Obj.DisplayName} - Checksum: 0x{result.Obj.DatChecksum:X} - Source: {result.ObjectSource} - Flags: {string.Join('|', result.Flags)}"); } } -QueryTrackStationOneSidedTrack(); +//QueryTrackStationOneSidedTrack(); static void QueryIndustryHasShadows() {