Skip to content

Commit

Permalink
Add basic support for saving edits to the objects (#12)
Browse files Browse the repository at this point in the history
* revert to old string table attribute since we need them in the struct definition to know which bytes to write 0 to in save()

* fix object loading

* get rudimentary saving working (vehicles don't load though...)

* vehicles load now

* fix scenariotext object loading

* misc fixes

* fix industryobject not loading

* show user list of supported save objects

* precommit for object control

* undo object control

* apply changes from code review, fix many compiler warnings
  • Loading branch information
LeftofZen authored Dec 20, 2023
1 parent 7f4836d commit 0c65d99
Show file tree
Hide file tree
Showing 58 changed files with 939 additions and 744 deletions.
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 Down
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

0 comments on commit 0c65d99

Please sign in to comment.