Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add basic support for saving edits to the objects #12

Merged
merged 11 commits into from
Dec 20, 2023
4 changes: 1 addition & 3 deletions DatFileRenamer/Program.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Reflection;

Console.WriteLine("Dat File Renamer v0.1");

Expand Down
6 changes: 6 additions & 0 deletions OpenLocoTool/DatFileParsing/AttributeHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,11 @@ public static class AttributeHelper

public static T? Get<T>(Type t) where T : Attribute
=> t.GetCustomAttribute(typeof(T), inherit: false) as T;

public static bool Has<T>(PropertyInfo p) where T : Attribute
=> p.GetCustomAttribute(typeof(T), inherit: false) is T;

public static IEnumerable<PropertyInfo> GetAllPropertiesWithAttribute<T>(Type t) where T : Attribute
=> t.GetProperties().Where(Has<T>);
}
}
10 changes: 2 additions & 8 deletions OpenLocoTool/DatFileParsing/ByteWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ public static void WriteT(Span<byte> 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);
}

Expand Down Expand Up @@ -77,14 +79,6 @@ public static void WriteT(Span<byte> data, Type t, int offset, object val)
}
}

public static ReadOnlySpan<byte> 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<byte> WriteLocoStruct(ILocoStruct obj)
{
if (obj == null)
Expand Down
12 changes: 12 additions & 0 deletions OpenLocoTool/DatFileParsing/G1Dat.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.ComponentModel;
using OpenLocoTool.Headers;

namespace OpenLocoTool.DatFileParsing
{
[TypeConverter(typeof(ExpandableObjectConverter))]
public class G1Dat(G1Header g1Header, List<G1Element32> g1Elements)
{
public G1Header G1Header { get; set; } = g1Header;
public List<G1Element32> G1Elements { get; set; } = g1Elements;
}
}
16 changes: 16 additions & 0 deletions OpenLocoTool/DatFileParsing/ILocoObject.cs
Original file line number Diff line number Diff line change
@@ -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<G1Element32> G1Elements { get; set; }
}
}
55 changes: 55 additions & 0 deletions OpenLocoTool/DatFileParsing/ILocoStruct.cs
Original file line number Diff line number Diff line change
@@ -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; }
}
}
10 changes: 10 additions & 0 deletions OpenLocoTool/DatFileParsing/ILocoStructStringTablePostLoad.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System.ComponentModel;

namespace OpenLocoTool.DatFileParsing
{
[TypeConverter(typeof(ExpandableObjectConverter))]
public interface ILocoStructStringTablePostLoad
{
void LoadPostStringTable(StringTable stringTable);
}
}
12 changes: 12 additions & 0 deletions OpenLocoTool/DatFileParsing/ILocoStructVariableData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.ComponentModel;

namespace OpenLocoTool.DatFileParsing
{
[TypeConverter(typeof(ExpandableObjectConverter))]
public interface ILocoStructVariableData
{
ReadOnlySpan<byte> Load(ReadOnlySpan<byte> remainingData);

ReadOnlySpan<byte> Save();
}
}
13 changes: 5 additions & 8 deletions OpenLocoTool/DatFileParsing/LocoAttributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
{ }
}
143 changes: 30 additions & 113 deletions OpenLocoTool/DatFileParsing/LocoObject.cs
Original file line number Diff line number Diff line change
@@ -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<byte> Load(ReadOnlySpan<byte> remainingData);

ReadOnlySpan<byte> 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<G1Element32> G1Elements { get; set; }
}

[TypeConverter(typeof(ExpandableObjectConverter))]
public class G1Dat(G1Header g1Header, List<G1Element32> g1Elements)
{
public G1Header G1Header { get; set; } = g1Header;
public List<G1Element32> 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<G1Element32> g1Elements)
public LocoObject(S5Header s5Hdr, ObjectHeader objHdr, ILocoStruct obj, StringTable stringTable, G1Header? g1Header, List<G1Element32>? g1Elements)
{
S5Header = s5Hdr;
ObjectHeader = objHdr;
Expand All @@ -156,7 +73,7 @@
public ObjectHeader ObjectHeader { get; set; }
public ILocoStruct Object { get; set; }
public StringTable StringTable { get; set; }
public G1Header? G1Header { get; set; }

Check warning on line 76 in OpenLocoTool/DatFileParsing/LocoObject.cs

View workflow job for this annotation

GitHub Actions / build (Release)

Nullability of reference types in return type of 'G1Header? LocoObject.G1Header.get' doesn't match implicitly implemented member 'G1Header ILocoObject.G1Header.get' (possibly because of nullability attributes).
public List<G1Element32>? G1Elements { get; set; }

Check warning on line 77 in OpenLocoTool/DatFileParsing/LocoObject.cs

View workflow job for this annotation

GitHub Actions / build (Release)

Nullability of reference types in return type of 'List<G1Element32>? LocoObject.G1Elements.get' doesn't match implicitly implemented member 'List<G1Element32> ILocoObject.G1Elements.get' (possibly because of nullability attributes).
}
}
9 changes: 5 additions & 4 deletions OpenLocoTool/DatFileParsing/ObjectAnnotator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<LocoStringAttribute>(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;
Expand All @@ -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
Expand Down
Loading
Loading