Skip to content

Commit 0c65d99

Browse files
authored
Add basic support for saving edits to the objects (#12)
* 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
1 parent 7f4836d commit 0c65d99

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+939
-744
lines changed

DatFileRenamer/Program.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
using System.Diagnostics;
2-
using System.IO;
3-
using System.Reflection;
1+
using System.Reflection;
42

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

OpenLocoTool/DatFileParsing/AttributeHelper.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,11 @@ public static class AttributeHelper
1212

1313
public static T? Get<T>(Type t) where T : Attribute
1414
=> t.GetCustomAttribute(typeof(T), inherit: false) as T;
15+
16+
public static bool Has<T>(PropertyInfo p) where T : Attribute
17+
=> p.GetCustomAttribute(typeof(T), inherit: false) is T;
18+
19+
public static IEnumerable<PropertyInfo> GetAllPropertiesWithAttribute<T>(Type t) where T : Attribute
20+
=> t.GetProperties().Where(Has<T>);
1521
}
1622
}

OpenLocoTool/DatFileParsing/ByteWriter.cs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ public static void WriteT(Span<byte> data, Type t, int offset, object val)
3636

3737
else if (t == typeof(string_id))
3838
{
39+
// string ids should always be 0 in the dat file - they're only set when loaded into memory and never saved
40+
val = 0;
3941
ByteWriterT.Write(data, offset, (string_id)(dynamic)val);
4042
}
4143

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

80-
public static ReadOnlySpan<byte> Bytes(object obj)
81-
{
82-
// todo: this just a skeleton placeholder method
83-
// we need to implement Save() for image table and G1 data
84-
throw new NotImplementedException();
85-
return new byte[1];
86-
}
87-
8882
public static ReadOnlySpan<byte> WriteLocoStruct(ILocoStruct obj)
8983
{
9084
if (obj == null)
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using System.ComponentModel;
2+
using OpenLocoTool.Headers;
3+
4+
namespace OpenLocoTool.DatFileParsing
5+
{
6+
[TypeConverter(typeof(ExpandableObjectConverter))]
7+
public class G1Dat(G1Header g1Header, List<G1Element32> g1Elements)
8+
{
9+
public G1Header G1Header { get; set; } = g1Header;
10+
public List<G1Element32> G1Elements { get; set; } = g1Elements;
11+
}
12+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using System.ComponentModel;
2+
using OpenLocoTool.Headers;
3+
4+
namespace OpenLocoTool.DatFileParsing
5+
{
6+
[TypeConverter(typeof(ExpandableObjectConverter))]
7+
public interface ILocoObject
8+
{
9+
S5Header S5Header { get; set; }
10+
ObjectHeader ObjectHeader { get; set; }
11+
ILocoStruct Object { get; set; }
12+
StringTable StringTable { get; set; }
13+
G1Header G1Header { get; set; }
14+
List<G1Element32> G1Elements { get; set; }
15+
}
16+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
using System.ComponentModel;
2+
3+
namespace OpenLocoTool.DatFileParsing
4+
{
5+
/*
6+
=== Dat File Format ===
7+
|-File-------------------------------|
8+
|-S5Header-|-DatHeader--|-ObjectData-|
9+
10+
==============================================================================================================
11+
12+
|-S5Header----------------|
13+
|-Flags-|-Name-|-Checksum-|
14+
15+
|-DatHeader-------------|
16+
|-Encoding-|-Datalength-|
17+
18+
|-ObjectData-----------------------------------------|
19+
|-Object-|-StringTable-|-VariableData-|-GraphicsData-|
20+
21+
==============================================================================================================
22+
23+
|-Object-|
24+
-- per-object
25+
26+
|-StringTable-|
27+
|-String{n}---|
28+
29+
|-VariableData-|
30+
-- per-object
31+
32+
|-GraphicsData------------------------------|
33+
|-G1Header-|-G1Element32{n}-|-ImageBytes{n}-|
34+
35+
==============================================================================================================
36+
37+
|-String-----------------|
38+
|-Language-|-StringBytes-|
39+
40+
|-G1Header---------------|
41+
|-NumEntries-|-TotalSize-|
42+
43+
|-G1Element32------------------------------------------------------|
44+
|-Offset-|-Width-|-Height-|-xOffset-|-yOffset-|-Flags-|-ZoomOffset-|
45+
46+
|-ImageBytes-|
47+
-- offset by G1Element32.Offset
48+
*/
49+
50+
[TypeConverter(typeof(ExpandableObjectConverter))]
51+
public interface ILocoStruct
52+
{
53+
static int StructSize { get; }
54+
}
55+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
using System.ComponentModel;
2+
3+
namespace OpenLocoTool.DatFileParsing
4+
{
5+
[TypeConverter(typeof(ExpandableObjectConverter))]
6+
public interface ILocoStructStringTablePostLoad
7+
{
8+
void LoadPostStringTable(StringTable stringTable);
9+
}
10+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using System.ComponentModel;
2+
3+
namespace OpenLocoTool.DatFileParsing
4+
{
5+
[TypeConverter(typeof(ExpandableObjectConverter))]
6+
public interface ILocoStructVariableData
7+
{
8+
ReadOnlySpan<byte> Load(ReadOnlySpan<byte> remainingData);
9+
10+
ReadOnlySpan<byte> Save();
11+
}
12+
}

OpenLocoTool/DatFileParsing/LocoAttributes.cs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,6 @@ public class LocoStructSizeAttribute(int size) : Attribute
1818
public int Size => size;
1919
}
2020

21-
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false)]
22-
public class LocoStringTableAttribute(params string[] names) : Attribute
23-
{
24-
public string[] Names => names;
25-
26-
public int Count => Names.Length;
27-
}
28-
2921
// basically a 'skip' attribute to allow deferred loading for variable data
3022
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property, AllowMultiple = false)]
3123
public class LocoStructVariableLoadAttribute : Attribute
@@ -35,4 +27,9 @@ public class LocoStructVariableLoadAttribute : Attribute
3527
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property, AllowMultiple = false)]
3628
public class LocoStructSkipReadAttribute : Attribute
3729
{ }
30+
31+
// basically a 'skip' attribute to allow deferred loading for variable data, and writing of this property will be 0
32+
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property, AllowMultiple = false)]
33+
public class LocoStringAttribute : Attribute
34+
{ }
3835
}

OpenLocoTool/DatFileParsing/LocoObject.cs

Lines changed: 30 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -1,139 +1,56 @@
11
using System.ComponentModel;
22
using OpenLocoTool.Headers;
3-
using OpenLocoTool.Objects;
43

54
namespace OpenLocoTool.DatFileParsing
65
{
76
/*
8-
=== Dat File Format ===
9-
|-File-------------------------------|
10-
|-S5Header-|-DatHeader--|-ObjectData-|
7+
=== Dat File Format ===
8+
|-File-------------------------------|
9+
|-S5Header-|-DatHeader--|-ObjectData-|
1110
12-
==============================================================================================================
11+
==============================================================================================================
1312
14-
|-S5Header----------------|
15-
|-Flags-|-Name-|-Checksum-|
13+
|-S5Header----------------|
14+
|-Flags-|-Name-|-Checksum-|
1615
17-
|-DatHeader-------------|
18-
|-Encoding-|-Datalength-|
16+
|-DatHeader-------------|
17+
|-Encoding-|-Datalength-|
1918
20-
|-ObjectData-----------------------------------------|
21-
|-Object-|-StringTable-|-VariableData-|-GraphicsData-|
19+
|-ObjectData-----------------------------------------|
20+
|-Object-|-StringTable-|-VariableData-|-GraphicsData-|
2221
23-
==============================================================================================================
22+
==============================================================================================================
2423
25-
|-Object-|
26-
-- per-object
24+
|-Object-|
25+
-- per-object
2726
28-
|-StringTable-|
29-
|-String{n}---|
27+
|-StringTable-|
28+
|-String{n}---|
3029
31-
|-VariableData-|
32-
-- per-object
30+
|-VariableData-|
31+
-- per-object
3332
34-
|-GraphicsData------------------------------|
35-
|-G1Header-|-G1Element32{n}-|-ImageBytes{n}-|
33+
|-GraphicsData------------------------------|
34+
|-G1Header-|-G1Element32{n}-|-ImageBytes{n}-|
3635
37-
==============================================================================================================
36+
==============================================================================================================
3837
39-
|-String-----------------|
40-
|-Language-|-StringBytes-|
38+
|-String-----------------|
39+
|-Language-|-StringBytes-|
4140
42-
|-G1Header---------------|
43-
|-Numentries-|-Totalsize-|
41+
|-G1Header---------------|
42+
|-NumEntries-|-TotalSize-|
4443
45-
|-G1Element32------------------------------------------------------|
46-
|-Offset-|-Width-|-Height-|-xOffset-|-yOffset-|-Flags-|-ZoomOffset-|
47-
48-
|-ImageBytes-|
49-
-- offset by G1Element32.Offset
50-
*/
51-
52-
[TypeConverter(typeof(ExpandableObjectConverter))]
53-
public interface ILocoStruct
54-
{
55-
static int StructSize { get; }
56-
}
57-
58-
[TypeConverter(typeof(ExpandableObjectConverter))]
59-
public interface ILocoStructVariableData
60-
{
61-
ReadOnlySpan<byte> Load(ReadOnlySpan<byte> remainingData);
62-
63-
ReadOnlySpan<byte> Save();
64-
}
65-
66-
[TypeConverter(typeof(ExpandableObjectConverter))]
67-
public interface ILocoStructStringTablePostLoad
68-
{
69-
void LoadPostStringTable(StringTable stringTable);
70-
}
71-
72-
[TypeConverter(typeof(ExpandableObjectConverter))]
73-
public interface ILocoObject
74-
{
75-
S5Header S5Header { get; set; }
76-
ObjectHeader ObjectHeader { get; set; }
77-
ILocoStruct Object { get; set; }
78-
StringTable StringTable { get; set; }
79-
G1Header G1Header { get; set; }
80-
List<G1Element32> G1Elements { get; set; }
81-
}
82-
83-
[TypeConverter(typeof(ExpandableObjectConverter))]
84-
public class G1Dat(G1Header g1Header, List<G1Element32> g1Elements)
85-
{
86-
public G1Header G1Header { get; set; } = g1Header;
87-
public List<G1Element32> G1Elements { get; set; } = g1Elements;
88-
}
89-
90-
public static class ObjectTypeFixedSize
91-
{
92-
public static int GetSize(ObjectType objectType)
93-
=> objectType switch
94-
{
95-
ObjectType.Airport => AirportObject.StructSize,
96-
ObjectType.Bridge => BridgeObject.StructSize,
97-
ObjectType.Building => BuildingObject.StructSize,
98-
ObjectType.Cargo => CargoObject.StructSize,
99-
ObjectType.CliffEdge => CliffEdgeObject.StructSize,
100-
ObjectType.Climate => ClimateObject.StructSize,
101-
ObjectType.Competitor => CompetitorObject.StructSize,
102-
ObjectType.Currency => CurrencyObject.StructSize,
103-
ObjectType.Dock => DockObject.StructSize,
104-
ObjectType.HillShapes => HillShapesObject.StructSize,
105-
ObjectType.Industry => IndustryObject.StructSize,
106-
ObjectType.InterfaceSkin => InterfaceSkinObject.StructSize,
107-
ObjectType.Land => LandObject.StructSize,
108-
ObjectType.LevelCrossing => LevelCrossingObject.StructSize,
109-
ObjectType.Region => RegionObject.StructSize,
110-
ObjectType.RoadExtra => RoadExtraObject.StructSize,
111-
ObjectType.Road => RoadObject.StructSize,
112-
ObjectType.RoadStation => RoadStationObject.StructSize,
113-
ObjectType.Scaffolding => ScaffoldingObject.StructSize,
114-
ObjectType.ScenarioText => ScenarioTextObject.StructSize,
115-
ObjectType.Snow => SnowObject.StructSize,
116-
ObjectType.Sound => SoundObject.StructSize,
117-
ObjectType.Steam => SteamObject.StructSize,
118-
ObjectType.StreetLight => StreetLightObject.StructSize,
119-
ObjectType.TownNames => TownNamesObject.StructSize,
120-
ObjectType.TrackExtra => TrackExtraObject.StructSize,
121-
ObjectType.Track => TrackObject.StructSize,
122-
ObjectType.TrainSignal => TrainSignalObject.StructSize,
123-
ObjectType.TrainStation => TrainStationObject.StructSize,
124-
ObjectType.Tree => TreeObject.StructSize,
125-
ObjectType.Tunnel => TunnelObject.StructSize,
126-
ObjectType.Vehicle => VehicleObject.StructSize,
127-
ObjectType.Wall => WallObject.StructSize,
128-
ObjectType.Water => WaterObject.StructSize,
129-
_ => throw new ArgumentOutOfRangeException(nameof(objectType), $"unknown object type {objectType}")
130-
};
131-
}
44+
|-G1Element32------------------------------------------------------|
45+
|-Offset-|-Width-|-Height-|-xOffset-|-yOffset-|-Flags-|-ZoomOffset-|
13246
47+
|-ImageBytes-|
48+
-- offset by G1Element32.Offset
49+
*/
13350
[TypeConverter(typeof(ExpandableObjectConverter))]
13451
public class LocoObject : ILocoObject
13552
{
136-
public LocoObject(S5Header s5Hdr, ObjectHeader objHdr, ILocoStruct obj, StringTable stringTable, G1Header g1Header, List<G1Element32> g1Elements)
53+
public LocoObject(S5Header s5Hdr, ObjectHeader objHdr, ILocoStruct obj, StringTable stringTable, G1Header? g1Header, List<G1Element32>? g1Elements)
13754
{
13855
S5Header = s5Hdr;
13956
ObjectHeader = objHdr;

0 commit comments

Comments
 (0)