diff --git a/DatFileRenamer/Program.cs b/DatFileRenamer/Program.cs index 6d799025..128c0edd 100644 --- a/DatFileRenamer/Program.cs +++ b/DatFileRenamer/Program.cs @@ -1,6 +1,4 @@ -using System.Diagnostics; -using System.IO; -using System.Reflection; +using System.Reflection; Console.WriteLine("Dat File Renamer v0.1"); diff --git a/OpenLocoTool/DatFileParsing/AttributeHelper.cs b/OpenLocoTool/DatFileParsing/AttributeHelper.cs index a6e828dc..901f053c 100644 --- a/OpenLocoTool/DatFileParsing/AttributeHelper.cs +++ b/OpenLocoTool/DatFileParsing/AttributeHelper.cs @@ -12,5 +12,11 @@ public static class AttributeHelper public static T? Get(Type t) where T : Attribute => t.GetCustomAttribute(typeof(T), inherit: false) as T; + + public static bool Has(PropertyInfo p) where T : Attribute + => p.GetCustomAttribute(typeof(T), inherit: false) is T; + + public static IEnumerable GetAllPropertiesWithAttribute(Type t) where T : Attribute + => t.GetProperties().Where(Has); } } diff --git a/OpenLocoTool/DatFileParsing/ByteWriter.cs b/OpenLocoTool/DatFileParsing/ByteWriter.cs index 9c650448..8afe1919 100644 --- a/OpenLocoTool/DatFileParsing/ByteWriter.cs +++ b/OpenLocoTool/DatFileParsing/ByteWriter.cs @@ -36,6 +36,8 @@ public static void WriteT(Span data, Type t, int offset, object val) else if (t == typeof(string_id)) { + // string ids should always be 0 in the dat file - they're only set when loaded into memory and never saved + val = 0; ByteWriterT.Write(data, offset, (string_id)(dynamic)val); } @@ -77,14 +79,6 @@ public static void WriteT(Span data, Type t, int offset, object val) } } - public static ReadOnlySpan Bytes(object obj) - { - // todo: this just a skeleton placeholder method - // we need to implement Save() for image table and G1 data - throw new NotImplementedException(); - return new byte[1]; - } - public static ReadOnlySpan WriteLocoStruct(ILocoStruct obj) { if (obj == null) diff --git a/OpenLocoTool/DatFileParsing/G1Dat.cs b/OpenLocoTool/DatFileParsing/G1Dat.cs new file mode 100644 index 00000000..ca01b0e7 --- /dev/null +++ b/OpenLocoTool/DatFileParsing/G1Dat.cs @@ -0,0 +1,12 @@ +using System.ComponentModel; +using OpenLocoTool.Headers; + +namespace OpenLocoTool.DatFileParsing +{ + [TypeConverter(typeof(ExpandableObjectConverter))] + public class G1Dat(G1Header g1Header, List g1Elements) + { + public G1Header G1Header { get; set; } = g1Header; + public List G1Elements { get; set; } = g1Elements; + } +} diff --git a/OpenLocoTool/DatFileParsing/ILocoObject.cs b/OpenLocoTool/DatFileParsing/ILocoObject.cs new file mode 100644 index 00000000..e0b47280 --- /dev/null +++ b/OpenLocoTool/DatFileParsing/ILocoObject.cs @@ -0,0 +1,16 @@ +using System.ComponentModel; +using OpenLocoTool.Headers; + +namespace OpenLocoTool.DatFileParsing +{ + [TypeConverter(typeof(ExpandableObjectConverter))] + public interface ILocoObject + { + S5Header S5Header { get; set; } + ObjectHeader ObjectHeader { get; set; } + ILocoStruct Object { get; set; } + StringTable StringTable { get; set; } + G1Header G1Header { get; set; } + List G1Elements { get; set; } + } +} diff --git a/OpenLocoTool/DatFileParsing/ILocoStruct.cs b/OpenLocoTool/DatFileParsing/ILocoStruct.cs new file mode 100644 index 00000000..565d13d3 --- /dev/null +++ b/OpenLocoTool/DatFileParsing/ILocoStruct.cs @@ -0,0 +1,55 @@ +using System.ComponentModel; + +namespace OpenLocoTool.DatFileParsing +{ + /* + === Dat File Format === + |-File-------------------------------| + |-S5Header-|-DatHeader--|-ObjectData-| + + ============================================================================================================== + + |-S5Header----------------| + |-Flags-|-Name-|-Checksum-| + + |-DatHeader-------------| + |-Encoding-|-Datalength-| + + |-ObjectData-----------------------------------------| + |-Object-|-StringTable-|-VariableData-|-GraphicsData-| + + ============================================================================================================== + + |-Object-| + -- per-object + + |-StringTable-| + |-String{n}---| + + |-VariableData-| + -- per-object + + |-GraphicsData------------------------------| + |-G1Header-|-G1Element32{n}-|-ImageBytes{n}-| + + ============================================================================================================== + + |-String-----------------| + |-Language-|-StringBytes-| + + |-G1Header---------------| + |-NumEntries-|-TotalSize-| + + |-G1Element32------------------------------------------------------| + |-Offset-|-Width-|-Height-|-xOffset-|-yOffset-|-Flags-|-ZoomOffset-| + + |-ImageBytes-| + -- offset by G1Element32.Offset + */ + + [TypeConverter(typeof(ExpandableObjectConverter))] + public interface ILocoStruct + { + static int StructSize { get; } + } +} diff --git a/OpenLocoTool/DatFileParsing/ILocoStructStringTablePostLoad.cs b/OpenLocoTool/DatFileParsing/ILocoStructStringTablePostLoad.cs new file mode 100644 index 00000000..24047b87 --- /dev/null +++ b/OpenLocoTool/DatFileParsing/ILocoStructStringTablePostLoad.cs @@ -0,0 +1,10 @@ +using System.ComponentModel; + +namespace OpenLocoTool.DatFileParsing +{ + [TypeConverter(typeof(ExpandableObjectConverter))] + public interface ILocoStructStringTablePostLoad + { + void LoadPostStringTable(StringTable stringTable); + } +} diff --git a/OpenLocoTool/DatFileParsing/ILocoStructVariableData.cs b/OpenLocoTool/DatFileParsing/ILocoStructVariableData.cs new file mode 100644 index 00000000..63ed9148 --- /dev/null +++ b/OpenLocoTool/DatFileParsing/ILocoStructVariableData.cs @@ -0,0 +1,12 @@ +using System.ComponentModel; + +namespace OpenLocoTool.DatFileParsing +{ + [TypeConverter(typeof(ExpandableObjectConverter))] + public interface ILocoStructVariableData + { + ReadOnlySpan Load(ReadOnlySpan remainingData); + + ReadOnlySpan Save(); + } +} diff --git a/OpenLocoTool/DatFileParsing/LocoAttributes.cs b/OpenLocoTool/DatFileParsing/LocoAttributes.cs index 5f1d95b1..dd56d965 100644 --- a/OpenLocoTool/DatFileParsing/LocoAttributes.cs +++ b/OpenLocoTool/DatFileParsing/LocoAttributes.cs @@ -18,14 +18,6 @@ public class LocoStructSizeAttribute(int size) : Attribute public int Size => size; } - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false)] - public class LocoStringTableAttribute(params string[] names) : Attribute - { - public string[] Names => names; - - public int Count => Names.Length; - } - // basically a 'skip' attribute to allow deferred loading for variable data [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property, AllowMultiple = false)] public class LocoStructVariableLoadAttribute : Attribute @@ -35,4 +27,9 @@ public class LocoStructVariableLoadAttribute : Attribute [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property, AllowMultiple = false)] public class LocoStructSkipReadAttribute : Attribute { } + + // basically a 'skip' attribute to allow deferred loading for variable data, and writing of this property will be 0 + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property, AllowMultiple = false)] + public class LocoStringAttribute : Attribute + { } } diff --git a/OpenLocoTool/DatFileParsing/LocoObject.cs b/OpenLocoTool/DatFileParsing/LocoObject.cs index 4d6e6c0b..41c041ef 100644 --- a/OpenLocoTool/DatFileParsing/LocoObject.cs +++ b/OpenLocoTool/DatFileParsing/LocoObject.cs @@ -1,139 +1,56 @@ using System.ComponentModel; using OpenLocoTool.Headers; -using OpenLocoTool.Objects; namespace OpenLocoTool.DatFileParsing { /* - === Dat File Format === - |-File-------------------------------| - |-S5Header-|-DatHeader--|-ObjectData-| +=== Dat File Format === +|-File-------------------------------| +|-S5Header-|-DatHeader--|-ObjectData-| - ============================================================================================================== +============================================================================================================== - |-S5Header----------------| - |-Flags-|-Name-|-Checksum-| +|-S5Header----------------| +|-Flags-|-Name-|-Checksum-| - |-DatHeader-------------| - |-Encoding-|-Datalength-| +|-DatHeader-------------| +|-Encoding-|-Datalength-| - |-ObjectData-----------------------------------------| - |-Object-|-StringTable-|-VariableData-|-GraphicsData-| +|-ObjectData-----------------------------------------| +|-Object-|-StringTable-|-VariableData-|-GraphicsData-| - ============================================================================================================== +============================================================================================================== - |-Object-| - -- per-object +|-Object-| +-- per-object - |-StringTable-| - |-String{n}---| +|-StringTable-| +|-String{n}---| - |-VariableData-| - -- per-object +|-VariableData-| +-- per-object - |-GraphicsData------------------------------| - |-G1Header-|-G1Element32{n}-|-ImageBytes{n}-| +|-GraphicsData------------------------------| +|-G1Header-|-G1Element32{n}-|-ImageBytes{n}-| - ============================================================================================================== +============================================================================================================== - |-String-----------------| - |-Language-|-StringBytes-| +|-String-----------------| +|-Language-|-StringBytes-| - |-G1Header---------------| - |-Numentries-|-Totalsize-| +|-G1Header---------------| +|-NumEntries-|-TotalSize-| - |-G1Element32------------------------------------------------------| - |-Offset-|-Width-|-Height-|-xOffset-|-yOffset-|-Flags-|-ZoomOffset-| - - |-ImageBytes-| - -- offset by G1Element32.Offset - */ - - [TypeConverter(typeof(ExpandableObjectConverter))] - public interface ILocoStruct - { - static int StructSize { get; } - } - - [TypeConverter(typeof(ExpandableObjectConverter))] - public interface ILocoStructVariableData - { - ReadOnlySpan Load(ReadOnlySpan remainingData); - - ReadOnlySpan Save(); - } - - [TypeConverter(typeof(ExpandableObjectConverter))] - public interface ILocoStructStringTablePostLoad - { - void LoadPostStringTable(StringTable stringTable); - } - - [TypeConverter(typeof(ExpandableObjectConverter))] - public interface ILocoObject - { - S5Header S5Header { get; set; } - ObjectHeader ObjectHeader { get; set; } - ILocoStruct Object { get; set; } - StringTable StringTable { get; set; } - G1Header G1Header { get; set; } - List G1Elements { get; set; } - } - - [TypeConverter(typeof(ExpandableObjectConverter))] - public class G1Dat(G1Header g1Header, List g1Elements) - { - public G1Header G1Header { get; set; } = g1Header; - public List G1Elements { get; set; } = g1Elements; - } - - public static class ObjectTypeFixedSize - { - public static int GetSize(ObjectType objectType) - => objectType switch - { - ObjectType.Airport => AirportObject.StructSize, - ObjectType.Bridge => BridgeObject.StructSize, - ObjectType.Building => BuildingObject.StructSize, - ObjectType.Cargo => CargoObject.StructSize, - ObjectType.CliffEdge => CliffEdgeObject.StructSize, - ObjectType.Climate => ClimateObject.StructSize, - ObjectType.Competitor => CompetitorObject.StructSize, - ObjectType.Currency => CurrencyObject.StructSize, - ObjectType.Dock => DockObject.StructSize, - ObjectType.HillShapes => HillShapesObject.StructSize, - ObjectType.Industry => IndustryObject.StructSize, - ObjectType.InterfaceSkin => InterfaceSkinObject.StructSize, - ObjectType.Land => LandObject.StructSize, - ObjectType.LevelCrossing => LevelCrossingObject.StructSize, - ObjectType.Region => RegionObject.StructSize, - ObjectType.RoadExtra => RoadExtraObject.StructSize, - ObjectType.Road => RoadObject.StructSize, - ObjectType.RoadStation => RoadStationObject.StructSize, - ObjectType.Scaffolding => ScaffoldingObject.StructSize, - ObjectType.ScenarioText => ScenarioTextObject.StructSize, - ObjectType.Snow => SnowObject.StructSize, - ObjectType.Sound => SoundObject.StructSize, - ObjectType.Steam => SteamObject.StructSize, - ObjectType.StreetLight => StreetLightObject.StructSize, - ObjectType.TownNames => TownNamesObject.StructSize, - ObjectType.TrackExtra => TrackExtraObject.StructSize, - ObjectType.Track => TrackObject.StructSize, - ObjectType.TrainSignal => TrainSignalObject.StructSize, - ObjectType.TrainStation => TrainStationObject.StructSize, - ObjectType.Tree => TreeObject.StructSize, - ObjectType.Tunnel => TunnelObject.StructSize, - ObjectType.Vehicle => VehicleObject.StructSize, - ObjectType.Wall => WallObject.StructSize, - ObjectType.Water => WaterObject.StructSize, - _ => throw new ArgumentOutOfRangeException(nameof(objectType), $"unknown object type {objectType}") - }; - } +|-G1Element32------------------------------------------------------| +|-Offset-|-Width-|-Height-|-xOffset-|-yOffset-|-Flags-|-ZoomOffset-| +|-ImageBytes-| +-- offset by G1Element32.Offset +*/ [TypeConverter(typeof(ExpandableObjectConverter))] public class LocoObject : ILocoObject { - public LocoObject(S5Header s5Hdr, ObjectHeader objHdr, ILocoStruct obj, StringTable stringTable, G1Header g1Header, List g1Elements) + public LocoObject(S5Header s5Hdr, ObjectHeader objHdr, ILocoStruct obj, StringTable stringTable, G1Header? g1Header, List? g1Elements) { S5Header = s5Hdr; ObjectHeader = objHdr; diff --git a/OpenLocoTool/DatFileParsing/ObjectAnnotator.cs b/OpenLocoTool/DatFileParsing/ObjectAnnotator.cs index ea054ad1..608367e7 100644 --- a/OpenLocoTool/DatFileParsing/ObjectAnnotator.cs +++ b/OpenLocoTool/DatFileParsing/ObjectAnnotator.cs @@ -142,10 +142,11 @@ public static int AnnotateStringTable(byte[] fullData, int runningCount, ILocoSt var root = new Annotation("String Table", runningCount, 1); annotations.Add(root); - var stringAttr = locoStruct.GetType().GetCustomAttribute(typeof(LocoStringTableAttribute), inherit: false) as LocoStringTableAttribute; - var stringsInTable = stringAttr?.Count ?? 0; + var stringAttributes = AttributeHelper.GetAllPropertiesWithAttribute(locoStruct.GetType()); + //var stringsInTable = stringAttributes?.Count ?? 0; - for (var i = 0; i < stringsInTable; i++) + var i = 0; + foreach (var locoString in stringAttributes) { var index = 0; var continuing = true; @@ -166,7 +167,7 @@ public static int AnnotateStringTable(byte[] fullData, int runningCount, ILocoSt while (continuing); var endIndexOfStringList = index + runningCount; - var elementRoot = new Annotation("Element " + i, root, runningCount, index); + var elementRoot = new Annotation("Element " + i++, root, runningCount, index); annotations.Add(elementRoot); do diff --git a/OpenLocoTool/DatFileParsing/SawyerStreamReader.cs b/OpenLocoTool/DatFileParsing/SawyerStreamReader.cs index ae25f68e..b4e6a3f8 100644 --- a/OpenLocoTool/DatFileParsing/SawyerStreamReader.cs +++ b/OpenLocoTool/DatFileParsing/SawyerStreamReader.cs @@ -1,6 +1,5 @@ using System.Diagnostics; using System.Numerics; -using System.Reflection; using System.Text; using OpenLocoTool.Headers; using OpenLocoTool.Objects; @@ -30,14 +29,32 @@ static uint32_t ComputeChecksum(ReadOnlySpan data, uint32_t seed) return checksum; } - public G1Dat LoadG1(string filename) + public static G1Dat LoadG1(string filename, ILogger? logger = null) { ReadOnlySpan fullData = LoadBytesFromFile(filename); var (g1Header, imageTable, imageTableBytesRead) = LoadImageTable(fullData); - logger.Info($"FileLength={new FileInfo(filename).Length} NumEntries={g1Header.NumEntries} TotalSize={g1Header.TotalSize} ImageTableLength={imageTableBytesRead}"); + logger?.Info($"FileLength={new FileInfo(filename).Length} NumEntries={g1Header.NumEntries} TotalSize={g1Header.TotalSize} ImageTableLength={imageTableBytesRead}"); return new G1Dat(g1Header, imageTable); } + // load file + public static byte[] LoadDecode(string filename) + { + ReadOnlySpan fullData = LoadBytesFromFile(filename); + + // make openlocotool useful objects + var s5Header = S5Header.Read(fullData[0..S5Header.StructLength]); + var remainingData = fullData[S5Header.StructLength..]; + + var objectHeader = ObjectHeader.Read(remainingData[0..ObjectHeader.StructLength]); + remainingData = remainingData[ObjectHeader.StructLength..]; + + var decodedData = Decode(objectHeader.Encoding, remainingData); + remainingData = decodedData; + + return [.. fullData[0..(S5Header.StructLength + ObjectHeader.StructLength)], .. decodedData]; + } + // load file public static ILocoObject LoadFull(string filename, ILogger? logger = null, bool loadExtra = true) { @@ -54,6 +71,7 @@ public static ILocoObject LoadFull(string filename, ILogger? logger = null, bool var decodedData = Decode(objectHeader.Encoding, remainingData); remainingData = decodedData; + var locoStruct = GetLocoStruct(s5Header.ObjectType, remainingData); if (locoStruct == null) @@ -89,11 +107,20 @@ public static ILocoObject LoadFull(string filename, ILogger? logger = null, bool remainingData = locoStructExtra.Load(remainingData); } - // some objects have graphics data - var (g1Header, imageTable, imageTableBytesRead) = LoadImageTable(remainingData); - logger?.Info($"FileLength={new FileInfo(filename).Length} HeaderLength={S5Header.StructLength} DataLength={objectHeader.DataLength} StringTableLength={stringTableBytesRead} ImageTableLength={imageTableBytesRead}"); + LocoObject newObj = null; + try + { + // some objects have graphics data + var (g1Header, imageTable, imageTableBytesRead) = LoadImageTable(remainingData); + logger?.Info($"FileLength={new FileInfo(filename).Length} HeaderLength={S5Header.StructLength} DataLength={objectHeader.DataLength} StringTableLength={stringTableBytesRead} ImageTableLength={imageTableBytesRead}"); - var newObj = new LocoObject(s5Header, objectHeader, locoStruct, stringTable, g1Header, imageTable); + newObj = new LocoObject(s5Header, objectHeader, locoStruct, stringTable, g1Header, imageTable); + } + catch (Exception ex) + { + newObj = new LocoObject(s5Header, objectHeader, locoStruct, stringTable); + logger?.Error(ex, "Error loading graphics table"); + } // add to object manager SObjectManager.Add(newObj); @@ -104,17 +131,18 @@ public static ILocoObject LoadFull(string filename, ILogger? logger = null, bool static (StringTable table, int bytesRead) LoadStringTable(ReadOnlySpan data, ILocoStruct locoStruct) { var stringTable = new StringTable(); + var stringAttributes = AttributeHelper.GetAllPropertiesWithAttribute(locoStruct.GetType()); - if (data.Length == 0 || locoStruct.GetType().GetCustomAttribute(typeof(LocoStringTableAttribute), inherit: false) is not LocoStringTableAttribute stringTableAttr || stringTableAttr.Count == 0) + if (data.Length == 0 || !stringAttributes.Any()) { return (stringTable, 0); } var ptr = 0; - for (var i = 0; i < stringTableAttr.Count; ++i) + foreach (var locoString in stringAttributes) { - var stringName = stringTableAttr.Names[i]; + var stringName = locoString.Name; stringTable.Add(stringName, []); var languageDict = stringTable[stringName]; @@ -126,7 +154,7 @@ public static ILocoObject LoadFull(string filename, ILogger? logger = null, bool while (data[ptr++] != '\0') ; var str = Encoding.ASCII.GetString(data[ini..(ptr - 1)]); // do -1 to exclude the \0 - if (!languageDict.TryAdd(lang, new StringTableEntry { String = str })) + if (!languageDict.TryAdd(lang, str)) //new StringTableEntry { String = str })) { //Logger.Error($"Key {(i, lang)} already exists (this shouldn't happen)"); break; @@ -154,12 +182,11 @@ public static ILocoObject LoadFull(string filename, ILogger? logger = null, bool var g1ElementHeaders = data[8..]; - const int g1Element32Size = 0x10; // todo: lookup from the LocoStructSize attribute - var imageData = g1ElementHeaders[((int)g1Header.NumEntries * g1Element32Size)..]; + var imageData = g1ElementHeaders[((int)g1Header.NumEntries * G1Element32.StructLength)..]; g1Header.ImageData = imageData.ToArray(); for (var i = 0; i < g1Header.NumEntries; ++i) { - var g32ElementData = g1ElementHeaders[(i * g1Element32Size)..((i + 1) * g1Element32Size)]; + var g32ElementData = g1ElementHeaders[(i * G1Element32.StructLength)..((i + 1) * G1Element32.StructLength)]; var g32Element = (G1Element32)ByteReader.ReadLocoStruct(g32ElementData); g1Element32s.Add(g32Element); } @@ -170,7 +197,8 @@ public static ILocoObject LoadFull(string filename, ILogger? logger = null, bool var currElement = g1Element32s[i]; var nextOffset = i < g1Header.NumEntries - 1 ? g1Element32s[i + 1].Offset - : g1Header.TotalSize; + // : g1Header.TotalSize; + : (uint)g1Header.ImageData.Length; currElement.ImageData = imageData[(int)currElement.Offset..(int)nextOffset].ToArray(); @@ -276,7 +304,7 @@ public static byte[] LoadBytesFromFile(string filename) return File.ReadAllBytes(filename); } - public static S5Header LoadHeader(string filename, ILogger? logger) + public static S5Header LoadHeader(string filename, ILogger? logger = null) { if (!File.Exists(filename)) { diff --git a/OpenLocoTool/DatFileParsing/SawyerStreamWriter.cs b/OpenLocoTool/DatFileParsing/SawyerStreamWriter.cs index ed641f27..5a8e9825 100644 --- a/OpenLocoTool/DatFileParsing/SawyerStreamWriter.cs +++ b/OpenLocoTool/DatFileParsing/SawyerStreamWriter.cs @@ -1,145 +1,189 @@ -using OpenLocoToolCommon; +using System.Text; +using OpenLocoTool.Headers; +using OpenLocoToolCommon; namespace OpenLocoTool.DatFileParsing { - public class SawyerStreamWriter(ILogger logger) + public static class SawyerStreamWriter { - public void Save(string filepath, ILocoObject locoObject) + public static void Save(string filepath, ILocoObject locoObject, ILogger? logger = null) { ArgumentNullException.ThrowIfNull(locoObject); - logger.Info($"Writing \"{locoObject.S5Header.Name}\" to {filepath}"); + logger?.Info($"Writing \"{locoObject.S5Header.Name}\" to {filepath}"); var objBytes = WriteLocoObject(locoObject); - // hardcode uncompressed as encoding is currently not working - var encoded = Encode(SawyerEncoding.Uncompressed, objBytes); - //var encoded = Encode(locoObject.ObjectHeader.Encoding, objBytes); - - WriteToFile(filepath, locoObject.S5Header.Write(), locoObject.ObjectHeader.Write(), encoded); + var stream = File.Create(filepath); + stream.Write(objBytes); + stream.Flush(); + stream.Close(); } public static ReadOnlySpan WriteLocoObject(ILocoObject obj) { - var objBytes = ByteWriter.WriteLocoStruct(obj.Object); var ms = new MemoryStream(); - ms.Write(objBytes); - //var stringBytes = Bytes(obj.StringTable); - //ms.Write(stringBytes); + ms.Write(obj.S5Header.Write()); + ms.Write((obj.ObjectHeader with { Encoding = SawyerEncoding.Uncompressed }).Write()); - if (obj.Object is ILocoStructVariableData objV) - { - //var variableBytes = objV.Save(); - //ms.Write(variableBytes); - } + var objBytes = ByteWriter.WriteLocoStruct(obj.Object); + ms.Write(objBytes); - if (obj.G1Header.NumEntries != 0 && obj.G1Elements.Count != 0) + // string table + foreach (var ste in obj.StringTable.table) { - //var g1Bytes = Bytes(obj.G1Header); - //ms.Write(g1Bytes); + foreach (var language in ste.Value) + { + ms.WriteByte((byte)language.Key); - //var g1ElementsBytes = Bytes(obj.G1Elements); - //ms.Write(g1ElementsBytes); + var strBytes = Encoding.ASCII.GetBytes(language.Value); + ms.Write(strBytes, 0, strBytes.Length); + ms.WriteByte((byte)'\0'); + } + ms.WriteByte(0xff); } - ms.Flush(); - ms.Close(); - - return ms.ToArray(); - } - - public ReadOnlySpan Encode(SawyerEncoding encoding, ReadOnlySpan data) - { - switch (encoding) + // variable data + if (obj.Object is ILocoStructVariableData objV) { - case SawyerEncoding.Uncompressed: - return data; - case SawyerEncoding.RunLengthSingle: - return EncodeRunLengthSingle(data); - //case SawyerEncoding.runLengthMulti: - // return encodeRunLengthMulti(decodeRunLengthSingle(data)); - //case SawyerEncoding.rotate: - // return encodeRotate(data); - default: - logger.Error("Unknown chunk encoding scheme"); - throw new InvalidDataException("Unknown encoding"); + var variableBytes = objV.Save(); + ms.Write(variableBytes); } - } - public static void WriteToFile(string filepath, ReadOnlySpan s5Header, ReadOnlySpan objectHeader, ReadOnlySpan encodedData) - { - var stream = File.Create(filepath); - stream.Write(s5Header); - stream.Write(objectHeader); - stream.Write(encodedData); - stream.Flush(); - stream.Close(); - } - - // taken from openloco SawyerStreamReader::encodeRunLengthSingle - // not sure why it doesn't work, but it doesn't work. gets the first 10 or so bytes correct for SIGC3.dat but then fails - private static Span EncodeRunLengthSingle(ReadOnlySpan data) - { - List buffer = []; - var src = 0; // ptr - var srcNormStart = 0; // ptr - var srcEnd = data.Length; - var count = 0; - - while (src < srcEnd - 1) + // graphics data + if (obj.G1Header != null && obj.G1Elements != null && obj.G1Header.NumEntries != 0 && obj.G1Elements.Count != 0) { - if ((count != 0 && data[src] == data[src + 1]) || count > 125) - { - buffer.Add((byte)(count - 1)); - buffer.AddRange(Enumerable.Repeat(data[srcNormStart], count)); - srcNormStart += count; - count = 0; - } + // write G1Header + ms.Write(BitConverter.GetBytes(obj.G1Header.NumEntries)); + ms.Write(BitConverter.GetBytes(obj.G1Elements.Sum(x => G1Element32.StructLength + x.ImageData.Length))); - if (data[src] == data[src + 1]) + int idx = 0; + // write G1Elements + foreach (var g1Element in obj.G1Elements) { - for (; count < 125 && src + count < srcEnd; count++) + // we need to update the offsets of the image data + // and we're not going to compress the data on save, so make sure the RLECompressed flag is not set + var offset = idx < 1 ? 0 : obj.G1Elements[idx - 1].Offset + (uint)obj.G1Elements[idx - 1].ImageData.Length; + var newElement = g1Element with { - if (data[src] != data[count]) - { - break; - } - } - - buffer.Add((byte)(257 - count)); - buffer.Add(data[src]); - src += count; - srcNormStart = src; - count = 0; + Offset = offset, + Flags = g1Element.Flags & ~G1ElementFlags.IsRLECompressed + }; + ms.Write(newElement.Write()); + idx++; } - else + + // write G1Elements ImageData + foreach (var g1Element in obj.G1Elements) { - count++; - src++; + // we're not going to compress the data on save, so make sure the RLECompressed flag is not set + ms.Write(g1Element.ImageData); } } - if (data[src] == data[srcEnd - 1]) - { - count++; - } - - if (count != 0) - { - buffer.Add((byte)(count - 1)); - buffer.AddRange(Enumerable.Repeat(data[srcNormStart], count)); - } + // calculate size and write the size in obj header offset 18 + //var length = ms.Position - 21; + //ms.Position = 17; // this is the offset of the length unit32_t in the whole object + //ms.Write(BitConverter.GetBytes(length), 0, 4); - // convert to span - Span encodedSpan = new byte[buffer.Count]; - var counter = 0; - foreach (var b in buffer) - { - encodedSpan[counter++] = b; - } + ms.Flush(); + ms.Close(); - return encodedSpan; + return ms.ToArray(); } + + //public static ReadOnlySpan Encode(SawyerEncoding encoding, ReadOnlySpan data, ILogger? logger = null) + //{ + // switch (encoding) + // { + // case SawyerEncoding.Uncompressed: + // return data; + // case SawyerEncoding.RunLengthSingle: + // return EncodeRunLengthSingle(data); + // //case SawyerEncoding.runLengthMulti: + // // return encodeRunLengthMulti(decodeRunLengthSingle(data)); + // //case SawyerEncoding.rotate: + // // return encodeRotate(data); + // default: + // logger?.Error("Unknown chunk encoding scheme"); + // throw new InvalidDataException("Unknown encoding"); + // } + //} + + //public static void WriteToFile(string filepath, ReadOnlySpan s5Header, ReadOnlySpan objectHeader, ReadOnlySpan encodedData) + //{ + // var stream = File.Create(filepath); + // stream.Write(s5Header); + // stream.Write(objectHeader); + // stream.Write(encodedData); + // stream.Flush(); + // stream.Close(); + //} + + // taken from openloco SawyerStreamReader::encodeRunLengthSingle + // not sure why it doesn't work, but it doesn't work. gets the first 10 or so bytes correct for SIGC3.dat but then fails + //private static Span EncodeRunLengthSingle(ReadOnlySpan data) + //{ + // List buffer = []; + // var src = 0; // ptr + // var srcNormStart = 0; // ptr + // var srcEnd = data.Length; + // var count = 0; + + // while (src < srcEnd - 1) + // { + // if ((count != 0 && data[src] == data[src + 1]) || count > 125) + // { + // buffer.Add((byte)(count - 1)); + // buffer.AddRange(Enumerable.Repeat(data[srcNormStart], count)); + // srcNormStart += count; + // count = 0; + // } + + // if (data[src] == data[src + 1]) + // { + // for (; count < 125 && src + count < srcEnd; count++) + // { + // if (data[src] != data[count]) + // { + // break; + // } + // } + + // buffer.Add((byte)(257 - count)); + // buffer.Add(data[src]); + // src += count; + // srcNormStart = src; + // count = 0; + // } + // else + // { + // count++; + // src++; + // } + // } + + // if (data[src] == data[srcEnd - 1]) + // { + // count++; + // } + + // if (count != 0) + // { + // buffer.Add((byte)(count - 1)); + // buffer.AddRange(Enumerable.Repeat(data[srcNormStart], count)); + // } + + // // convert to span + // Span encodedSpan = new byte[buffer.Count]; + // var counter = 0; + // foreach (var b in buffer) + // { + // encodedSpan[counter++] = b; + // } + + // return encodedSpan; + //} } } diff --git a/OpenLocoTool/DatFileParsing/Typedefs.cs b/OpenLocoTool/DatFileParsing/Typedefs.cs index 3816609b..88d571bc 100644 --- a/OpenLocoTool/DatFileParsing/Typedefs.cs +++ b/OpenLocoTool/DatFileParsing/Typedefs.cs @@ -23,29 +23,18 @@ public record Pos2( public static int StructSize => 0x04; } - // this is only required because WinForms' DataGridView cannot handle plain old dictionary, because string type doesn't have the getter+setter it needs - //public class StringTable - //{ - // Dictionary table = new(); - //} - public class StringTable { - public Dictionary> table { get; set; } = new(); + public Dictionary> table { get; set; } = new(); - public void Add(string key, Dictionary value) => table.Add(key, value); + public void Add(string key, Dictionary value) => table.Add(key, value); - public Dictionary this[string key] + public Dictionary this[string key] { get => table[key]; set => table[key] = value; } - public Dictionary>.KeyCollection Keys => table.Keys; - } - - public class StringTableEntry - { - public string String { get; set; } + public Dictionary>.KeyCollection Keys => table.Keys; } } diff --git a/OpenLocoTool/Data/ObjectTypeFixedSize.cs b/OpenLocoTool/Data/ObjectTypeFixedSize.cs new file mode 100644 index 00000000..17b5013f --- /dev/null +++ b/OpenLocoTool/Data/ObjectTypeFixedSize.cs @@ -0,0 +1,48 @@ +using OpenLocoTool.Headers; +using OpenLocoTool.Objects; + +namespace OpenLocoTool.Data +{ + public static class ObjectTypeFixedSize + { + public static int GetSize(ObjectType objectType) + => objectType switch + { + ObjectType.Airport => AirportObject.StructSize, + ObjectType.Bridge => BridgeObject.StructSize, + ObjectType.Building => BuildingObject.StructSize, + ObjectType.Cargo => CargoObject.StructSize, + ObjectType.CliffEdge => CliffEdgeObject.StructSize, + ObjectType.Climate => ClimateObject.StructSize, + ObjectType.Competitor => CompetitorObject.StructSize, + ObjectType.Currency => CurrencyObject.StructSize, + ObjectType.Dock => DockObject.StructSize, + ObjectType.HillShapes => HillShapesObject.StructSize, + ObjectType.Industry => IndustryObject.StructSize, + ObjectType.InterfaceSkin => InterfaceSkinObject.StructSize, + ObjectType.Land => LandObject.StructSize, + ObjectType.LevelCrossing => LevelCrossingObject.StructSize, + ObjectType.Region => RegionObject.StructSize, + ObjectType.RoadExtra => RoadExtraObject.StructSize, + ObjectType.Road => RoadObject.StructSize, + ObjectType.RoadStation => RoadStationObject.StructSize, + ObjectType.Scaffolding => ScaffoldingObject.StructSize, + ObjectType.ScenarioText => ScenarioTextObject.StructSize, + ObjectType.Snow => SnowObject.StructSize, + ObjectType.Sound => SoundObject.StructSize, + ObjectType.Steam => SteamObject.StructSize, + ObjectType.StreetLight => StreetLightObject.StructSize, + ObjectType.TownNames => TownNamesObject.StructSize, + ObjectType.TrackExtra => TrackExtraObject.StructSize, + ObjectType.Track => TrackObject.StructSize, + ObjectType.TrainSignal => TrainSignalObject.StructSize, + ObjectType.TrainStation => TrainStationObject.StructSize, + ObjectType.Tree => TreeObject.StructSize, + ObjectType.Tunnel => TunnelObject.StructSize, + ObjectType.Vehicle => VehicleObject.StructSize, + ObjectType.Wall => WallObject.StructSize, + ObjectType.Water => WaterObject.StructSize, + _ => throw new ArgumentOutOfRangeException(nameof(objectType), $"unknown object type {objectType}") + }; + } +} diff --git a/OpenLocoTool/Data/SaveEnabledObjects.cs b/OpenLocoTool/Data/SaveEnabledObjects.cs new file mode 100644 index 00000000..c42be2b9 --- /dev/null +++ b/OpenLocoTool/Data/SaveEnabledObjects.cs @@ -0,0 +1,12 @@ +using OpenLocoTool.Headers; + +namespace OpenLocoTool.Data +{ + public static class SaveEnabledObjects + { + public static HashSet Types = + [ + ObjectType.StreetLight, + ]; + } +} diff --git a/OpenLocoTool/Headers/G1Header.cs b/OpenLocoTool/Headers/G1Header.cs index 23ca6cb0..2fee3265 100644 --- a/OpenLocoTool/Headers/G1Header.cs +++ b/OpenLocoTool/Headers/G1Header.cs @@ -31,6 +31,31 @@ public record G1Element32( { public static int StructLength => 0x10; public byte[] ImageData; + + public ReadOnlySpan Write() + { + var span = new byte[StructLength]; + + var offset = BitConverter.GetBytes(Offset); + var width = BitConverter.GetBytes(Width); + var height = BitConverter.GetBytes(Height); + var xOffset = BitConverter.GetBytes(XOffset); + var yOffset = BitConverter.GetBytes(YOffset); + var flags = BitConverter.GetBytes((uint16_t)Flags); + var zoomOffset = BitConverter.GetBytes(ZoomOffset); + + offset.CopyTo(span, 0x00); + width.CopyTo(span, 0x04); + height.CopyTo(span, 0x06); + xOffset.CopyTo(span, 0x08); + yOffset.CopyTo(span, 0x0A); + flags.CopyTo(span, 0x0C); + zoomOffset.CopyTo(span, 0x0E); + + // image data is copied later + + return span; + } } [TypeConverter(typeof(ExpandableObjectConverter))] diff --git a/OpenLocoTool/Headers/ObjectHeader.cs b/OpenLocoTool/Headers/ObjectHeader.cs index 64c513f3..73123a88 100644 --- a/OpenLocoTool/Headers/ObjectHeader.cs +++ b/OpenLocoTool/Headers/ObjectHeader.cs @@ -24,7 +24,10 @@ public ReadOnlySpan Write() { var span = new byte[StructLength]; span[0] = (byte)Encoding; - BitConverter.GetBytes(StructLength).CopyTo(span, 1); + + // we don't know full struct length until the object has been fully written...just write 0 for now + //BitConverter.GetBytes(StructLength).CopyTo(span, 1); + return span; } } diff --git a/OpenLocoTool/Objects/AirportObject.cs b/OpenLocoTool/Objects/AirportObject.cs index 2178077d..3e6a068d 100644 --- a/OpenLocoTool/Objects/AirportObject.cs +++ b/OpenLocoTool/Objects/AirportObject.cs @@ -47,14 +47,13 @@ public record MovementEdge( [TypeConverter(typeof(ExpandableObjectConverter))] [LocoStructSize(0xBA)] - [LocoStringTable("Name")] public record AirportObject( - //[property: LocoStructOffset(0x00)] string_id Name, + [property: LocoStructOffset(0x00), LocoString, Browsable(false)] string_id Name, [property: LocoStructOffset(0x02)] int16_t BuildCostFactor, [property: LocoStructOffset(0x04)] int16_t SellCostFactor, [property: LocoStructOffset(0x06)] uint8_t CostIndex, [property: LocoStructOffset(0x07)] uint8_t var_07, - //[property: LocoStructOffset(0x08)] uint32_t Image, + [property: LocoStructOffset(0x08)] uint32_t Image, [property: LocoStructOffset(0x0C)] uint32_t var_0C, [property: LocoStructOffset(0x10)] uint16_t AllowedPlaneTypes, [property: LocoStructOffset(0x12)] uint8_t NumSpriteSets, diff --git a/OpenLocoTool/Objects/BridgeObject.cs b/OpenLocoTool/Objects/BridgeObject.cs index 8b9f8aa8..c4c5a92a 100644 --- a/OpenLocoTool/Objects/BridgeObject.cs +++ b/OpenLocoTool/Objects/BridgeObject.cs @@ -4,12 +4,28 @@ namespace OpenLocoTool.Objects { - // for this object I tried a different way (class) of making the object (instead of record) - // it works just the same but has more code. [TypeConverter(typeof(ExpandableObjectConverter))] [LocoStructSize(0x2C)] - [LocoStringTable("Name")] - public class BridgeObject : ILocoStruct, ILocoStructVariableData + public record BridgeObject( + [property: LocoStructOffset(0x00), LocoString, Browsable(false)] string_id Name, + [property: LocoStructOffset(0x02)] uint8_t NoRoof, + [property: LocoStructOffset(0x03), LocoArrayLength(0x06 - 0x03)] uint8_t[] pad_03, + [property: LocoStructOffset(0x06)] uint16_t var_06, + [property: LocoStructOffset(0x08)] uint8_t SpanLength, + [property: LocoStructOffset(0x09)] uint8_t PillarSpacing, + [property: LocoStructOffset(0x0A)] Speed16 MaxSpeed, + [property: LocoStructOffset(0x0C)] MicroZ MaxHeight, + [property: LocoStructOffset(0x0D)] uint8_t CostIndex, + [property: LocoStructOffset(0x0E)] int16_t BaseCostFactor, + [property: LocoStructOffset(0x10)] int16_t HeightCostFactor, + [property: LocoStructOffset(0x12)] int16_t SellCostFactor, + [property: LocoStructOffset(0x14)] uint16_t DisabledTrackCfg, + [property: LocoStructOffset(0x16)] uint32_t Image, + [property: LocoStructOffset(0x1A)] uint8_t TrackNumCompatible, + [property: LocoStructOffset(0x1B), LocoArrayLength(BridgeObject.TrackModsLength)] uint8_t[] TrackMods, + [property: LocoStructOffset(0x22)] uint8_t RoadNumCompatible, + [property: LocoStructOffset(0x23), LocoArrayLength(BridgeObject.RoadModsLength)] uint8_t[] RoadMods, + [property: LocoStructOffset(0x2A)] uint16_t DesignedYear) : ILocoStruct, ILocoStructVariableData { public const ObjectType ObjType = ObjectType.Bridge; public const int StructSize = 0x2C; @@ -17,29 +33,6 @@ public class BridgeObject : ILocoStruct, ILocoStructVariableData public const int TrackModsLength = 7; public const int RoadModsLength = 7; - public BridgeObject(/*ushort name, */ byte noRoof, byte[] pad_03, ushort var_06, byte spanLength, byte pillarSpacing, short maxSpeed, byte maxHeight, byte costIndex, short baseCostFactor, short heightCostFactor, short sellCostFactor, ushort disabledTrackCfg, /*uint image,*/ byte trackNumCompatible, byte[] trackMods, byte roadNumCompatible, byte[] roadMods, ushort designedYear) - { - //Name = name; - NoRoof = noRoof; - this.pad_03 = pad_03; - this.var_06 = var_06; - SpanLength = spanLength; - PillarSpacing = pillarSpacing; - MaxSpeed = maxSpeed; - MaxHeight = maxHeight; - CostIndex = costIndex; - BaseCostFactor = baseCostFactor; - HeightCostFactor = heightCostFactor; - SellCostFactor = sellCostFactor; - DisabledTrackCfg = disabledTrackCfg; - //Image = image; - TrackNumCompatible = trackNumCompatible; - TrackMods = trackMods; - RoadNumCompatible = roadNumCompatible; - RoadMods = roadMods; - DesignedYear = designedYear; - } - // return number of bytes read public ReadOnlySpan Load(ReadOnlySpan remainingData) { @@ -47,26 +40,6 @@ public ReadOnlySpan Load(ReadOnlySpan remainingData) return remainingData[bytesRead..]; } - //[LocoStructOffset(0x00)] public string_id Name { get; set; } - [LocoStructOffset(0x02)] public uint8_t NoRoof { get; set; } - [LocoStructOffset(0x03), LocoArrayLength(0x06 - 0x03)] public uint8_t[] pad_03 { get; set; } - [LocoStructOffset(0x06)] public uint16_t var_06 { get; set; } - [LocoStructOffset(0x08)] public uint8_t SpanLength { get; set; } - [LocoStructOffset(0x09)] public uint8_t PillarSpacing { get; set; } - [LocoStructOffset(0x0A)] public Speed16 MaxSpeed { get; set; } - [LocoStructOffset(0x0C)] public MicroZ MaxHeight { get; set; } - [LocoStructOffset(0x0D)] public uint8_t CostIndex { get; set; } - [LocoStructOffset(0x0E)] public int16_t BaseCostFactor { get; set; } - [LocoStructOffset(0x10)] public int16_t HeightCostFactor { get; set; } - [LocoStructOffset(0x12)] public int16_t SellCostFactor { get; set; } - [LocoStructOffset(0x14)] public uint16_t DisabledTrackCfg { get; set; } - //[LocoStructOffset(0x16)] public uint32_t Image { get; set; } - [LocoStructOffset(0x1A)] public uint8_t TrackNumCompatible { get; set; } - [LocoStructOffset(0x1B), LocoArrayLength(BridgeObject.TrackModsLength)] public uint8_t[] TrackMods { get; set; } - [LocoStructOffset(0x22)] public uint8_t RoadNumCompatible { get; set; } - [LocoStructOffset(0x23), LocoArrayLength(BridgeObject.RoadModsLength)] public uint8_t[] RoadMods { get; set; } - [LocoStructOffset(0x2A)] public uint16_t DesignedYear { get; set; } - public ReadOnlySpan Save() => throw new NotImplementedException(); } } \ No newline at end of file diff --git a/OpenLocoTool/Objects/BuildingObject.cs b/OpenLocoTool/Objects/BuildingObject.cs index 8c8a6cf2..fcdb7ff0 100644 --- a/OpenLocoTool/Objects/BuildingObject.cs +++ b/OpenLocoTool/Objects/BuildingObject.cs @@ -16,10 +16,9 @@ public enum BuildingObjectFlags : uint8_t [TypeConverter(typeof(ExpandableObjectConverter))] [LocoStructSize(0xBE)] - [LocoStringTable("Name")] public record BuildingObject( - //[property: LocoStructOffset(0x00)] string_id Name, - //[property: LocoStructOffset(0x02)] uint32_t Image, + [property: LocoStructOffset(0x00), LocoString, Browsable(false)] string_id Name, + [property: LocoStructOffset(0x02)] uint32_t Image, [property: LocoStructOffset(0x06)] uint8_t var_06, [property: LocoStructOffset(0x07)] uint8_t NumVariations, [property: LocoStructOffset(0x08), LocoArrayLength(4)] uint8_t[] VariationHeights, diff --git a/OpenLocoTool/Objects/CargoObject.cs b/OpenLocoTool/Objects/CargoObject.cs index 0a441b4c..fe8c34ec 100644 --- a/OpenLocoTool/Objects/CargoObject.cs +++ b/OpenLocoTool/Objects/CargoObject.cs @@ -14,14 +14,13 @@ public enum CargoObjectFlags : uint8_t [TypeConverter(typeof(ExpandableObjectConverter))] [LocoStructSize(0x1F)] - [LocoStringTable("Name", "UnitsAndCargoName", "UnitNameSingular", "UnitNamePlural")] public record CargoObject( - //[property: LocoStructOffset(0x00)] string_id Name, + [property: LocoStructOffset(0x00), LocoString, Browsable(false)] string_id Name, [property: LocoStructOffset(0x02)] uint16_t var_02, [property: LocoStructOffset(0x04)] uint16_t var_04, - //[property: LocoStructOffset(0x06)] string_id UnitsAndCargoName, - //[property: LocoStructOffset(0x08)] string_id UnitNameSingular, - //[property: LocoStructOffset(0x0A)] string_id UnitNamePlural, + [property: LocoStructOffset(0x06), LocoString, Browsable(false)] string_id UnitsAndCargoName, + [property: LocoStructOffset(0x08), LocoString, Browsable(false)] string_id UnitNameSingular, + [property: LocoStructOffset(0x0A), LocoString, Browsable(false)] string_id UnitNamePlural, [property: LocoStructOffset(0x0C)] uint32_t UnitInlineSprite, [property: LocoStructOffset(0x10)] uint16_t MatchFlags, [property: LocoStructOffset(0x12)] CargoObjectFlags Flags, diff --git a/OpenLocoTool/Objects/CliffEdgeObject.cs b/OpenLocoTool/Objects/CliffEdgeObject.cs index a2ebdd61..d8c7e1b3 100644 --- a/OpenLocoTool/Objects/CliffEdgeObject.cs +++ b/OpenLocoTool/Objects/CliffEdgeObject.cs @@ -6,19 +6,18 @@ namespace OpenLocoTool.Objects { [TypeConverter(typeof(ExpandableObjectConverter))] [LocoStructSize(0x06)] - [LocoStringTable("Name")] public class CliffEdgeObject : ILocoStruct { public const ObjectType ObjType = ObjectType.CliffEdge; public const int StructSize = 0x06; - public CliffEdgeObject(/*string_id name, uint32_t image*/) + public CliffEdgeObject(string_id name, uint32_t image) { - //Name = name; - //Image = image; + Name = name; + Image = image; } - //[LocoStructOffset(0x00)] public string_id Name { get; set; } - //[LocoStructOffset(0x02)] public uint32_t Image { get; set; } + [LocoStructOffset(0x00), LocoString, Browsable(false)] public string_id Name { get; set; } + [LocoStructOffset(0x02)] public uint32_t Image { get; set; } } } diff --git a/OpenLocoTool/Objects/ClimateObject.cs b/OpenLocoTool/Objects/ClimateObject.cs index b9aeb162..56ac5f2d 100644 --- a/OpenLocoTool/Objects/ClimateObject.cs +++ b/OpenLocoTool/Objects/ClimateObject.cs @@ -6,9 +6,9 @@ namespace OpenLocoTool.Objects { [TypeConverter(typeof(ExpandableObjectConverter))] [LocoStructSize(0x0A)] - [LocoStringTable("Name")] + //[LocoStringTable("Name")] public record ClimateObject( - //[property: LocoStructOffset(0x00)] string_id Name, + [property: LocoStructOffset(0x00), LocoString, Browsable(false)] string_id Name, [property: LocoStructOffset(0x02)] uint8_t FirstSeason, [property: LocoStructOffset(0x03), LocoArrayLength(ClimateObject.Seasons)] uint8_t[] SeasonLengths, [property: LocoStructOffset(0x07)] uint8_t WinterSnowLine, diff --git a/OpenLocoTool/Objects/CompetitorObject.cs b/OpenLocoTool/Objects/CompetitorObject.cs index d4f70036..508f4262 100644 --- a/OpenLocoTool/Objects/CompetitorObject.cs +++ b/OpenLocoTool/Objects/CompetitorObject.cs @@ -6,10 +6,9 @@ namespace OpenLocoTool.Objects { [TypeConverter(typeof(ExpandableObjectConverter))] [LocoStructSize(0x38)] - [LocoStringTable("var_00, var_02")] public record CompetitorObject( - //[property: LocoStructOffset(0x00)] string_id var_00, - //[property: LocoStructOffset(0x02)] string_id var_02, + [property: LocoStructOffset(0x00), LocoString] string_id var_00, + [property: LocoStructOffset(0x02), LocoString, Browsable(false)] string_id var_02, [property: LocoStructOffset(0x04)] uint32_t var_04, [property: LocoStructOffset(0x08)] uint32_t var_08, [property: LocoStructOffset(0x0C)] uint32_t Emotions, diff --git a/OpenLocoTool/Objects/CurrencyObject.cs b/OpenLocoTool/Objects/CurrencyObject.cs index 883fa284..8921fd6a 100644 --- a/OpenLocoTool/Objects/CurrencyObject.cs +++ b/OpenLocoTool/Objects/CurrencyObject.cs @@ -6,12 +6,11 @@ namespace OpenLocoTool.Objects { [TypeConverter(typeof(ExpandableObjectConverter))] [LocoStructSize(0x0C)] - [LocoStringTable("Name", "PrefixSymbol", "SuffixSymbol")] public record CurrencyObject ( - //[property: LocoStructOffset(0x00)] string_id Name, - //[property: LocoStructOffset(0x02)] string_id PrefixSymbol, - //[property: LocoStructOffset(0x04)] string_id SuffixSymbol, + [property: LocoStructOffset(0x00), LocoString, Browsable(false)] string_id Name, + [property: LocoStructOffset(0x02), LocoString, Browsable(false)] string_id PrefixSymbol, + [property: LocoStructOffset(0x04), LocoString, Browsable(false)] string_id SuffixSymbol, [property: LocoStructOffset(0x06)] uint32_t ObjectIcon, [property: LocoStructOffset(0x0A)] uint8_t Separator, [property: LocoStructOffset(0x0B)] uint8_t Factor diff --git a/OpenLocoTool/Objects/DockObject.cs b/OpenLocoTool/Objects/DockObject.cs index f3aecb96..41651044 100644 --- a/OpenLocoTool/Objects/DockObject.cs +++ b/OpenLocoTool/Objects/DockObject.cs @@ -13,21 +13,20 @@ public enum DockObjectFlags : uint16_t [TypeConverter(typeof(ExpandableObjectConverter))] [LocoStructSize(0x28)] - [LocoStringTable("Name")] public record DockObject( - //[property: LocoStructOffset(0x00)] string_id Name, + [property: LocoStructOffset(0x00), LocoString, Browsable(false)] string_id Name, [property: LocoStructOffset(0x02)] int16_t BuildCostFactor, [property: LocoStructOffset(0x04)] int16_t SellCostFactor, [property: LocoStructOffset(0x06)] uint8_t CostIndex, [property: LocoStructOffset(0x07)] uint8_t var_07, - //[property: LocoStructOffset(0x08)] uint32_t Image, + [property: LocoStructOffset(0x08)] uint32_t Image, [property: LocoStructOffset(0x0C)] uint32_t var_0C, [property: LocoStructOffset(0x10)] DockObjectFlags Flags, [property: LocoStructOffset(0x12)] uint8_t NumAux01, [property: LocoStructOffset(0x13)] uint8_t NumAux02Ent, /* must be 1 or 0 */ - // [property: LocoStructProperty(0x14)] const uint8_t* var_14, - // [property: LocoStructProperty(0x18)] const uint16_t* var_18, - // [property: LocoStructProperty(0x1C)] const uint8_t* var_1C[1], // odd that this is size 1 but that is how its used + //[property: LocoStructOffset(0x14)] const uint8_t* var_14, + //[property: LocoStructOffset(0x18)] const uint16_t* var_18, + //[property: LocoStructOffset(0x1C)] const uint8_t* var_1C[1], // odd that this is size 1 but that is how its used [property: LocoStructOffset(0x20)] uint16_t DesignedYear, [property: LocoStructOffset(0x22)] uint16_t ObsoleteYear, [property: LocoStructOffset(0x24)] Pos2 BoatPosition diff --git a/OpenLocoTool/Objects/HillShapesObject.cs b/OpenLocoTool/Objects/HillShapesObject.cs index 1d67dca7..0cf49b4b 100644 --- a/OpenLocoTool/Objects/HillShapesObject.cs +++ b/OpenLocoTool/Objects/HillShapesObject.cs @@ -6,12 +6,11 @@ namespace OpenLocoTool.Objects { [TypeConverter(typeof(ExpandableObjectConverter))] [LocoStructSize(0x0E)] - [LocoStringTable("Name")] public record HillShapesObject( - //[property: LocoStructOffset(0x00)] string_id Name, + [property: LocoStructOffset(0x00), LocoString, Browsable(false)] string_id Name, [property: LocoStructOffset(0x02)] uint8_t HillHeightMapCount, [property: LocoStructOffset(0x03)] uint8_t MountainHeightMapCount, - //[property: LocoStructOffset(0x04)] uint32_t Image, + [property: LocoStructOffset(0x04)] uint32_t Image, [property: LocoStructOffset(0x08)] uint32_t var_08, [property: LocoStructOffset(0x0C), LocoArrayLength(0x0E - 0x0C)] uint8_t[] pad_0C ) : ILocoStruct diff --git a/OpenLocoTool/Objects/IndustryObject.cs b/OpenLocoTool/Objects/IndustryObject.cs index 0c65bbf2..ff145397 100644 --- a/OpenLocoTool/Objects/IndustryObject.cs +++ b/OpenLocoTool/Objects/IndustryObject.cs @@ -74,15 +74,15 @@ public record IndustryObjectProductionRateRange( [TypeConverter(typeof(ExpandableObjectConverter))] [LocoStructSize(0xF4)] - [LocoStringTable("Name", "var_02", "", "NameClosingDown", "NameUpProduction", "NameDownProduction", "NameSingular", "NamePlural")] public record IndustryObject( - //[property: LocoStructOffset(0x00)] string_id Name, - //[property: LocoStructOffset(0x02)] string_id var_02, - //[property: LocoStructOffset(0x04)] string_id NameClosingDown, - //[property: LocoStructOffset(0x06)] string_id NameUpProduction, - //[property: LocoStructOffset(0x08)] string_id NameDownProduction, - //[property: LocoStructOffset(0x0A)] string_id NameSingular, - //[property: LocoStructOffset(0x0C)] string_id NamePlural, + [property: LocoStructOffset(0x00), LocoString, Browsable(false)] string_id Name, + [property: LocoStructOffset(0x02), LocoString, Browsable(false)] string_id var_02, + [property: LocoStructOffset(0x02), LocoString, Browsable(false)] string_id _unused, + [property: LocoStructOffset(0x04), LocoString, Browsable(false)] string_id NameClosingDown, + [property: LocoStructOffset(0x06), LocoString, Browsable(false)] string_id NameUpProduction, + [property: LocoStructOffset(0x08), LocoString, Browsable(false)] string_id NameDownProduction, + [property: LocoStructOffset(0x0A), LocoString, Browsable(false)] string_id NameSingular, + [property: LocoStructOffset(0x0C), LocoString, Browsable(false)] string_id NamePlural, [property: LocoStructOffset(0x0E)] uint32_t var_0E, // shadows image id base [property: LocoStructOffset(0x12)] uint32_t var_12, // Base image id for building 0 [property: LocoStructOffset(0x16)] uint32_t var_16, @@ -108,8 +108,8 @@ public record IndustryObject( [property: LocoStructOffset(0xD4)] uint8_t ScaffoldingSegmentType, [property: LocoStructOffset(0xD5)] Colour ScaffoldingColour, [property: LocoStructOffset(0xD6), LocoArrayLength(2)] IndustryObjectProductionRateRange[] InitialProductionRate, - ///[property: LocoStructOffset(0xDE), LocoArrayLength(IndustryObject.MaxProducedCargoType)] uint8_t[] ProducedCargoType, // (0xFF = null) - //[property: LocoStructOffset(0xE0), LocoArrayLength(IndustryObject.MaxRequiredCargoType)] uint8_t[] RequiredCargoType, // (0xFF = null) + [property: LocoStructOffset(0xDE), LocoArrayLength(IndustryObject.MaxProducedCargoType)] uint8_t[] ProducedCargoType, // (0xFF = null) + [property: LocoStructOffset(0xE0), LocoArrayLength(IndustryObject.MaxRequiredCargoType)] uint8_t[] RequiredCargoType, // (0xFF = null) [property: LocoStructOffset(0xE3)] uint8_t pad_E3, [property: LocoStructOffset(0xE4)] IndustryObjectFlags Flags, [property: LocoStructOffset(0xE8)] uint8_t var_E8, diff --git a/OpenLocoTool/Objects/InterfaceSkinObject.cs b/OpenLocoTool/Objects/InterfaceSkinObject.cs index 768f1808..593f5bae 100644 --- a/OpenLocoTool/Objects/InterfaceSkinObject.cs +++ b/OpenLocoTool/Objects/InterfaceSkinObject.cs @@ -7,10 +7,9 @@ namespace OpenLocoTool.Objects { [TypeConverter(typeof(ExpandableObjectConverter))] [LocoStructSize(0x18)] - [LocoStringTable("Name")] public record InterfaceSkinObject( - //[property: LocoStructOffset(0x00)] string_id Name, - //[property: LocoStructOffset(0x02)] uint32_t Image, + [property: LocoStructOffset(0x00), LocoString, Browsable(false)] string_id Name, + [property: LocoStructOffset(0x02)] uint32_t Image, [property: LocoStructOffset(0x06)] Colour Colour_06, [property: LocoStructOffset(0x07)] Colour Colour_07, [property: LocoStructOffset(0x08)] Colour TooltipColour, diff --git a/OpenLocoTool/Objects/LandObject.cs b/OpenLocoTool/Objects/LandObject.cs index e6fdf3dc..4e24b850 100644 --- a/OpenLocoTool/Objects/LandObject.cs +++ b/OpenLocoTool/Objects/LandObject.cs @@ -17,9 +17,8 @@ public enum LandObjectFlags : uint8_t [TypeConverter(typeof(ExpandableObjectConverter))] [LocoStructSize(0x1E)] - [LocoStringTable("Name")] public record LandObject( - //[property: LocoStructOffset(0x00)] string_id Name, + [property: LocoStructOffset(0x00), LocoString, Browsable(false)] string_id Name, [property: LocoStructOffset(0x02)] uint8_t CostIndex, [property: LocoStructOffset(0x03)] uint8_t var_03, [property: LocoStructOffset(0x04)] uint8_t var_04, @@ -28,7 +27,7 @@ public record LandObject( [property: LocoStructOffset(0x07)] uint8_t var_07, [property: LocoStructOffset(0x08)] int8_t CostFactor, [property: LocoStructOffset(0x09)] uint8_t pad_09, - //[property: LocoStructOffset(0x0A)] uint32_t Image, + [property: LocoStructOffset(0x0A)] uint32_t Image, [property: LocoStructOffset(0x0E)] uint32_t var_0E, [property: LocoStructOffset(0x12)] uint32_t var_12, [property: LocoStructOffset(0x16)] uint32_t var_16, diff --git a/OpenLocoTool/Objects/LevelCrossingObject.cs b/OpenLocoTool/Objects/LevelCrossingObject.cs index 69f87fa9..e62b3e89 100644 --- a/OpenLocoTool/Objects/LevelCrossingObject.cs +++ b/OpenLocoTool/Objects/LevelCrossingObject.cs @@ -7,9 +7,8 @@ namespace OpenLocoTool.Objects { [TypeConverter(typeof(ExpandableObjectConverter))] [LocoStructSize(0x12)] - [LocoStringTable("Name")] public record LevelCrossingObject( - //[property: LocoStructOffset(0x00)] string_id Name, + [property: LocoStructOffset(0x00), LocoString, Browsable(false)] string_id Name, [property: LocoStructOffset(0x02)] int16_t CostFactor, [property: LocoStructOffset(0x04)] int16_t SellCostFactor, [property: LocoStructOffset(0x06)] uint8_t CostIndex, @@ -17,8 +16,8 @@ public record LevelCrossingObject( [property: LocoStructOffset(0x08)] uint8_t ClosingFrames, [property: LocoStructOffset(0x09)] uint8_t ClosedFrames, [property: LocoStructOffset(0x0A), LocoArrayLength(0x0C - 0x0A)] uint8_t[] pad_0A, - [property: LocoStructOffset(0x0C)] uint16_t DesignedYear - //[property: LocoStructOffset(0x0E)] uint32_t Image + [property: LocoStructOffset(0x0C)] uint16_t DesignedYear, + [property: LocoStructOffset(0x0E)] uint32_t Image ) : ILocoStruct { public static ObjectType ObjectType => ObjectType.LevelCrossing; diff --git a/OpenLocoTool/Objects/RegionObject.cs b/OpenLocoTool/Objects/RegionObject.cs index 003be9d4..50118d77 100644 --- a/OpenLocoTool/Objects/RegionObject.cs +++ b/OpenLocoTool/Objects/RegionObject.cs @@ -7,10 +7,9 @@ namespace OpenLocoTool.Objects { [TypeConverter(typeof(ExpandableObjectConverter))] [LocoStructSize(0x12)] - [LocoStringTable("Name")] public record RegionObject( - //[property: LocoStructOffset(0x00)] string_id Name, - //[property: LocoStructOffset(0x02)] uint32_t Image, + [property: LocoStructOffset(0x00), LocoString, Browsable(false)] string_id Name, + [property: LocoStructOffset(0x02)] uint32_t Image, [property: LocoStructOffset(0x06), LocoArrayLength(0x8 - 0x6)] uint8_t[] pad_06, [property: LocoStructOffset(0x08)] uint8_t var_08, [property: LocoStructOffset(0x09), LocoArrayLength(4)] uint8_t[] var_09, diff --git a/OpenLocoTool/Objects/RoadExtraObject.cs b/OpenLocoTool/Objects/RoadExtraObject.cs index 95e46903..bb36160e 100644 --- a/OpenLocoTool/Objects/RoadExtraObject.cs +++ b/OpenLocoTool/Objects/RoadExtraObject.cs @@ -7,15 +7,14 @@ namespace OpenLocoTool.Objects { [TypeConverter(typeof(ExpandableObjectConverter))] [LocoStructSize(0x12)] - [LocoStringTable("Name")] public record RoadExtraObject( - //[property: LocoStructOffset(0x00)] string_id Name, + [property: LocoStructOffset(0x00), LocoString, Browsable(false)] string_id Name, [property: LocoStructOffset(0x02)] uint16_t RoadPieces, [property: LocoStructOffset(0x04)] uint8_t PaintStyle, [property: LocoStructOffset(0x05)] uint8_t CostIndex, [property: LocoStructOffset(0x06)] int16_t BuildCostFactor, [property: LocoStructOffset(0x08)] int16_t SellCostFactor, - //[property: LocoStructOffset(0x0A)] uint32_t Image, + [property: LocoStructOffset(0x0A)] uint32_t Image, [property: LocoStructOffset(0x0E)] uint32_t var_0E ) : ILocoStruct { diff --git a/OpenLocoTool/Objects/RoadObject.cs b/OpenLocoTool/Objects/RoadObject.cs index e810a048..02053535 100644 --- a/OpenLocoTool/Objects/RoadObject.cs +++ b/OpenLocoTool/Objects/RoadObject.cs @@ -34,9 +34,8 @@ public enum RoadObjectPieceFlags : uint16_t [TypeConverter(typeof(ExpandableObjectConverter))] [LocoStructSize(0x30)] - [LocoStringTable("Name")] public record RoadObject( - //[property: LocoStructOffset(0x00)] string_id Name, + [property: LocoStructOffset(0x00), LocoString, Browsable(false)] string_id Name, [property: LocoStructOffset(0x02)] RoadObjectPieceFlags RoadPieces, [property: LocoStructOffset(0x04)] int16_t BuildCostFactor, [property: LocoStructOffset(0x06)] int16_t SellCostFactor, @@ -44,7 +43,7 @@ public record RoadObject( [property: LocoStructOffset(0x0A)] uint8_t CostIndex, [property: LocoStructOffset(0x0B)] uint8_t Tunnel, [property: LocoStructOffset(0x0C)] Speed16 MaxSpeed, - //[property: LocoStructOffset(0x0E)] uint32_t Image, + [property: LocoStructOffset(0x0E)] uint32_t Image, [property: LocoStructOffset(0x12)] RoadObjectFlags Flags, [property: LocoStructOffset(0x14)] uint8_t NumBridges, [property: LocoStructOffset(0x15), LocoArrayLength(7)] uint8_t[] Bridges, diff --git a/OpenLocoTool/Objects/RoadStationObject.cs b/OpenLocoTool/Objects/RoadStationObject.cs index abead815..3ed4eae8 100644 --- a/OpenLocoTool/Objects/RoadStationObject.cs +++ b/OpenLocoTool/Objects/RoadStationObject.cs @@ -16,9 +16,8 @@ public enum RoadStationFlags : uint8_t [TypeConverter(typeof(ExpandableObjectConverter))] [LocoStructSize(0x6E)] - [LocoStringTable("Name")] public record RoadStationObject( - //[property: LocoStructOffset(0x00)] string_id Name, + [property: LocoStructOffset(0x00), LocoString, Browsable(false)] string_id Name, [property: LocoStructOffset(0x02)] uint8_t PaintStyle, [property: LocoStructOffset(0x03)] uint8_t pad_03, [property: LocoStructOffset(0x04)] uint16_t RoadPieces, @@ -26,7 +25,7 @@ public record RoadStationObject( [property: LocoStructOffset(0x08)] int16_t SellCostFactor, [property: LocoStructOffset(0x0A)] uint8_t CostIndex, [property: LocoStructOffset(0x0B)] RoadStationFlags Flags, - //[property: LocoStructOffset(0x0C)] uint32_t Image, + [property: LocoStructOffset(0x0C)] uint32_t Image, [property: LocoStructOffset(0x10), LocoArrayLength(4)] uint32_t[] var_10, [property: LocoStructOffset(0x20)] uint8_t NumCompatible, [property: LocoStructOffset(0x21), LocoArrayLength(7)] uint8_t[] Mods, diff --git a/OpenLocoTool/Objects/ScaffoldingObject.cs b/OpenLocoTool/Objects/ScaffoldingObject.cs index a8d2cd82..ad133d29 100644 --- a/OpenLocoTool/Objects/ScaffoldingObject.cs +++ b/OpenLocoTool/Objects/ScaffoldingObject.cs @@ -7,10 +7,9 @@ namespace OpenLocoTool.Objects { [TypeConverter(typeof(ExpandableObjectConverter))] [LocoStructSize(0x12)] - [LocoStringTable("Name")] public record ScaffoldingObject( - //[property: LocoStructOffset(0x00)] string_id Name, - //[property: LocoStructOffset(0x02)] uint32_t Image, + [property: LocoStructOffset(0x00), LocoString, Browsable(false)] string_id Name, + [property: LocoStructOffset(0x02)] uint32_t Image, [property: LocoStructOffset(0x06), LocoArrayLength(3)] uint16_t[] SegmentHeights, // 0x06 [property: LocoStructOffset(0x0C), LocoArrayLength(3)] uint16_t[] RoofHeights // 0x0C ) : ILocoStruct diff --git a/OpenLocoTool/Objects/ScenarioTextObject.cs b/OpenLocoTool/Objects/ScenarioTextObject.cs index 235876fb..d6dfd479 100644 --- a/OpenLocoTool/Objects/ScenarioTextObject.cs +++ b/OpenLocoTool/Objects/ScenarioTextObject.cs @@ -7,11 +7,10 @@ namespace OpenLocoTool.Objects { [TypeConverter(typeof(ExpandableObjectConverter))] [LocoStructSize(0x06)] - [LocoStringTable("Name", "Details")] public record ScenarioTextObject( - //[property: LocoStructOffset(0x00)] string_id Name, - //[property: LocoStructOffset(0x02)] string_id Details, - [property: LocoStructOffset(0x04), LocoArrayLength(0x6 - 0x4)] string_id pad_04 // 0x04 + [property: LocoStructOffset(0x00), LocoString, Browsable(false)] string_id Name, + [property: LocoStructOffset(0x02), LocoString, Browsable(false)] string_id Details, + [property: LocoStructOffset(0x04), LocoArrayLength(0x6 - 0x4)] uint8_t pad_04 // 0x04 ) : ILocoStruct { public static ObjectType ObjectType => ObjectType.ScenarioText; diff --git a/OpenLocoTool/Objects/SnowObject.cs b/OpenLocoTool/Objects/SnowObject.cs index c931d714..e7e04b97 100644 --- a/OpenLocoTool/Objects/SnowObject.cs +++ b/OpenLocoTool/Objects/SnowObject.cs @@ -7,10 +7,9 @@ namespace OpenLocoTool.Objects { [TypeConverter(typeof(ExpandableObjectConverter))] [LocoStructSize(0x06)] - [LocoStringTable("Name")] public record SnowObject( - //[property: LocoStructOffset(0x00)] string_id Name, - //[property: LocoStructOffset(0x02)] uint32_t Image + [property: LocoStructOffset(0x00), LocoString, Browsable(false)] string_id Name, + [property: LocoStructOffset(0x02)] uint32_t Image ) : ILocoStruct { public static ObjectType ObjectType => ObjectType.Snow; diff --git a/OpenLocoTool/Objects/SoundObject.cs b/OpenLocoTool/Objects/SoundObject.cs index c83a9a36..6a15a4e6 100644 --- a/OpenLocoTool/Objects/SoundObject.cs +++ b/OpenLocoTool/Objects/SoundObject.cs @@ -33,9 +33,8 @@ public record SoundObjectData( [TypeConverter(typeof(ExpandableObjectConverter))] [LocoStructSize(0x0C)] - [LocoStringTable("Name")] public record SoundObject( - //[property: LocoStructOffset(0x00)] string_id Name, + [property: LocoStructOffset(0x00), LocoString, Browsable(false)] string_id Name, [property: LocoStructOffset(0x02)] uint32_t SoundObjectDataPtr, [property: LocoStructOffset(0x06)] uint8_t var_06, [property: LocoStructOffset(0x07)] uint8_t pad_07, @@ -47,7 +46,7 @@ public record SoundObject( public SoundObjectData SoundObjectData { get; set; } - public byte[] RawPcmData { get; set; } + public byte[] RawPcmData { get; set; } = []; public ReadOnlySpan Load(ReadOnlySpan remainingData) { diff --git a/OpenLocoTool/Objects/SteamObject.cs b/OpenLocoTool/Objects/SteamObject.cs index 85034bde..960d3f39 100644 --- a/OpenLocoTool/Objects/SteamObject.cs +++ b/OpenLocoTool/Objects/SteamObject.cs @@ -27,9 +27,8 @@ public record ImageAndHeight( [TypeConverter(typeof(ExpandableObjectConverter))] [LocoStructSize(0x28)] - [LocoStringTable("Name")] public record SteamObject( - //[property: LocoStructOffset(0x00)] string_id Name, + [property: LocoStructOffset(0x00), LocoString, Browsable(false)] string_id Name, [property: LocoStructOffset(0x02)] uint16_t NumImages, [property: LocoStructOffset(0x04)] uint8_t NumStationaryTicks, // while stationary can be affected by wind [property: LocoStructOffset(0x05)] uint8_t SpriteWidth, diff --git a/OpenLocoTool/Objects/StreetLightObject.cs b/OpenLocoTool/Objects/StreetLightObject.cs index 8d877051..0492bae2 100644 --- a/OpenLocoTool/Objects/StreetLightObject.cs +++ b/OpenLocoTool/Objects/StreetLightObject.cs @@ -5,10 +5,9 @@ namespace OpenLocoTool.Objects { [TypeConverter(typeof(ExpandableObjectConverter))] - [LocoStructSize(0xC)] - [LocoStringTable("Name")] + [LocoStructSize(0x0C)] public record StreetLightObject( - [property: LocoStructOffset(0x00), LocoStructSkipRead, Browsable(false)] string_id Name, + [property: LocoStructOffset(0x00), LocoStructSkipRead, LocoString, Browsable(false)] string_id Name, [property: LocoStructOffset(0x02), LocoArrayLength(StreetLightObject.DesignedYearLength)] uint16_t[] DesignedYear, [property: LocoStructOffset(0x08), LocoStructSkipRead, Browsable(false)] uint32_t Image ) : ILocoStruct diff --git a/OpenLocoTool/Objects/TownNamesObject.cs b/OpenLocoTool/Objects/TownNamesObject.cs index 6016b544..91f05630 100644 --- a/OpenLocoTool/Objects/TownNamesObject.cs +++ b/OpenLocoTool/Objects/TownNamesObject.cs @@ -18,9 +18,8 @@ public record TownNamesUnk( [TypeConverter(typeof(ExpandableObjectConverter))] [LocoStructSize(0x1A)] - [LocoStringTable("Name")] public record TownNamesObject( - //[property: LocoStructOffset(0x00)] string_id Name, + [property: LocoStructOffset(0x00), LocoString, Browsable(false)] string_id Name, [property: LocoStructOffset(0x02), LocoArrayLength(6)] TownNamesUnk[] unks ) : ILocoStruct, ILocoStructVariableData { diff --git a/OpenLocoTool/Objects/TrackExtraObject.cs b/OpenLocoTool/Objects/TrackExtraObject.cs index bea61517..9bc5b8cc 100644 --- a/OpenLocoTool/Objects/TrackExtraObject.cs +++ b/OpenLocoTool/Objects/TrackExtraObject.cs @@ -7,15 +7,14 @@ namespace OpenLocoTool.Objects { [TypeConverter(typeof(ExpandableObjectConverter))] [LocoStructSize(0x12)] - [LocoStringTable("Name")] public record TrackExtraObject( - //[property: LocoStructOffset(0x00)] string_id Name, + [property: LocoStructOffset(0x00), LocoString, Browsable(false)] string_id Name, [property: LocoStructOffset(0x02)] uint16_t TrackPieces, [property: LocoStructOffset(0x04)] uint8_t PaintStyle, [property: LocoStructOffset(0x05)] uint8_t CostIndex, [property: LocoStructOffset(0x06)] int16_t BuildCostFactor, [property: LocoStructOffset(0x08)] int16_t SellCostFactor, - //[property: LocoStructOffset(0x0A)] uint32_t Image, + [property: LocoStructOffset(0x0A)] uint32_t Image, [property: LocoStructOffset(0x0E)] uint32_t var_0E ) : ILocoStruct { diff --git a/OpenLocoTool/Objects/TrackObject.cs b/OpenLocoTool/Objects/TrackObject.cs index 91be8f0e..2e52548e 100644 --- a/OpenLocoTool/Objects/TrackObject.cs +++ b/OpenLocoTool/Objects/TrackObject.cs @@ -30,9 +30,8 @@ public enum TrackObjectFlags : uint16_t [TypeConverter(typeof(ExpandableObjectConverter))] [LocoStructSize(0x36)] - [LocoStringTable("Name")] public record TrackObject( - //[property: LocoStructOffset(0x00)] string_id Name, + [property: LocoStructOffset(0x00), LocoString, Browsable(false)] string_id Name, [property: LocoStructOffset(0x02)] TrackObjectPieceFlags TrackPieces, [property: LocoStructOffset(0x04)] uint16_t StationTrackPieces, [property: LocoStructOffset(0x06)] uint8_t var_06, @@ -49,7 +48,7 @@ public record TrackObject( [property: LocoStructOffset(0x1A)] uint8_t CostIndex, [property: LocoStructOffset(0x1B)] uint8_t Tunnel, [property: LocoStructOffset(0x1C)] uint16_t CurveSpeed, - //[property: LocoStructOffset(0x1E)] uint32_t Image, + [property: LocoStructOffset(0x1E)] uint32_t Image, [property: LocoStructOffset(0x22)] TrackObjectFlags Flags, [property: LocoStructOffset(0x24)] uint8_t NumBridges, [property: LocoStructOffset(0x25), LocoArrayLength(7)] uint8_t[] Bridges, // 0x25 diff --git a/OpenLocoTool/Objects/TrainSignalObject.cs b/OpenLocoTool/Objects/TrainSignalObject.cs index f2b4ef02..af3aa3c1 100644 --- a/OpenLocoTool/Objects/TrainSignalObject.cs +++ b/OpenLocoTool/Objects/TrainSignalObject.cs @@ -15,9 +15,8 @@ public enum TrainSignalObjectFlags : uint16_t [TypeConverter(typeof(ExpandableObjectConverter))] [LocoStructSize(0x1E)] - [LocoStringTable("Name", "Description")] public record TrainSignalObject( - //[property: LocoStructOffset(0x00)] string_id Name, + [property: LocoStructOffset(0x00), LocoString, Browsable(false)] string_id Name, [property: LocoStructOffset(0x02)] TrainSignalObjectFlags Flags, [property: LocoStructOffset(0x04)] uint8_t AnimationSpeed, [property: LocoStructOffset(0x05)] uint8_t NumFrames, @@ -25,8 +24,8 @@ public record TrainSignalObject( [property: LocoStructOffset(0x08)] int16_t SellCostFactor, [property: LocoStructOffset(0x0A)] uint8_t CostIndex, [property: LocoStructOffset(0x0B)] uint8_t var_0B, - //[property: LocoStructOffset(0x0C)] string_id Description, - //[property: LocoStructOffset(0x0E)] uint32_t Image, + [property: LocoStructOffset(0x0C), LocoString, Browsable(false)] string_id Description, + [property: LocoStructOffset(0x0E)] uint32_t Image, [property: LocoStructOffset(0x12)] uint8_t NumCompatible, [property: LocoStructOffset(0x13), LocoArrayLength(TrainSignalObject.ModsLength)] uint8_t[] Mods, [property: LocoStructOffset(0x1A)] uint16_t DesignedYear, diff --git a/OpenLocoTool/Objects/TrainStationObject.cs b/OpenLocoTool/Objects/TrainStationObject.cs index cb6ec5b6..55788f1e 100644 --- a/OpenLocoTool/Objects/TrainStationObject.cs +++ b/OpenLocoTool/Objects/TrainStationObject.cs @@ -15,9 +15,8 @@ public enum TrainStationFlags : uint8_t [TypeConverter(typeof(ExpandableObjectConverter))] [LocoStructSize(0xAE)] - [LocoStringTable("Name")] public record TrainStationObject( - //[property: LocoStructOffset(0x00)] string_id Name, + [property: LocoStructOffset(0x00), LocoString, Browsable(false)] string_id Name, [property: LocoStructOffset(0x02)] uint8_t PaintStyle, [property: LocoStructOffset(0x03)] uint8_t var_03, [property: LocoStructOffset(0x04)] uint16_t TrackPieces, @@ -27,7 +26,7 @@ public record TrainStationObject( [property: LocoStructOffset(0x0B)] uint8_t var_0B, [property: LocoStructOffset(0x0C)] RoadStationFlags Flags, [property: LocoStructOffset(0x0D)] uint8_t var_0D, - //[property: LocoStructOffset(0x0E)] uint32_t Image, + [property: LocoStructOffset(0x0E)] uint32_t Image, [property: LocoStructOffset(0x12), LocoArrayLength(4)] uint32_t[] var_12, [property: LocoStructOffset(0x22)] uint8_t NumCompatible, [property: LocoStructOffset(0x23), LocoArrayLength(7)] uint8_t[] Mods, diff --git a/OpenLocoTool/Objects/TreeObject.cs b/OpenLocoTool/Objects/TreeObject.cs index 04bad457..b5250d65 100644 --- a/OpenLocoTool/Objects/TreeObject.cs +++ b/OpenLocoTool/Objects/TreeObject.cs @@ -20,9 +20,8 @@ public enum TreeObjectFlags : uint16_t [TypeConverter(typeof(ExpandableObjectConverter))] [LocoStructSize(0x4C)] - [LocoStringTable("Name")] public record TreeObject( - //[property: LocoStructOffset(0x00)] string_id Name, + [property: LocoStructOffset(0x00), LocoString, Browsable(false)] string_id Name, [property: LocoStructOffset(0x02)] uint8_t var_02, [property: LocoStructOffset(0x03)] uint8_t Height, [property: LocoStructOffset(0x04)] uint8_t var_04, diff --git a/OpenLocoTool/Objects/TunnelObject.cs b/OpenLocoTool/Objects/TunnelObject.cs index 16535fee..f9c599db 100644 --- a/OpenLocoTool/Objects/TunnelObject.cs +++ b/OpenLocoTool/Objects/TunnelObject.cs @@ -7,10 +7,9 @@ namespace OpenLocoTool.Objects { [TypeConverter(typeof(ExpandableObjectConverter))] [LocoStructSize(0x06)] - [LocoStringTable("Name")] public record TunnelObject( - //[property: LocoStructOffset(0x00)] string_id Name, - //[property: LocoStructOffset(0x02)] uint32_t Image + [property: LocoStructOffset(0x00), LocoString, Browsable(false)] string_id Name, + [property: LocoStructOffset(0x02)] uint32_t Image ) : ILocoStruct { public static ObjectType ObjectType => ObjectType.Tunnel; diff --git a/OpenLocoTool/Objects/Vehicle/VehicleObject.cs b/OpenLocoTool/Objects/Vehicle/VehicleObject.cs index b0551657..92ebb08b 100644 --- a/OpenLocoTool/Objects/Vehicle/VehicleObject.cs +++ b/OpenLocoTool/Objects/Vehicle/VehicleObject.cs @@ -6,7 +6,6 @@ namespace OpenLocoTool.Objects { [TypeConverter(typeof(ExpandableObjectConverter))] [LocoStructSize(0x15E)] - [LocoStringTable("Name")] public class VehicleObject : ILocoStruct, ILocoStructVariableData { public static ObjectType ObjectType => ObjectType.Vehicle; @@ -16,9 +15,9 @@ public class VehicleObject : ILocoStruct, ILocoStructVariableData public List CompatibleCargo { get; set; } = []; - public VehicleObject(/*ushort name,*/ TransportMode mode, VehicleType type, byte var_04, byte trackType, byte numMods, byte costIndex, short costFactor, byte reliability, byte runCostIndex, short runCostFactor, byte colourType, byte numCompat, ushort[] compatibleVehicles, byte[] requiredTrackExtras, VehicleObjectUnk[] var_24, BodySprite[] bodySprites, BogieSprite[] bogieSprites, ushort power, short speed, short rackSpeed, ushort weight, VehicleObjectFlags flags, byte[] maxCargo, uint[] cargoTypes, byte[] cargoTypeSpriteOffsets, byte numSimultaneousCargoTypes, SimpleAnimation[] animation, byte var_113, ushort designed, ushort obsolete, byte rackRailType, DrivingSoundType drivingSoundType, byte[] pad_135, byte numStartSounds, byte[] startSounds) + public VehicleObject(ushort name, TransportMode mode, VehicleType type, byte var_04, byte trackType, byte numMods, byte costIndex, short costFactor, byte reliability, byte runCostIndex, short runCostFactor, byte colourType, byte numCompat, ushort[] compatibleVehicles, byte[] requiredTrackExtras, VehicleObjectUnk[] var_24, BodySprite[] bodySprites, BogieSprite[] bogieSprites, ushort power, short speed, short rackSpeed, ushort weight, VehicleObjectFlags flags, byte[] maxCargo, uint[] cargoTypes, byte[] cargoTypeSpriteOffsets, byte numSimultaneousCargoTypes, SimpleAnimation[] animation, byte var_113, ushort designed, ushort obsolete, byte rackRailType, DrivingSoundType drivingSoundType, byte[] pad_135, byte numStartSounds, byte[] startSounds) { - //Name = name; + Name = name; Mode = mode; Type = type; this.var_04 = var_04; @@ -56,7 +55,7 @@ public VehicleObject(/*ushort name,*/ TransportMode mode, VehicleType type, byte StartSounds = startSounds; } - //[LocoStructOffset(0x00)] public string_id Name { get; set; } + [LocoStructOffset(0x00), LocoString, Browsable(false)] public string_id Name { get; set; } [LocoStructOffset(0x02)] public TransportMode Mode { get; set; } [LocoStructOffset(0x03)] public VehicleType Type { get; set; } [LocoStructOffset(0x04)] public uint8_t var_04 { get; set; } @@ -136,7 +135,7 @@ public ReadOnlySpan Load(ReadOnlySpan remainingData) } var ptr = BitConverter.ToUInt16(remainingData[0..2]); - while (ptr != (ushort)0xFFFF) + while (ptr != 0xFFFF) { var cargoMatchFlags = BitConverter.ToUInt16(remainingData[0..2]); CargoMatchFlags.Add(cargoMatchFlags); diff --git a/OpenLocoTool/Objects/WallObject.cs b/OpenLocoTool/Objects/WallObject.cs index 095fdc81..d40bfb1f 100644 --- a/OpenLocoTool/Objects/WallObject.cs +++ b/OpenLocoTool/Objects/WallObject.cs @@ -20,10 +20,9 @@ public enum WallObjectFlags : uint8_t [TypeConverter(typeof(ExpandableObjectConverter))] [LocoStructSize(0x0A)] - [LocoStringTable("Name")] public record WallObject( - //[property: LocoStructOffset(0x00)] string_id Name, - //[property: LocoStructOffset(0x02)] uint32_t Image, + [property: LocoStructOffset(0x00), LocoString, Browsable(false)] string_id Name, + [property: LocoStructOffset(0x02)] uint32_t Image, [property: LocoStructOffset(0x06)] uint8_t var_06, [property: LocoStructOffset(0x07)] WallObjectFlags Flags, [property: LocoStructOffset(0x08)] uint8_t Height, diff --git a/OpenLocoTool/Objects/WaterObject.cs b/OpenLocoTool/Objects/WaterObject.cs index b06c68a0..abdae8a7 100644 --- a/OpenLocoTool/Objects/WaterObject.cs +++ b/OpenLocoTool/Objects/WaterObject.cs @@ -7,14 +7,13 @@ namespace OpenLocoTool.Objects { [TypeConverter(typeof(ExpandableObjectConverter))] [LocoStructSize(0x0E)] - [LocoStringTable("Name")] public record WaterObject( - //[property: LocoStructOffset(0x00)] string_id Name, + [property: LocoStructOffset(0x00), LocoString, Browsable(false)] string_id Name, [property: LocoStructOffset(0x02)] uint8_t CostIndex, [property: LocoStructOffset(0x03)] uint8_t var_03, [property: LocoStructOffset(0x04)] int8_t CostFactor, [property: LocoStructOffset(0x05)] uint8_t var_05, - //[property: LocoStructOffset(0x06)] uint32_t Image, + [property: LocoStructOffset(0x06)] uint32_t Image, [property: LocoStructOffset(0x0A)] uint32_t var_0A ) : ILocoStruct { diff --git a/OpenLocoToolCommon/Logger.cs b/OpenLocoToolCommon/Logger.cs index 91605aa0..9e81571f 100644 --- a/OpenLocoToolCommon/Logger.cs +++ b/OpenLocoToolCommon/Logger.cs @@ -1,6 +1,4 @@ -using System.Runtime.CompilerServices; - -namespace OpenLocoToolCommon +namespace OpenLocoToolCommon { public class LogAddedEventArgs(LogLine log) : EventArgs { diff --git a/OpenLocoToolGui/MainForm.cs b/OpenLocoToolGui/MainForm.cs index 2b5d208a..ed92d6b1 100644 --- a/OpenLocoToolGui/MainForm.cs +++ b/OpenLocoToolGui/MainForm.cs @@ -1,5 +1,6 @@ using NAudio.Wave; using OpenLocoTool; +using OpenLocoTool.Data; using OpenLocoTool.DatFileParsing; using OpenLocoTool.Headers; using OpenLocoTool.Objects; @@ -337,14 +338,22 @@ void InitToolStripMenuItems() } } - // note: doesn't work atm private void saveChangesToolStripMenuItem_Click(object sender, EventArgs e) { - if (pgS5Header.SelectedObject is not ILocoObject obj) + if (CurrentUIObject is not ILocoObject obj) { return; } + if (!SaveEnabledObjects.Types.Contains(obj.S5Header.ObjectType)) + { + var msg = $"Saving is not currently implemented for {obj.S5Header.ObjectType} objects"; + logger.Error(msg); + logger.Info($"Saving is currently supported for the following objects: [{SaveEnabledObjects.Types.Aggregate("", (a, b) => a + ", " + b)}]"); + MessageBox.Show(msg); + return; + } + saveFileDialog1.InitialDirectory = model.Settings.ObjDataDirectory; saveFileDialog1.DefaultExt = "dat"; saveFileDialog1.Filter = "Locomotion DAT files (.dat)|*.dat"; @@ -363,11 +372,12 @@ private void saveChangesToolStripMenuItem_Click(object sender, EventArgs e) InitUI(cbVanillaObjects.Checked, tbFileFilter.Text); } - MessageBox.Show($"File \"{filename}\" saved successfully"); + logger.Info($"File \"{filename}\" saved successfully"); } catch (Exception ex) { - MessageBox.Show($"Error saving \"{filename}\": " + ex.Message); + logger.Error($"Error saving \"{filename}\": {ex.Message}"); + MessageBox.Show($"Error saving \"{filename}\": {ex.Message}"); } } } @@ -449,7 +459,7 @@ void LoadDataDump(string path, bool isG1 = false) static string constructAnnotationText(Annotation annotation) => string.Format("{0} (0x{1:X}-0x{2:X})", annotation.Name, annotation.Start, annotation.End); - + var parents = new Dictionary(); foreach (var annotation in DATDumpAnnotations) @@ -508,7 +518,14 @@ void tv_AfterSelect(object sender, TreeViewEventArgs e) var filename = e.Node.Name; CurrentUIObject = model.LoadAndCacheObject(filename); - LoadDataDump(filename); + try + { + LoadDataDump(filename); + } + catch (Exception ex) + { + logger?.Error(ex, $"Unable to annotate file \"{filename}\""); + } } } diff --git a/OpenLocoToolGui/MainFormModel.cs b/OpenLocoToolGui/MainFormModel.cs index a8434682..cf217c1b 100644 --- a/OpenLocoToolGui/MainFormModel.cs +++ b/OpenLocoToolGui/MainFormModel.cs @@ -15,8 +15,6 @@ namespace OpenLocoToolGui class MainFormModel { private readonly ILogger logger; - private readonly SawyerStreamReader reader; - private readonly SawyerStreamWriter writer; public HeaderIndex HeaderIndex { get; private set; } = []; @@ -68,8 +66,6 @@ private void LoadPalette() public MainFormModel(ILogger logger, string settingsFile) { this.logger = logger; - reader = new SawyerStreamReader(logger); - writer = new SawyerStreamWriter(logger); LoadSettings(settingsFile); @@ -85,7 +81,14 @@ public MainFormModel(ILogger logger, string settingsFile) foreach (var dep in HeaderIndex.Where(kvp => dependentObjectTypes.Contains(kvp.Value.ObjectType))) { + //try + //{ SawyerStreamReader.LoadFull(dep.Key); + //} + //catch (Exception ex) + //{ + // logger.Error($"File=\"{dep}\" Message=\"{ex.Message}\""); + //} } } @@ -189,13 +192,17 @@ void CreateIndex(string[] allFiles, IProgress progress) catch (Exception ex) { logger.Error($"Failed to load \"{file}\"", ex); + + var obj = SawyerStreamReader.LoadHeader(file); + var indexObjectHeader = new IndexObjectHeader(obj.Name, obj.ObjectType, null); + ccHeaderIndex.TryAdd(file, indexObjectHeader); } finally { Interlocked.Increment(ref count); progress.Report(count / (float)allFiles.Length); } - //} + //} }); HeaderIndex = ccHeaderIndex.OrderBy(kvp => kvp.Key).ToDictionary(kvp => kvp.Key, kvp => kvp.Value); @@ -206,7 +213,7 @@ void CreateIndex(string[] allFiles, IProgress progress) } public void SaveFile(string path, ILocoObject obj) - => writer.Save(path, obj); + => SawyerStreamWriter.Save(path, obj); public bool LoadDataDirectory(string directory) { @@ -219,7 +226,7 @@ public bool LoadDataDirectory(string directory) Settings.DataDirectory = directory; // load G1 only for now - G1 = reader.LoadG1(Settings.G1Path); + G1 = SawyerStreamReader.LoadG1(Settings.G1Path); LoadPalette(); // update palette from g1 SaveSettings(); diff --git a/OpenLocoToolGui/StringTableUserControl.cs b/OpenLocoToolGui/StringTableUserControl.cs index 8145c45c..05d5c24f 100644 --- a/OpenLocoToolGui/StringTableUserControl.cs +++ b/OpenLocoToolGui/StringTableUserControl.cs @@ -1,5 +1,4 @@ -using OpenLocoTool; -using OpenLocoTool.DatFileParsing; +using OpenLocoTool.DatFileParsing; using System.ComponentModel; namespace OpenLocoToolGui diff --git a/OpenLocoToolTests/ObjectLoadingTests.cs b/OpenLocoToolTests/ObjectLoadingTests.cs index e1ee8a6c..24cbe997 100644 --- a/OpenLocoToolTests/ObjectLoadingTests.cs +++ b/OpenLocoToolTests/ObjectLoadingTests.cs @@ -1,4 +1,5 @@ using NUnit.Framework; +using NUnit.Framework.Internal; using OpenLocoTool; using OpenLocoTool.DatFileParsing; using OpenLocoTool.Headers; @@ -9,30 +10,26 @@ namespace OpenLocoToolTests [TestFixture] public class ObjectLoadingTests { - static ILocoObject LoadObject(string filename) + static (ILocoObject, T) LoadObject(string filename) where T : ILocoStruct { var fileSize = new FileInfo(filename).Length; var logger = new OpenLocoToolCommon.Logger(); - var ssr = new SawyerStreamReader(logger); - var loaded = SawyerStreamReader.LoadFull(filename); + var loaded = SawyerStreamReader.LoadFull(filename, logger); Assert.That(loaded.ObjectHeader.DataLength, Is.EqualTo(fileSize - S5Header.StructLength - ObjectHeader.StructLength), "ObjectHeader.Length didn't match actual size of struct"); - return loaded; + return (loaded, (T)loaded.Object); } - static T LoadObject(string filename) - => (T)LoadObject(filename).Object; - //[Test] //public void DebuggingLoadObject() //{ // const string testFile = "Q:\\Steam\\steamapps\\common\\Locomotion\\ObjData\\260RENFE.DAT"; - // var obj = LoadObject(testFile); + // var (obj, struc) = LoadObject(testFile); // Assert.Multiple(() => // { - // Assert.That(obj.Name, Is.EqualTo(0), nameof(obj.Name)); + // Assert.That(struc.Name, Is.EqualTo(0), nameof(struc.Name)); // }); //} @@ -40,41 +37,41 @@ static T LoadObject(string filename) public void LoadAirportObject() { const string testFile = "Q:\\Steam\\steamapps\\common\\Locomotion\\ObjData\\AIRPORT1.DAT"; - var obj = LoadObject(testFile); + var (obj, struc) = LoadObject(testFile); Assert.Multiple(() => { - Assert.That(obj.BuildCostFactor, Is.EqualTo(256), nameof(obj.BuildCostFactor)); - Assert.That(obj.SellCostFactor, Is.EqualTo(-192), nameof(obj.SellCostFactor)); - Assert.That(obj.CostIndex, Is.EqualTo(1), nameof(obj.CostIndex)); - Assert.That(obj.var_07, Is.EqualTo(0), nameof(obj.var_07)); - Assert.That(obj.var_0C, Is.EqualTo(0), nameof(obj.var_0C)); - Assert.That(obj.AllowedPlaneTypes, Is.EqualTo(24), nameof(obj.AllowedPlaneTypes)); - Assert.That(obj.NumSpriteSets, Is.EqualTo(94), nameof(obj.NumSpriteSets)); - Assert.That(obj.NumTiles, Is.EqualTo(23), nameof(obj.NumTiles)); - - //Assert.That(obj.var_14, Is.EqualTo(0), nameof(obj.var_14)); - //Assert.That(obj.var_18, Is.EqualTo(0), nameof(obj.var_18)); - //Assert.That(obj.var_1C, Is.EqualTo(0), nameof(obj.var_1C)); - //Assert.That(obj.var_9C, Is.EqualTo(0), nameof(obj.var_9C)); - - Assert.That(obj.LargeTiles, Is.EqualTo(917759), nameof(obj.LargeTiles)); - Assert.That(obj.MinX, Is.EqualTo(-4), nameof(obj.MinX)); - Assert.That(obj.MinY, Is.EqualTo(-4), nameof(obj.MinY)); - Assert.That(obj.MaxX, Is.EqualTo(5), nameof(obj.MaxX)); - Assert.That(obj.MaxY, Is.EqualTo(5), nameof(obj.MaxY)); - Assert.That(obj.DesignedYear, Is.EqualTo(1970), nameof(obj.DesignedYear)); - Assert.That(obj.ObsoleteYear, Is.EqualTo(65535), nameof(obj.ObsoleteYear)); - Assert.That(obj.NumMovementNodes, Is.EqualTo(26), nameof(obj.NumMovementNodes)); - Assert.That(obj.NumMovementEdges, Is.EqualTo(30), nameof(obj.NumMovementEdges)); - - //Assert.That(obj.MovementNodes, Is.EqualTo(0), nameof(obj.MovementNodes)); - //Assert.That(obj.MovementEdges, Is.EqualTo(0), nameof(obj.MovementEdges)); - - Assert.That(obj.pad_B6[0], Is.EqualTo(0), nameof(obj.pad_B6) + "[0]"); - Assert.That(obj.pad_B6[1], Is.EqualTo(19), nameof(obj.pad_B6) + "[1]"); - Assert.That(obj.pad_B6[2], Is.EqualTo(0), nameof(obj.pad_B6) + "[2]"); - Assert.That(obj.pad_B6[3], Is.EqualTo(0), nameof(obj.pad_B6) + "[3]"); + Assert.That(struc.BuildCostFactor, Is.EqualTo(256), nameof(struc.BuildCostFactor)); + Assert.That(struc.SellCostFactor, Is.EqualTo(-192), nameof(struc.SellCostFactor)); + Assert.That(struc.CostIndex, Is.EqualTo(1), nameof(struc.CostIndex)); + Assert.That(struc.var_07, Is.EqualTo(0), nameof(struc.var_07)); + Assert.That(struc.var_0C, Is.EqualTo(0), nameof(struc.var_0C)); + Assert.That(struc.AllowedPlaneTypes, Is.EqualTo(24), nameof(struc.AllowedPlaneTypes)); + Assert.That(struc.NumSpriteSets, Is.EqualTo(94), nameof(struc.NumSpriteSets)); + Assert.That(struc.NumTiles, Is.EqualTo(23), nameof(struc.NumTiles)); + + //Assert.That(struc.var_14, Is.EqualTo(0), nameof(struc.var_14)); + //Assert.That(struc.var_18, Is.EqualTo(0), nameof(struc.var_18)); + //Assert.That(struc.var_1C, Is.EqualTo(0), nameof(struc.var_1C)); + //Assert.That(struc.var_9C, Is.EqualTo(0), nameof(struc.var_9C)); + + Assert.That(struc.LargeTiles, Is.EqualTo(917759), nameof(struc.LargeTiles)); + Assert.That(struc.MinX, Is.EqualTo(-4), nameof(struc.MinX)); + Assert.That(struc.MinY, Is.EqualTo(-4), nameof(struc.MinY)); + Assert.That(struc.MaxX, Is.EqualTo(5), nameof(struc.MaxX)); + 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.NumMovementNodes, Is.EqualTo(26), nameof(struc.NumMovementNodes)); + Assert.That(struc.NumMovementEdges, Is.EqualTo(30), nameof(struc.NumMovementEdges)); + + //Assert.That(struc.MovementNodes, Is.EqualTo(0), nameof(struc.MovementNodes)); + //Assert.That(struc.MovementEdges, Is.EqualTo(0), nameof(struc.MovementEdges)); + + Assert.That(struc.pad_B6[0], Is.EqualTo(0), nameof(struc.pad_B6) + "[0]"); + Assert.That(struc.pad_B6[1], Is.EqualTo(19), nameof(struc.pad_B6) + "[1]"); + Assert.That(struc.pad_B6[2], Is.EqualTo(0), nameof(struc.pad_B6) + "[2]"); + Assert.That(struc.pad_B6[3], Is.EqualTo(0), nameof(struc.pad_B6) + "[3]"); }); } @@ -82,31 +79,31 @@ public void LoadAirportObject() public void LoadBridgeObject() { const string testFile = "Q:\\Steam\\steamapps\\common\\Locomotion\\ObjData\\BRDGBRCK.DAT"; - var obj = LoadObject(testFile); + var (obj, struc) = LoadObject(testFile); Assert.Multiple(() => { - Assert.That(obj.NoRoof, Is.EqualTo(0), nameof(obj.NoRoof)); - - Assert.That(obj.pad_03[0], Is.EqualTo(0), nameof(obj.pad_03) + "[0]"); - Assert.That(obj.pad_03[1], Is.EqualTo(0), nameof(obj.pad_03) + "[1]"); - Assert.That(obj.pad_03[2], Is.EqualTo(0), nameof(obj.pad_03) + "[2]"); - - Assert.That(obj.var_06, Is.EqualTo(16), nameof(obj.var_06)); - Assert.That(obj.SpanLength, Is.EqualTo(1), nameof(obj.SpanLength)); - Assert.That(obj.PillarSpacing, Is.EqualTo(255), nameof(obj.PillarSpacing)); - Assert.That(obj.MaxSpeed, Is.EqualTo(60), nameof(obj.MaxSpeed)); - Assert.That(obj.MaxHeight, Is.EqualTo(10), nameof(obj.MaxHeight)); - Assert.That(obj.CostIndex, Is.EqualTo(1), nameof(obj.CostIndex)); - Assert.That(obj.BaseCostFactor, Is.EqualTo(16), nameof(obj.BaseCostFactor)); - Assert.That(obj.HeightCostFactor, Is.EqualTo(8), nameof(obj.HeightCostFactor)); - Assert.That(obj.SellCostFactor, Is.EqualTo(-12), nameof(obj.SellCostFactor)); - Assert.That(obj.DisabledTrackCfg, Is.EqualTo(0), nameof(obj.DisabledTrackCfg)); - Assert.That(obj.TrackNumCompatible, Is.EqualTo(0), nameof(obj.TrackNumCompatible)); - CollectionAssert.AreEqual(obj.TrackMods, Array.CreateInstance(typeof(byte), 7), nameof(obj.TrackMods)); - Assert.That(obj.RoadNumCompatible, Is.EqualTo(0), nameof(obj.RoadNumCompatible)); - CollectionAssert.AreEqual(obj.RoadMods, Array.CreateInstance(typeof(byte), 7), nameof(obj.RoadMods)); - Assert.That(obj.DesignedYear, Is.EqualTo(0), nameof(obj.DesignedYear)); + Assert.That(struc.NoRoof, Is.EqualTo(0), nameof(struc.NoRoof)); + + Assert.That(struc.pad_03[0], Is.EqualTo(0), nameof(struc.pad_03) + "[0]"); + Assert.That(struc.pad_03[1], Is.EqualTo(0), nameof(struc.pad_03) + "[1]"); + Assert.That(struc.pad_03[2], Is.EqualTo(0), nameof(struc.pad_03) + "[2]"); + + Assert.That(struc.var_06, Is.EqualTo(16), nameof(struc.var_06)); + Assert.That(struc.SpanLength, Is.EqualTo(1), nameof(struc.SpanLength)); + Assert.That(struc.PillarSpacing, Is.EqualTo(255), nameof(struc.PillarSpacing)); + Assert.That(struc.MaxSpeed, Is.EqualTo(60), nameof(struc.MaxSpeed)); + Assert.That(struc.MaxHeight, Is.EqualTo(10), nameof(struc.MaxHeight)); + Assert.That(struc.CostIndex, Is.EqualTo(1), nameof(struc.CostIndex)); + Assert.That(struc.BaseCostFactor, Is.EqualTo(16), nameof(struc.BaseCostFactor)); + Assert.That(struc.HeightCostFactor, Is.EqualTo(8), nameof(struc.HeightCostFactor)); + Assert.That(struc.SellCostFactor, Is.EqualTo(-12), nameof(struc.SellCostFactor)); + Assert.That(struc.DisabledTrackCfg, Is.EqualTo(0), nameof(struc.DisabledTrackCfg)); + Assert.That(struc.TrackNumCompatible, Is.EqualTo(0), nameof(struc.TrackNumCompatible)); + CollectionAssert.AreEqual(struc.TrackMods, Array.CreateInstance(typeof(byte), 7), nameof(struc.TrackMods)); + Assert.That(struc.RoadNumCompatible, Is.EqualTo(0), nameof(struc.RoadNumCompatible)); + CollectionAssert.AreEqual(struc.RoadMods, Array.CreateInstance(typeof(byte), 7), nameof(struc.RoadMods)); + Assert.That(struc.DesignedYear, Is.EqualTo(0), nameof(struc.DesignedYear)); }); } @@ -114,34 +111,34 @@ public void LoadBridgeObject() public void LoadBuildingObject() { const string testFile = "Q:\\Steam\\steamapps\\common\\Locomotion\\ObjData\\HQ1.DAT"; - var obj = LoadObject(testFile); + var (obj, struc) = LoadObject(testFile); Assert.Multiple(() => { - Assert.That(obj.var_06, Is.EqualTo(16), nameof(obj.var_06)); - Assert.That(obj.NumVariations, Is.EqualTo(5), nameof(obj.NumVariations)); - CollectionAssert.AreEqual(obj.VariationHeights, Array.CreateInstance(typeof(byte), 4), nameof(obj.VariationHeights)); - CollectionAssert.AreEqual(obj.var_0C, Array.CreateInstance(typeof(byte), 2), nameof(obj.var_0C)); - Assert.That(obj.Colours, Is.EqualTo(0), nameof(obj.Colours)); - Assert.That(obj.DesignedYear, Is.EqualTo(0), nameof(obj.DesignedYear)); - Assert.That(obj.ObsoleteYear, Is.EqualTo(65535), nameof(obj.ObsoleteYear)); - Assert.That(obj.Flags, Is.EqualTo(BuildingObjectFlags.LargeTile | BuildingObjectFlags.MiscBuilding | BuildingObjectFlags.IsHeadquarters), nameof(obj.Flags)); - Assert.That(obj.ClearCostIndex, Is.EqualTo(1), nameof(obj.ClearCostIndex)); - Assert.That(obj.ClearCostFactor, Is.EqualTo(0), nameof(obj.ClearCostFactor)); - Assert.That(obj.ScaffoldingSegmentType, Is.EqualTo(1), nameof(obj.ScaffoldingSegmentType)); - Assert.That(obj.ScaffoldingColour, Is.EqualTo(Colour.yellow), nameof(obj.ScaffoldingColour)); - - Assert.That(obj.pad_9E[0], Is.EqualTo(3), nameof(obj.pad_9E) + "[0]"); - Assert.That(obj.pad_9E[1], Is.EqualTo(3), nameof(obj.pad_9E) + "[1]"); - - CollectionAssert.AreEqual(obj.ProducedQuantity, Array.CreateInstance(typeof(byte), 2), nameof(obj.ProducedQuantity)); - CollectionAssert.AreEqual(obj.ProducedCargoType, Array.CreateInstance(typeof(byte), 2), nameof(obj.ProducedCargoType)); - CollectionAssert.AreEqual(obj.var_A6, Array.CreateInstance(typeof(byte), 2), nameof(obj.var_A6)); - CollectionAssert.AreEqual(obj.var_A8, Array.CreateInstance(typeof(byte), 2), nameof(obj.var_A8)); - CollectionAssert.AreEqual(obj.var_A4, Array.CreateInstance(typeof(byte), 2), nameof(obj.var_A4)); - Assert.That(obj.DemolishRatingReduction, Is.EqualTo(0), nameof(obj.DemolishRatingReduction)); - Assert.That(obj.var_AC, Is.EqualTo(255), nameof(obj.var_AC)); - Assert.That(obj.var_AD, Is.EqualTo(0), nameof(obj.var_AD)); + Assert.That(struc.var_06, Is.EqualTo(16), nameof(struc.var_06)); + Assert.That(struc.NumVariations, Is.EqualTo(5), nameof(struc.NumVariations)); + CollectionAssert.AreEqual(struc.VariationHeights, Array.CreateInstance(typeof(byte), 4), nameof(struc.VariationHeights)); + CollectionAssert.AreEqual(struc.var_0C, Array.CreateInstance(typeof(byte), 2), nameof(struc.var_0C)); + Assert.That(struc.Colours, Is.EqualTo(0), nameof(struc.Colours)); + Assert.That(struc.DesignedYear, Is.EqualTo(0), nameof(struc.DesignedYear)); + Assert.That(struc.ObsoleteYear, Is.EqualTo(65535), nameof(struc.ObsoleteYear)); + Assert.That(struc.Flags, Is.EqualTo(BuildingObjectFlags.LargeTile | BuildingObjectFlags.MiscBuilding | BuildingObjectFlags.IsHeadquarters), nameof(struc.Flags)); + Assert.That(struc.ClearCostIndex, Is.EqualTo(1), nameof(struc.ClearCostIndex)); + Assert.That(struc.ClearCostFactor, Is.EqualTo(0), nameof(struc.ClearCostFactor)); + Assert.That(struc.ScaffoldingSegmentType, Is.EqualTo(1), nameof(struc.ScaffoldingSegmentType)); + Assert.That(struc.ScaffoldingColour, Is.EqualTo(Colour.yellow), nameof(struc.ScaffoldingColour)); + + Assert.That(struc.pad_9E[0], Is.EqualTo(3), nameof(struc.pad_9E) + "[0]"); + Assert.That(struc.pad_9E[1], Is.EqualTo(3), nameof(struc.pad_9E) + "[1]"); + + CollectionAssert.AreEqual(struc.ProducedQuantity, Array.CreateInstance(typeof(byte), 2), nameof(struc.ProducedQuantity)); + CollectionAssert.AreEqual(struc.ProducedCargoType, Array.CreateInstance(typeof(byte), 2), nameof(struc.ProducedCargoType)); + CollectionAssert.AreEqual(struc.var_A6, Array.CreateInstance(typeof(byte), 2), nameof(struc.var_A6)); + CollectionAssert.AreEqual(struc.var_A8, Array.CreateInstance(typeof(byte), 2), nameof(struc.var_A8)); + CollectionAssert.AreEqual(struc.var_A4, Array.CreateInstance(typeof(byte), 2), nameof(struc.var_A4)); + Assert.That(struc.DemolishRatingReduction, Is.EqualTo(0), nameof(struc.DemolishRatingReduction)); + Assert.That(struc.var_AC, Is.EqualTo(255), nameof(struc.var_AC)); + Assert.That(struc.var_AD, Is.EqualTo(0), nameof(struc.var_AD)); }); } @@ -149,24 +146,24 @@ public void LoadBuildingObject() public void LoadCargoObject() { const string testFile = "Q:\\Steam\\steamapps\\common\\Locomotion\\ObjData\\CHEMICAL.DAT"; - var obj = LoadObject(testFile); + var (obj, struc) = LoadObject(testFile); Assert.Multiple(() => { - Assert.That(obj.var_02, Is.EqualTo(256), nameof(obj.var_02)); - Assert.That(obj.var_04, Is.EqualTo(64), nameof(obj.var_04)); - Assert.That(obj.UnitInlineSprite, Is.EqualTo(0), nameof(obj.UnitInlineSprite)); - Assert.That(obj.MatchFlags, Is.EqualTo(4), nameof(obj.MatchFlags)); - Assert.That(obj.Flags, Is.EqualTo(CargoObjectFlags.Delivering), nameof(obj.Flags)); - Assert.That(obj.NumPlatformVariations, Is.EqualTo(1), nameof(obj.NumPlatformVariations)); - Assert.That(obj.var_14, Is.EqualTo(4), nameof(obj.var_14)); - Assert.That(obj.PremiumDays, Is.EqualTo(10), nameof(obj.PremiumDays)); - Assert.That(obj.MaxNonPremiumDays, Is.EqualTo(30), nameof(obj.MaxNonPremiumDays)); - Assert.That(obj.MaxPremiumRate, Is.EqualTo(128), nameof(obj.MaxPremiumRate)); - Assert.That(obj.PenaltyRate, Is.EqualTo(256), nameof(obj.PenaltyRate)); - Assert.That(obj.PaymentFactor, Is.EqualTo(62), nameof(obj.PaymentFactor)); - Assert.That(obj.PaymentIndex, Is.EqualTo(10), nameof(obj.PaymentIndex)); - Assert.That(obj.UnitSize, Is.EqualTo(10), nameof(obj.UnitSize)); + Assert.That(struc.var_02, Is.EqualTo(256), nameof(struc.var_02)); + Assert.That(struc.var_04, Is.EqualTo(64), nameof(struc.var_04)); + Assert.That(struc.UnitInlineSprite, Is.EqualTo(0), nameof(struc.UnitInlineSprite)); + Assert.That(struc.MatchFlags, Is.EqualTo(4), nameof(struc.MatchFlags)); + Assert.That(struc.Flags, Is.EqualTo(CargoObjectFlags.Delivering), nameof(struc.Flags)); + Assert.That(struc.NumPlatformVariations, Is.EqualTo(1), nameof(struc.NumPlatformVariations)); + Assert.That(struc.var_14, Is.EqualTo(4), nameof(struc.var_14)); + Assert.That(struc.PremiumDays, Is.EqualTo(10), nameof(struc.PremiumDays)); + Assert.That(struc.MaxNonPremiumDays, Is.EqualTo(30), nameof(struc.MaxNonPremiumDays)); + Assert.That(struc.MaxPremiumRate, Is.EqualTo(128), nameof(struc.MaxPremiumRate)); + Assert.That(struc.PenaltyRate, Is.EqualTo(256), nameof(struc.PenaltyRate)); + Assert.That(struc.PaymentFactor, Is.EqualTo(62), nameof(struc.PaymentFactor)); + Assert.That(struc.PaymentIndex, Is.EqualTo(10), nameof(struc.PaymentIndex)); + Assert.That(struc.UnitSize, Is.EqualTo(10), nameof(struc.UnitSize)); }); } @@ -174,25 +171,25 @@ public void LoadCargoObject() public void LoadCliffEdgeObject() { const string testFile = "Q:\\Steam\\steamapps\\common\\Locomotion\\ObjData\\LSBROWN.DAT"; - var obj = LoadObject(testFile); + var (obj, struc) = LoadObject(testFile); } [Test] public void LoadClimateObject() { const string testFile = "Q:\\Steam\\steamapps\\common\\Locomotion\\ObjData\\CLIM1.DAT"; - var obj = LoadObject(testFile); + var (obj, struc) = LoadObject(testFile); Assert.Multiple(() => { - Assert.That(obj.FirstSeason, Is.EqualTo(1), nameof(obj.FirstSeason)); - Assert.That(obj.SeasonLengths[0], Is.EqualTo(57), nameof(obj.SeasonLengths) + "[0]"); - Assert.That(obj.SeasonLengths[1], Is.EqualTo(80), nameof(obj.SeasonLengths) + "[1]"); - Assert.That(obj.SeasonLengths[2], Is.EqualTo(100), nameof(obj.SeasonLengths) + "[2]"); - Assert.That(obj.SeasonLengths[3], Is.EqualTo(80), nameof(obj.SeasonLengths) + "[3]"); - Assert.That(obj.WinterSnowLine, Is.EqualTo(48), nameof(obj.WinterSnowLine)); - Assert.That(obj.SummerSnowLine, Is.EqualTo(76), nameof(obj.SummerSnowLine)); - Assert.That(obj.pad_09, Is.EqualTo(0), nameof(obj.pad_09)); + Assert.That(struc.FirstSeason, Is.EqualTo(1), nameof(struc.FirstSeason)); + Assert.That(struc.SeasonLengths[0], Is.EqualTo(57), nameof(struc.SeasonLengths) + "[0]"); + Assert.That(struc.SeasonLengths[1], Is.EqualTo(80), nameof(struc.SeasonLengths) + "[1]"); + Assert.That(struc.SeasonLengths[2], Is.EqualTo(100), nameof(struc.SeasonLengths) + "[2]"); + Assert.That(struc.SeasonLengths[3], Is.EqualTo(80), nameof(struc.SeasonLengths) + "[3]"); + Assert.That(struc.WinterSnowLine, Is.EqualTo(48), nameof(struc.WinterSnowLine)); + Assert.That(struc.SummerSnowLine, Is.EqualTo(76), nameof(struc.SummerSnowLine)); + Assert.That(struc.pad_09, Is.EqualTo(0), nameof(struc.pad_09)); }); } @@ -200,18 +197,18 @@ public void LoadClimateObject() public void LoadCompetitorObject() { const string testFile = "Q:\\Steam\\steamapps\\common\\Locomotion\\ObjData\\COMP1.DAT"; - var obj = LoadObject(testFile); + var (obj, struc) = LoadObject(testFile); Assert.Multiple(() => { - Assert.That(obj.var_04, Is.EqualTo(6672), nameof(obj.var_04)); - Assert.That(obj.var_08, Is.EqualTo(2053), nameof(obj.var_08)); - Assert.That(obj.Emotions, Is.EqualTo(511), nameof(obj.Emotions)); - CollectionAssert.AreEqual(obj.Images, Array.CreateInstance(typeof(byte), 9), nameof(obj.Images)); - Assert.That(obj.Intelligence, Is.EqualTo(7), nameof(obj.Intelligence)); - Assert.That(obj.Aggressiveness, Is.EqualTo(5), nameof(obj.Aggressiveness)); - Assert.That(obj.Competitiveness, Is.EqualTo(6), nameof(obj.Competitiveness)); - Assert.That(obj.var_37, Is.EqualTo(0), nameof(obj.var_37)); + Assert.That(struc.var_04, Is.EqualTo(6672), nameof(struc.var_04)); + Assert.That(struc.var_08, Is.EqualTo(2053), nameof(struc.var_08)); + Assert.That(struc.Emotions, Is.EqualTo(511), nameof(struc.Emotions)); + CollectionAssert.AreEqual(struc.Images, Array.CreateInstance(typeof(byte), 9), nameof(struc.Images)); + Assert.That(struc.Intelligence, Is.EqualTo(7), nameof(struc.Intelligence)); + Assert.That(struc.Aggressiveness, Is.EqualTo(5), nameof(struc.Aggressiveness)); + Assert.That(struc.Competitiveness, Is.EqualTo(6), nameof(struc.Competitiveness)); + Assert.That(struc.var_37, Is.EqualTo(0), nameof(struc.var_37)); }); } @@ -219,13 +216,13 @@ public void LoadCompetitorObject() public void LoadCurrencyObject() { const string testFile = "Q:\\Steam\\steamapps\\common\\Locomotion\\ObjData\\CURRDOLL.DAT"; - var obj = LoadObject(testFile); + var (obj, struc) = LoadObject(testFile); Assert.Multiple(() => { - Assert.That(obj.ObjectIcon, Is.EqualTo(0), nameof(obj.ObjectIcon)); - Assert.That(obj.Separator, Is.EqualTo(0), nameof(obj.Separator)); - Assert.That(obj.Factor, Is.EqualTo(1), nameof(obj.Factor)); + Assert.That(struc.ObjectIcon, Is.EqualTo(0), nameof(struc.ObjectIcon)); + Assert.That(struc.Separator, Is.EqualTo(0), nameof(struc.Separator)); + Assert.That(struc.Factor, Is.EqualTo(1), nameof(struc.Factor)); }); } @@ -233,26 +230,26 @@ public void LoadCurrencyObject() public void LoadDockObject() { const string testFile = "Q:\\Steam\\steamapps\\common\\Locomotion\\ObjData\\SHIPST1.DAT"; - var obj = LoadObject(testFile); + var (obj, struc) = LoadObject(testFile); Assert.Multiple(() => { - Assert.That(obj.BuildCostFactor, Is.EqualTo(38), nameof(obj.BuildCostFactor)); - Assert.That(obj.SellCostFactor, Is.EqualTo(-35), nameof(obj.SellCostFactor)); - Assert.That(obj.CostIndex, Is.EqualTo(1), nameof(obj.CostIndex)); - Assert.That(obj.var_07, Is.EqualTo(0), nameof(obj.var_07)); - Assert.That(obj.var_0C, Is.EqualTo(0), nameof(obj.var_0C)); - Assert.That(obj.Flags, Is.EqualTo(DockObjectFlags.None), nameof(obj.Flags)); - Assert.That(obj.NumAux01, Is.EqualTo(2), nameof(obj.NumAux01)); - Assert.That(obj.NumAux02Ent, Is.EqualTo(1), nameof(obj.NumAux02Ent)); - - //Assert.That(obj.var_14, Is.EqualTo(1), nameof(obj.var_14)); - //Assert.That(obj.var_14, Is.EqualTo(1), nameof(obj.var_18)); - //Assert.That(obj.var_1C[0], Is.EqualTo(1), nameof(obj.var_1C[0])); - - Assert.That(obj.DesignedYear, Is.EqualTo(0), nameof(obj.DesignedYear)); - Assert.That(obj.ObsoleteYear, Is.EqualTo(65535), nameof(obj.ObsoleteYear)); - Assert.That(obj.BoatPosition, Is.EqualTo(new Pos2(48, 0)), nameof(obj.BoatPosition)); + Assert.That(struc.BuildCostFactor, Is.EqualTo(38), nameof(struc.BuildCostFactor)); + Assert.That(struc.SellCostFactor, Is.EqualTo(-35), nameof(struc.SellCostFactor)); + Assert.That(struc.CostIndex, Is.EqualTo(1), nameof(struc.CostIndex)); + Assert.That(struc.var_07, Is.EqualTo(0), nameof(struc.var_07)); + Assert.That(struc.var_0C, Is.EqualTo(0), nameof(struc.var_0C)); + Assert.That(struc.Flags, Is.EqualTo(DockObjectFlags.None), nameof(struc.Flags)); + Assert.That(struc.NumAux01, Is.EqualTo(2), nameof(struc.NumAux01)); + Assert.That(struc.NumAux02Ent, Is.EqualTo(1), nameof(struc.NumAux02Ent)); + + //Assert.That(struc.var_14, Is.EqualTo(1), nameof(struc.var_14)); + //Assert.That(struc.var_14, Is.EqualTo(1), nameof(struc.var_18)); + //Assert.That(struc.var_1C[0], Is.EqualTo(1), nameof(struc.var_1C[0])); + + Assert.That(struc.DesignedYear, Is.EqualTo(0), nameof(struc.DesignedYear)); + Assert.That(struc.ObsoleteYear, Is.EqualTo(65535), nameof(struc.ObsoleteYear)); + Assert.That(struc.BoatPosition, Is.EqualTo(new Pos2(48, 0)), nameof(struc.BoatPosition)); }); } @@ -260,14 +257,14 @@ public void LoadDockObject() public void LoadHillShapesObject() { const string testFile = "Q:\\Steam\\steamapps\\common\\Locomotion\\ObjData\\HS1.DAT"; - var obj = LoadObject(testFile); + var (obj, struc) = LoadObject(testFile); Assert.Multiple(() => { - Assert.That(obj.HillHeightMapCount, Is.EqualTo(2), nameof(obj.HillHeightMapCount)); - Assert.That(obj.MountainHeightMapCount, Is.EqualTo(2), nameof(obj.MountainHeightMapCount)); - Assert.That(obj.var_08, Is.EqualTo(0), nameof(obj.var_08)); - CollectionAssert.AreEqual(obj.pad_0C, Array.CreateInstance(typeof(byte), 2), nameof(obj.pad_0C)); + 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)); + CollectionAssert.AreEqual(struc.pad_0C, Array.CreateInstance(typeof(byte), 2), nameof(struc.pad_0C)); }); } @@ -281,25 +278,25 @@ public void LoadHillShapesObject() public void LoadLandObject() { const string testFile = "Q:\\Steam\\steamapps\\common\\Locomotion\\ObjData\\GRASS1.DAT"; - var obj = LoadObject(testFile); + var (obj, struc) = LoadObject(testFile); Assert.Multiple(() => { - Assert.That(obj.CostIndex, Is.EqualTo(2), nameof(obj.CostIndex)); - Assert.That(obj.var_03, Is.EqualTo(5), nameof(obj.var_03)); - Assert.That(obj.var_04, Is.EqualTo(1), nameof(obj.var_04)); - Assert.That(obj.Flags, Is.EqualTo(LandObjectFlags.unk0), nameof(obj.Flags)); - Assert.That(obj.var_06, Is.EqualTo(0), nameof(obj.var_06)); - Assert.That(obj.var_07, Is.EqualTo(0), nameof(obj.var_07)); - Assert.That(obj.CostFactor, Is.EqualTo(20), nameof(obj.CostFactor)); - Assert.That(obj.pad_09, Is.EqualTo(0), nameof(obj.pad_09)); - Assert.That(obj.var_0E, Is.EqualTo(0), nameof(obj.var_0E)); - Assert.That(obj.var_12, Is.EqualTo(0), nameof(obj.var_12)); - Assert.That(obj.var_16, Is.EqualTo(0), nameof(obj.var_16)); - Assert.That(obj.pad_1A, Is.EqualTo(0), nameof(obj.pad_1A)); - Assert.That(obj.NumVariations, Is.EqualTo(3), nameof(obj.NumVariations)); - Assert.That(obj.VariationLikelihood, Is.EqualTo(10), nameof(obj.VariationLikelihood)); - Assert.That(obj.pad_1D, Is.EqualTo(0), nameof(obj.pad_1D)); + Assert.That(struc.CostIndex, Is.EqualTo(2), nameof(struc.CostIndex)); + Assert.That(struc.var_03, Is.EqualTo(5), nameof(struc.var_03)); + Assert.That(struc.var_04, Is.EqualTo(1), nameof(struc.var_04)); + Assert.That(struc.Flags, Is.EqualTo(LandObjectFlags.unk0), nameof(struc.Flags)); + Assert.That(struc.var_06, Is.EqualTo(0), nameof(struc.var_06)); + Assert.That(struc.var_07, Is.EqualTo(0), nameof(struc.var_07)); + Assert.That(struc.CostFactor, Is.EqualTo(20), nameof(struc.CostFactor)); + Assert.That(struc.pad_09, Is.EqualTo(0), nameof(struc.pad_09)); + Assert.That(struc.var_0E, Is.EqualTo(0), nameof(struc.var_0E)); + Assert.That(struc.var_12, Is.EqualTo(0), nameof(struc.var_12)); + Assert.That(struc.var_16, Is.EqualTo(0), nameof(struc.var_16)); + Assert.That(struc.pad_1A, Is.EqualTo(0), nameof(struc.pad_1A)); + Assert.That(struc.NumVariations, Is.EqualTo(3), nameof(struc.NumVariations)); + Assert.That(struc.VariationLikelihood, Is.EqualTo(10), nameof(struc.VariationLikelihood)); + Assert.That(struc.pad_1D, Is.EqualTo(0), nameof(struc.pad_1D)); }); } @@ -307,22 +304,22 @@ public void LoadLandObject() public void LoadLevelCrossingObject() { const string testFile = "Q:\\Steam\\steamapps\\common\\Locomotion\\ObjData\\LCROSS1.DAT"; - var obj = LoadObject(testFile); + var (obj, struc) = LoadObject(testFile); Assert.Multiple(() => { - Assert.That(obj.CostFactor, Is.EqualTo(30), nameof(obj.CostFactor)); - Assert.That(obj.SellCostFactor, Is.EqualTo(-10), nameof(obj.SellCostFactor)); - Assert.That(obj.CostIndex, Is.EqualTo(1), nameof(obj.CostIndex)); + Assert.That(struc.CostFactor, Is.EqualTo(30), nameof(struc.CostFactor)); + Assert.That(struc.SellCostFactor, Is.EqualTo(-10), nameof(struc.SellCostFactor)); + Assert.That(struc.CostIndex, Is.EqualTo(1), nameof(struc.CostIndex)); - Assert.That(obj.AnimationSpeed, Is.EqualTo(3), nameof(obj.AnimationSpeed)); - Assert.That(obj.ClosingFrames, Is.EqualTo(4), nameof(obj.ClosingFrames)); - Assert.That(obj.ClosedFrames, Is.EqualTo(11), nameof(obj.ClosedFrames)); + Assert.That(struc.AnimationSpeed, Is.EqualTo(3), nameof(struc.AnimationSpeed)); + Assert.That(struc.ClosingFrames, Is.EqualTo(4), nameof(struc.ClosingFrames)); + Assert.That(struc.ClosedFrames, Is.EqualTo(11), nameof(struc.ClosedFrames)); - Assert.That(obj.pad_0A[0], Is.EqualTo(3), nameof(obj.pad_0A) + "[0]"); - Assert.That(obj.pad_0A[1], Is.EqualTo(0), nameof(obj.pad_0A) + "[1]"); + Assert.That(struc.pad_0A[0], Is.EqualTo(3), nameof(struc.pad_0A) + "[0]"); + Assert.That(struc.pad_0A[1], Is.EqualTo(0), nameof(struc.pad_0A) + "[1]"); - Assert.That(obj.DesignedYear, Is.EqualTo(1955), nameof(obj.DesignedYear)); + Assert.That(struc.DesignedYear, Is.EqualTo(1955), nameof(struc.DesignedYear)); }); } @@ -330,14 +327,14 @@ public void LoadLevelCrossingObject() public void LoadRegionObject() { const string testFile = "Q:\\Steam\\steamapps\\common\\Locomotion\\ObjData\\REGUK.DAT"; - var obj = LoadObject(testFile); + var (obj, struc) = LoadObject(testFile); Assert.Multiple(() => { - CollectionAssert.AreEqual(obj.pad_06, Array.CreateInstance(typeof(byte), 2), nameof(obj.pad_06)); - Assert.That(obj.var_08, Is.EqualTo(1), nameof(obj.var_08)); - CollectionAssert.AreEqual(obj.var_09, Array.CreateInstance(typeof(byte), 4), nameof(obj.var_09)); - CollectionAssert.AreEqual(obj.pad_0D, Array.CreateInstance(typeof(byte), 5), nameof(obj.pad_0D)); + CollectionAssert.AreEqual(struc.pad_06, Array.CreateInstance(typeof(byte), 2), nameof(struc.pad_06)); + Assert.That(struc.var_08, Is.EqualTo(1), nameof(struc.var_08)); + CollectionAssert.AreEqual(struc.var_09, Array.CreateInstance(typeof(byte), 4), nameof(struc.var_09)); + CollectionAssert.AreEqual(struc.pad_0D, Array.CreateInstance(typeof(byte), 5), nameof(struc.pad_0D)); }); } @@ -345,16 +342,16 @@ public void LoadRegionObject() public void LoadRoadExtraObject() { const string testFile = "Q:\\Steam\\steamapps\\common\\Locomotion\\ObjData\\RDEXCAT1.DAT"; - var obj = LoadObject(testFile); + var (obj, struc) = LoadObject(testFile); Assert.Multiple(() => { - Assert.That(obj.RoadPieces, Is.EqualTo(127), nameof(obj.RoadPieces)); - Assert.That(obj.PaintStyle, Is.EqualTo(1), nameof(obj.PaintStyle)); - Assert.That(obj.CostIndex, Is.EqualTo(1), nameof(obj.CostIndex)); - Assert.That(obj.BuildCostFactor, Is.EqualTo(4), nameof(obj.BuildCostFactor)); - Assert.That(obj.SellCostFactor, Is.EqualTo(-3), nameof(obj.SellCostFactor)); - Assert.That(obj.var_0E, Is.EqualTo(0), nameof(obj.var_0E)); + Assert.That(struc.RoadPieces, Is.EqualTo(127), nameof(struc.RoadPieces)); + Assert.That(struc.PaintStyle, Is.EqualTo(1), nameof(struc.PaintStyle)); + Assert.That(struc.CostIndex, Is.EqualTo(1), nameof(struc.CostIndex)); + Assert.That(struc.BuildCostFactor, Is.EqualTo(4), nameof(struc.BuildCostFactor)); + Assert.That(struc.SellCostFactor, Is.EqualTo(-3), nameof(struc.SellCostFactor)); + Assert.That(struc.var_0E, Is.EqualTo(0), nameof(struc.var_0E)); }); } @@ -368,17 +365,17 @@ public void LoadRoadExtraObject() public void LoadScaffoldingObject() { const string testFile = "Q:\\Steam\\steamapps\\common\\Locomotion\\ObjData\\SCAFDEF.DAT"; - var obj = LoadObject(testFile); + var (obj, struc) = LoadObject(testFile); Assert.Multiple(() => { - Assert.That(obj.SegmentHeights[0], Is.EqualTo(16), nameof(obj.SegmentHeights) + "[0]"); - Assert.That(obj.SegmentHeights[1], Is.EqualTo(16), nameof(obj.SegmentHeights) + "[1]"); - Assert.That(obj.SegmentHeights[2], Is.EqualTo(32), nameof(obj.SegmentHeights) + "[2]"); + Assert.That(struc.SegmentHeights[0], Is.EqualTo(16), nameof(struc.SegmentHeights) + "[0]"); + Assert.That(struc.SegmentHeights[1], Is.EqualTo(16), nameof(struc.SegmentHeights) + "[1]"); + Assert.That(struc.SegmentHeights[2], Is.EqualTo(32), nameof(struc.SegmentHeights) + "[2]"); - Assert.That(obj.RoofHeights[0], Is.EqualTo(0), nameof(obj.RoofHeights) + "[0]"); - Assert.That(obj.RoofHeights[1], Is.EqualTo(0), nameof(obj.RoofHeights) + "[1]"); - Assert.That(obj.RoofHeights[2], Is.EqualTo(14), nameof(obj.RoofHeights) + "[2]"); + Assert.That(struc.RoofHeights[0], Is.EqualTo(0), nameof(struc.RoofHeights) + "[0]"); + Assert.That(struc.RoofHeights[1], Is.EqualTo(0), nameof(struc.RoofHeights) + "[1]"); + Assert.That(struc.RoofHeights[2], Is.EqualTo(14), nameof(struc.RoofHeights) + "[2]"); }); } @@ -386,19 +383,16 @@ public void LoadScaffoldingObject() public void LoadScenarioTextObject() { const string testFile = "Q:\\Steam\\steamapps\\common\\Locomotion\\ObjData\\STEX000.DAT"; - var obj = LoadObject(testFile); + var (obj, struc) = LoadObject(testFile); - Assert.Multiple(() => - { - Assert.That(obj.pad_04, Is.EqualTo(0), nameof(obj.pad_04)); - }); + Assert.That(struc.pad_04, Is.EqualTo(0), nameof(struc.pad_04)); } [Test] public void LoadSnowObject() { const string testFile = "Q:\\Steam\\steamapps\\common\\Locomotion\\ObjData\\SNOW.DAT"; - var obj = LoadObject(testFile); + var (obj, struc) = LoadObject(testFile); Assert.Pass(); } @@ -412,16 +406,90 @@ public void LoadSnowObject() public void LoadStreetLightObject() { const string testFile = "Q:\\Steam\\steamapps\\common\\Locomotion\\ObjData\\SLIGHT1.DAT"; - var obj = LoadObject(testFile); + var (obj, struc) = LoadObject(testFile); Assert.Multiple(() => { - Assert.That(obj.DesignedYear[0], Is.EqualTo(1900), nameof(obj.DesignedYear) + "[0]"); - Assert.That(obj.DesignedYear[1], Is.EqualTo(1950), nameof(obj.DesignedYear) + "[1]"); - Assert.That(obj.DesignedYear[2], Is.EqualTo(1985), nameof(obj.DesignedYear) + "[2]"); + Assert.That(struc.DesignedYear[0], Is.EqualTo(1900), nameof(struc.DesignedYear) + "[0]"); + Assert.That(struc.DesignedYear[1], Is.EqualTo(1950), nameof(struc.DesignedYear) + "[1]"); + Assert.That(struc.DesignedYear[2], Is.EqualTo(1985), nameof(struc.DesignedYear) + "[2]"); + + Assert.That(struc.Image, Is.EqualTo(0)); + + Assert.That(obj.StringTable["Name"].Count, Is.EqualTo(2)); + Assert.That(obj.StringTable["Name"][LanguageId.english_uk], Is.EqualTo("Street Lights")); + Assert.That(obj.StringTable["Name"][LanguageId.english_us], Is.EqualTo("Street Lights")); }); } + [Test] + public void SaveStreetLightObject() + { + // load + const string testFile = "Q:\\Steam\\steamapps\\common\\Locomotion\\ObjData\\SLIGHT1.DAT"; + + var (obj, struc) = LoadObject(testFile); + + // struct write only + //var bytes = SawyerStreamWriter.WriteLocoObject(obj); + //CollectionAssert.AreEqual((byte[])[0, 0, 108, 7, 158, 7, 193, 7, 0, 0, 0, 0], bytes.ToArray()); + + // save + var tempFile = Path.GetTempFileName(); + SawyerStreamWriter.Save(tempFile, obj); + + // load the saved object + var (obj2, struc2) = LoadObject(tempFile); + + // this is just the asserts from LoadStreetLightObject + Assert.Multiple(() => + { + Assert.That(struc2.DesignedYear[0], Is.EqualTo(1900), nameof(struc2.DesignedYear) + "[0]"); + Assert.That(struc2.DesignedYear[1], Is.EqualTo(1950), nameof(struc2.DesignedYear) + "[1]"); + Assert.That(struc2.DesignedYear[2], Is.EqualTo(1985), nameof(struc2.DesignedYear) + "[2]"); + + Assert.That(struc2.Image, Is.EqualTo(0)); + + Assert.That(obj2.StringTable["Name"].Count, Is.EqualTo(2)); + Assert.That(obj2.StringTable["Name"][LanguageId.english_uk], Is.EqualTo("Street Lights")); + Assert.That(obj2.StringTable["Name"][LanguageId.english_us], Is.EqualTo("Street Lights")); + }); + } + + [Test] + public void SaveStreetLightObject2() + { + // load + const string testFile = "Q:\\Steam\\steamapps\\common\\Locomotion\\ObjData\\SLIGHT1.DAT"; + + var (obj, struc) = LoadObject(testFile); + + // save + var bytesSource = SawyerStreamReader.LoadDecode(testFile); + var bytesDest = SawyerStreamWriter.WriteLocoObject(obj).ToArray(); + + var saveA = "Q:\\Games\\Locomotion\\ExperimentalObjects\\original.dat"; + var saveB = "Q:\\Games\\Locomotion\\ExperimentalObjects\\saved.dat"; + + File.WriteAllBytes(saveA, bytesSource); + File.WriteAllBytes(saveB, bytesDest); + + var headerA = SawyerStreamReader.LoadHeader(saveA); + var headerB = SawyerStreamReader.LoadHeader(saveB); + + //Assert.Multiple(() => + //{ + // for (int i = 0; i < Math.Min(bytesSource.Length, bytesDest.Length); ++i) + // { + // Assert.AreEqual(bytesSource[i], bytesDest[i], $"[{i}] {bytesSource[i]} {bytesDest[i]}"); + // } + //}); + + CollectionAssert.AreEqual(bytesSource[0..16], bytesDest[0..16]); + // skip object header + CollectionAssert.AreEqual(bytesSource[21..], bytesDest[21..]); + } + [Test] public void LoadTownNamesObject() => Assert.Fail(); @@ -429,16 +497,16 @@ public void LoadStreetLightObject() public void LoadTrackExtraObject() { const string testFile = "Q:\\Steam\\steamapps\\common\\Locomotion\\ObjData\\TREXCAT1.DAT"; - var obj = LoadObject(testFile); + var (obj, struc) = LoadObject(testFile); Assert.Multiple(() => { - Assert.That(obj.TrackPieces, Is.EqualTo(1023), nameof(obj.TrackPieces)); - Assert.That(obj.PaintStyle, Is.EqualTo(1), nameof(obj.PaintStyle)); - Assert.That(obj.CostIndex, Is.EqualTo(1), nameof(obj.CostIndex)); - Assert.That(obj.BuildCostFactor, Is.EqualTo(2), nameof(obj.BuildCostFactor)); - Assert.That(obj.SellCostFactor, Is.EqualTo(-1), nameof(obj.SellCostFactor)); - Assert.That(obj.var_0E, Is.EqualTo(0), nameof(obj.var_0E)); + Assert.That(struc.TrackPieces, Is.EqualTo(1023), nameof(struc.TrackPieces)); + Assert.That(struc.PaintStyle, Is.EqualTo(1), nameof(struc.PaintStyle)); + Assert.That(struc.CostIndex, Is.EqualTo(1), nameof(struc.CostIndex)); + Assert.That(struc.BuildCostFactor, Is.EqualTo(2), nameof(struc.BuildCostFactor)); + Assert.That(struc.SellCostFactor, Is.EqualTo(-1), nameof(struc.SellCostFactor)); + Assert.That(struc.var_0E, Is.EqualTo(0), nameof(struc.var_0E)); }); } @@ -455,29 +523,29 @@ public void LoadTrackExtraObject() public void LoadTreeObject() { const string testFile = "Q:\\Steam\\steamapps\\common\\Locomotion\\ObjData\\BEECH.DAT"; - var obj = LoadObject(testFile); + var (obj, struc) = LoadObject(testFile); Assert.Multiple(() => { - Assert.That(obj.var_02, Is.EqualTo(40), nameof(obj.var_02)); - Assert.That(obj.Height, Is.EqualTo(131), nameof(obj.Height)); - Assert.That(obj.var_04, Is.EqualTo(27), nameof(obj.var_04)); - Assert.That(obj.var_05, Is.EqualTo(83), nameof(obj.var_05)); - Assert.That(obj.NumRotations, Is.EqualTo(1), nameof(obj.NumRotations)); - Assert.That(obj.Growth, Is.EqualTo(4), nameof(obj.Growth)); - Assert.That(obj.Flags, Is.EqualTo(TreeObjectFlags.HighAltitude | TreeObjectFlags.RequiresWater | TreeObjectFlags.HasShadow), nameof(obj.Flags)); - CollectionAssert.AreEqual(obj.Sprites, Array.CreateInstance(typeof(byte), 6), nameof(obj.Sprites)); - CollectionAssert.AreEqual(obj.SnowSprites, Array.CreateInstance(typeof(byte), 6), nameof(obj.SnowSprites)); - Assert.That(obj.ShadowImageOffset, Is.EqualTo(0), nameof(obj.ShadowImageOffset)); - Assert.That(obj.var_3C, Is.EqualTo(15), nameof(obj.var_3C)); - Assert.That(obj.SeasonState, Is.EqualTo(3), nameof(obj.SeasonState)); - Assert.That(obj.var_3E, Is.EqualTo(2), nameof(obj.var_3E)); - Assert.That(obj.CostIndex, Is.EqualTo(3), nameof(obj.CostIndex)); - Assert.That(obj.BuildCostFactor, Is.EqualTo(8), nameof(obj.BuildCostFactor)); - Assert.That(obj.ClearCostFactor, Is.EqualTo(4), nameof(obj.ClearCostFactor)); - Assert.That(obj.Colours, Is.EqualTo(0), nameof(obj.Colours)); - Assert.That(obj.Rating, Is.EqualTo(10), nameof(obj.Rating)); - Assert.That(obj.DemolishRatingReduction, Is.EqualTo(-15), nameof(obj.DemolishRatingReduction)); + Assert.That(struc.var_02, Is.EqualTo(40), nameof(struc.var_02)); + Assert.That(struc.Height, Is.EqualTo(131), nameof(struc.Height)); + Assert.That(struc.var_04, Is.EqualTo(27), nameof(struc.var_04)); + Assert.That(struc.var_05, Is.EqualTo(83), nameof(struc.var_05)); + Assert.That(struc.NumRotations, Is.EqualTo(1), nameof(struc.NumRotations)); + Assert.That(struc.Growth, Is.EqualTo(4), nameof(struc.Growth)); + Assert.That(struc.Flags, Is.EqualTo(TreeObjectFlags.HighAltitude | TreeObjectFlags.RequiresWater | TreeObjectFlags.HasShadow), nameof(struc.Flags)); + CollectionAssert.AreEqual(struc.Sprites, Array.CreateInstance(typeof(byte), 6), nameof(struc.Sprites)); + CollectionAssert.AreEqual(struc.SnowSprites, Array.CreateInstance(typeof(byte), 6), nameof(struc.SnowSprites)); + Assert.That(struc.ShadowImageOffset, Is.EqualTo(0), nameof(struc.ShadowImageOffset)); + Assert.That(struc.var_3C, Is.EqualTo(15), nameof(struc.var_3C)); + Assert.That(struc.SeasonState, Is.EqualTo(3), nameof(struc.SeasonState)); + Assert.That(struc.var_3E, Is.EqualTo(2), nameof(struc.var_3E)); + Assert.That(struc.CostIndex, Is.EqualTo(3), nameof(struc.CostIndex)); + Assert.That(struc.BuildCostFactor, Is.EqualTo(8), nameof(struc.BuildCostFactor)); + Assert.That(struc.ClearCostFactor, Is.EqualTo(4), nameof(struc.ClearCostFactor)); + Assert.That(struc.Colours, Is.EqualTo(0), nameof(struc.Colours)); + Assert.That(struc.Rating, Is.EqualTo(10), nameof(struc.Rating)); + Assert.That(struc.DemolishRatingReduction, Is.EqualTo(-15), nameof(struc.DemolishRatingReduction)); }); } @@ -485,7 +553,7 @@ public void LoadTreeObject() public void LoadTunnelObject() { const string testFile = "Q:\\Steam\\steamapps\\common\\Locomotion\\ObjData\\TUNNEL1.DAT"; - var obj = LoadObject(testFile); + var (obj, struc) = LoadObject(testFile); Assert.Pass(); } @@ -493,10 +561,10 @@ public void LoadTunnelObject() public void LoadVehicleAircraftObject() { const string testFile = "Q:\\Steam\\steamapps\\common\\Locomotion\\ObjData\\707.DAT"; - var locoObj = LoadObject(testFile); + var (obj, struc) = LoadObject(testFile); - var s5header = locoObj.S5Header; - var objHeader = locoObj.ObjectHeader; + var s5header = obj.S5Header; + var objHeader = obj.ObjectHeader; Assert.Multiple(() => { @@ -509,50 +577,49 @@ public void LoadVehicleAircraftObject() Assert.That(objHeader.DataLength, Is.EqualTo(159566), nameof(objHeader.DataLength)); }); - var obj = locoObj.Object as VehicleObject; Assert.Multiple(() => { - Assert.That(obj.Mode, Is.EqualTo(TransportMode.Air), nameof(obj.Mode)); - Assert.That(obj.Type, Is.EqualTo(VehicleType.Aircraft), nameof(obj.Type)); - Assert.That(obj.var_04, Is.EqualTo(1), nameof(obj.var_04)); - // Assert.That(obj.TrackType, Is.EqualTo(0xFF), nameof(obj.TrackType)); // is changed after load from 0 to 255 - Assert.That(obj.NumMods, Is.EqualTo(0), nameof(obj.NumMods)); - Assert.That(obj.CostIndex, Is.EqualTo(8), nameof(obj.CostIndex)); - Assert.That(obj.CostFactor, Is.EqualTo(345), nameof(obj.CostFactor)); - Assert.That(obj.Reliability, Is.EqualTo(88), nameof(obj.Reliability)); - Assert.That(obj.RunCostIndex, Is.EqualTo(4), nameof(obj.RunCostIndex)); - Assert.That(obj.RunCostFactor, Is.EqualTo(55), nameof(obj.RunCostFactor)); - Assert.That(obj.ColourType, Is.EqualTo(9), nameof(obj.ColourType)); - Assert.That(obj.NumCompat, Is.EqualTo(0), nameof(obj.NumCompat)); - CollectionAssert.AreEqual(Enumerable.Repeat(0, 8).ToArray(), obj.CompatibleVehicles, nameof(obj.CompatibleVehicles)); - CollectionAssert.AreEqual(Enumerable.Repeat(0, 4).ToArray(), obj.RequiredTrackExtras, nameof(obj.RequiredTrackExtras)); - //Assert.That(obj.var_24, Is.EqualTo(0), nameof(obj.var_24)); - //Assert.That(obj.BodySprites, Is.EqualTo(0), nameof(obj.BodySprites)); - //Assert.That(obj.BogieSprites, Is.EqualTo(1), nameof(obj.BogieSprites)); - Assert.That(obj.Power, Is.EqualTo(3000), nameof(obj.Power)); - Assert.That(obj.Speed, Is.EqualTo(604), nameof(obj.Speed)); - Assert.That(obj.RackSpeed, Is.EqualTo(120), nameof(obj.RackSpeed)); - Assert.That(obj.Weight, Is.EqualTo(141), nameof(obj.Weight)); - Assert.That(obj.Flags, Is.EqualTo((VehicleObjectFlags)16384), nameof(obj.Flags)); - // CollectionAssert.AreEqual(obj.MaxCargo, Enumerable.Repeat(0, 2).ToArray(), nameof(obj.MaxCargo)); // this is changed after load from 0 to 24 - CollectionAssert.AreEqual(Enumerable.Repeat(0, 2).ToArray(), obj.CargoTypes, nameof(obj.CargoTypes)); - CollectionAssert.AreEqual(Enumerable.Repeat(0, 32).ToArray(), obj.CargoTypeSpriteOffsets, nameof(obj.CargoTypeSpriteOffsets)); - Assert.That(obj.NumSimultaneousCargoTypes, Is.EqualTo(1), nameof(obj.NumSimultaneousCargoTypes)); - Assert.That(obj.Animation[0].ObjectId, Is.EqualTo(0), nameof(obj.Animation)); - Assert.That(obj.Animation[0].Height, Is.EqualTo(24), nameof(obj.Animation)); - Assert.That(obj.Animation[0].Type, Is.EqualTo(SimpleAnimationType.None), nameof(obj.Animation)); - Assert.That(obj.Animation[1].ObjectId, Is.EqualTo(0), nameof(obj.Animation)); - Assert.That(obj.Animation[1].Height, Is.EqualTo(0), nameof(obj.Animation)); - Assert.That(obj.Animation[1].Type, Is.EqualTo(SimpleAnimationType.None), nameof(obj.Animation)); - Assert.That(obj.var_113, Is.EqualTo(0), nameof(obj.var_113)); - Assert.That(obj.Designed, Is.EqualTo(1957), nameof(obj.Designed)); - Assert.That(obj.Obsolete, Is.EqualTo(1987), nameof(obj.Obsolete)); - Assert.That(obj.RackRailType, Is.EqualTo(0), nameof(obj.RackRailType)); - Assert.That(obj.DrivingSoundType, Is.EqualTo(DrivingSoundType.Engine1), nameof(obj.DrivingSoundType)); - //Assert.That(obj.Sound, Is.EqualTo(0), nameof(obj.Sound)); - //Assert.That(obj.pad_135, Is.EqualTo(0), nameof(obj.pad_135)); - Assert.That(obj.NumStartSounds, Is.EqualTo(2), nameof(obj.NumStartSounds)); - CollectionAssert.AreEqual(Enumerable.Repeat(0, 3).ToArray(), obj.StartSounds, nameof(obj.StartSounds)); + Assert.That(struc.Mode, Is.EqualTo(TransportMode.Air), nameof(struc.Mode)); + Assert.That(struc.Type, Is.EqualTo(VehicleType.Aircraft), nameof(struc.Type)); + Assert.That(struc.var_04, Is.EqualTo(1), nameof(struc.var_04)); + // Assert.That(struc.TrackType, Is.EqualTo(0xFF), nameof(struc.TrackType)); // is changed after load from 0 to 255 + Assert.That(struc.NumMods, Is.EqualTo(0), nameof(struc.NumMods)); + Assert.That(struc.CostIndex, Is.EqualTo(8), nameof(struc.CostIndex)); + Assert.That(struc.CostFactor, Is.EqualTo(345), nameof(struc.CostFactor)); + Assert.That(struc.Reliability, Is.EqualTo(88), nameof(struc.Reliability)); + Assert.That(struc.RunCostIndex, Is.EqualTo(4), nameof(struc.RunCostIndex)); + Assert.That(struc.RunCostFactor, Is.EqualTo(55), nameof(struc.RunCostFactor)); + Assert.That(struc.ColourType, Is.EqualTo(9), nameof(struc.ColourType)); + Assert.That(struc.NumCompat, Is.EqualTo(0), nameof(struc.NumCompat)); + CollectionAssert.AreEqual(Enumerable.Repeat(0, 8).ToArray(), struc.CompatibleVehicles, nameof(struc.CompatibleVehicles)); + CollectionAssert.AreEqual(Enumerable.Repeat(0, 4).ToArray(), struc.RequiredTrackExtras, nameof(struc.RequiredTrackExtras)); + //Assert.That(struc.var_24, Is.EqualTo(0), nameof(struc.var_24)); + //Assert.That(struc.BodySprites, Is.EqualTo(0), nameof(struc.BodySprites)); + //Assert.That(struc.BogieSprites, Is.EqualTo(1), nameof(struc.BogieSprites)); + Assert.That(struc.Power, Is.EqualTo(3000), nameof(struc.Power)); + Assert.That(struc.Speed, Is.EqualTo(604), nameof(struc.Speed)); + Assert.That(struc.RackSpeed, Is.EqualTo(120), nameof(struc.RackSpeed)); + Assert.That(struc.Weight, Is.EqualTo(141), nameof(struc.Weight)); + Assert.That(struc.Flags, Is.EqualTo((VehicleObjectFlags)16384), nameof(struc.Flags)); + // CollectionAssert.AreEqual(struc.MaxCargo, Enumerable.Repeat(0, 2).ToArray(), nameof(struc.MaxCargo)); // this is changed after load from 0 to 24 + CollectionAssert.AreEqual(Enumerable.Repeat(0, 2).ToArray(), struc.CargoTypes, nameof(struc.CargoTypes)); + CollectionAssert.AreEqual(Enumerable.Repeat(0, 32).ToArray(), struc.CargoTypeSpriteOffsets, nameof(struc.CargoTypeSpriteOffsets)); + Assert.That(struc.NumSimultaneousCargoTypes, Is.EqualTo(1), nameof(struc.NumSimultaneousCargoTypes)); + Assert.That(struc.Animation[0].ObjectId, Is.EqualTo(0), nameof(struc.Animation)); + Assert.That(struc.Animation[0].Height, Is.EqualTo(24), nameof(struc.Animation)); + Assert.That(struc.Animation[0].Type, Is.EqualTo(SimpleAnimationType.None), nameof(struc.Animation)); + Assert.That(struc.Animation[1].ObjectId, Is.EqualTo(0), nameof(struc.Animation)); + Assert.That(struc.Animation[1].Height, Is.EqualTo(0), nameof(struc.Animation)); + Assert.That(struc.Animation[1].Type, Is.EqualTo(SimpleAnimationType.None), nameof(struc.Animation)); + Assert.That(struc.var_113, Is.EqualTo(0), nameof(struc.var_113)); + Assert.That(struc.Designed, Is.EqualTo(1957), nameof(struc.Designed)); + Assert.That(struc.Obsolete, Is.EqualTo(1987), nameof(struc.Obsolete)); + Assert.That(struc.RackRailType, Is.EqualTo(0), nameof(struc.RackRailType)); + Assert.That(struc.DrivingSoundType, Is.EqualTo(DrivingSoundType.Engine1), nameof(struc.DrivingSoundType)); + //Assert.That(struc.Sound, Is.EqualTo(0), nameof(struc.Sound)); + //Assert.That(struc.pad_135, Is.EqualTo(0), nameof(struc.pad_135)); + Assert.That(struc.NumStartSounds, Is.EqualTo(2), nameof(struc.NumStartSounds)); + CollectionAssert.AreEqual(Enumerable.Repeat(0, 3).ToArray(), struc.StartSounds, nameof(struc.StartSounds)); }); } @@ -560,14 +627,14 @@ public void LoadVehicleAircraftObject() public void LoadWallObject() { const string testFile = "Q:\\Steam\\steamapps\\common\\Locomotion\\ObjData\\FENCE1.DAT"; - var obj = LoadObject(testFile); + var (obj, struc) = LoadObject(testFile); Assert.Multiple(() => { - Assert.That(obj.var_06, Is.EqualTo(15), nameof(obj.var_06)); - Assert.That(obj.Flags, Is.EqualTo(WallObjectFlags.None), nameof(obj.Flags)); - Assert.That(obj.Height, Is.EqualTo(2), nameof(obj.Height)); - Assert.That(obj.var_09, Is.EqualTo(8), nameof(obj.var_09)); + Assert.That(struc.var_06, Is.EqualTo(15), nameof(struc.var_06)); + Assert.That(struc.Flags, Is.EqualTo(WallObjectFlags.None), nameof(struc.Flags)); + Assert.That(struc.Height, Is.EqualTo(2), nameof(struc.Height)); + Assert.That(struc.var_09, Is.EqualTo(8), nameof(struc.var_09)); }); } @@ -575,15 +642,15 @@ public void LoadWallObject() public void LoadWaterObject() { const string testFile = "Q:\\Steam\\steamapps\\common\\Locomotion\\ObjData\\WATER1.DAT"; - var obj = LoadObject(testFile); + var (obj, struc) = LoadObject(testFile); Assert.Multiple(() => { - Assert.That(obj.CostIndex, Is.EqualTo(2), nameof(obj.CostIndex)); - Assert.That(obj.var_03, Is.EqualTo(0), nameof(obj.var_03)); - Assert.That(obj.CostFactor, Is.EqualTo(51), nameof(obj.CostFactor)); - Assert.That(obj.var_05, Is.EqualTo(0), nameof(obj.var_05)); - Assert.That(obj.var_0A, Is.EqualTo(0), nameof(obj.var_0A)); + Assert.That(struc.CostIndex, Is.EqualTo(2), nameof(struc.CostIndex)); + Assert.That(struc.var_03, Is.EqualTo(0), nameof(struc.var_03)); + Assert.That(struc.CostFactor, Is.EqualTo(51), nameof(struc.CostFactor)); + Assert.That(struc.var_05, Is.EqualTo(0), nameof(struc.var_05)); + Assert.That(struc.var_0A, Is.EqualTo(0), nameof(struc.var_0A)); }); } } diff --git a/OpenLocoToolTests/ObjectSavingTests.cs b/OpenLocoToolTests/ObjectSavingTests.cs index d777a70d..424304a2 100644 --- a/OpenLocoToolTests/ObjectSavingTests.cs +++ b/OpenLocoToolTests/ObjectSavingTests.cs @@ -16,8 +16,6 @@ public void WriteLocoStruct() const string testFile = "Q:\\Steam\\steamapps\\common\\Locomotion\\ObjData\\SIGC3.DAT"; var fileSize = new FileInfo(testFile).Length; var logger = new Logger(); - var ssr = new SawyerStreamReader(logger); - var ssw = new SawyerStreamWriter(logger); var loaded = SawyerStreamReader.LoadFull(testFile); // load data in raw bytes for test