Skip to content

Commit 8bf19bc

Browse files
committed
Throw exception if variable-load data is non-zero on loading the object. Resolves #38
1 parent 8f86250 commit 8bf19bc

File tree

1 file changed

+110
-65
lines changed

1 file changed

+110
-65
lines changed

Dat/FileParsing/ByteReader.cs

Lines changed: 110 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -5,36 +5,66 @@ namespace OpenLoco.Dat.FileParsing
55
{
66
public static class ByteReader
77
{
8-
public static object ReadT(ReadOnlySpan<byte> data, Type t, int offset, int arrLength = 0)
8+
public static object ReadT(ReadOnlySpan<byte> data, Type t, int offset, int arrLength = 0, bool isVariableLoad = false)
99
{
1010
if (t == typeof(uint8_t))
1111
{
12-
return ByteReaderT.Read_uint8t(data, offset);
12+
var val = ByteReaderT.Read_uint8t(data, offset);
13+
if (isVariableLoad && val != 0)
14+
{
15+
throw new InvalidDataException($"uint8_t at offset {offset} had non-zero variable-load data. Value={val}");
16+
}
17+
return val;
1318
}
1419

1520
if (t == typeof(int8_t))
1621
{
17-
return ByteReaderT.Read_int8t(data, offset);
22+
var val = ByteReaderT.Read_int8t(data, offset);
23+
if (isVariableLoad && val != 0)
24+
{
25+
throw new InvalidDataException($"int8_t at offset {offset} had non-zero variable-load data. Value={val}");
26+
}
27+
return val;
1828
}
1929

2030
if (t == typeof(uint16_t))
2131
{
22-
return ByteReaderT.Read_uint16t(data, offset);
32+
var val = ByteReaderT.Read_uint16t(data, offset);
33+
if (isVariableLoad && val != 0)
34+
{
35+
throw new InvalidDataException($"uint16_t at offset {offset} had non-zero variable-load data. Value={val}");
36+
}
37+
return val;
2338
}
2439

2540
if (t == typeof(int16_t))
2641
{
27-
return ByteReaderT.Read_int16t(data, offset);
42+
var val = ByteReaderT.Read_int16t(data, offset);
43+
if (isVariableLoad && val != 0)
44+
{
45+
throw new InvalidDataException($"int16_t at offset {offset} had non-zero variable-load data. Value={val}");
46+
}
47+
return val;
2848
}
2949

3050
if (t == typeof(uint32_t))
3151
{
32-
return ByteReaderT.Read_uint32t(data, offset);
52+
var val = ByteReaderT.Read_uint32t(data, offset);
53+
if (isVariableLoad && val != 0)
54+
{
55+
throw new InvalidDataException($"uint32_t at offset {offset} had non-zero variable-load data. Value={val}");
56+
}
57+
return val;
3358
}
3459

3560
if (t == typeof(int32_t))
3661
{
37-
return ByteReaderT.Read_int32t(data, offset);
62+
var val = ByteReaderT.Read_int32t(data, offset);
63+
if (isVariableLoad && val != 0)
64+
{
65+
throw new InvalidDataException($"int32_t at offset {offset} had non-zero variable-load data. Value={val}");
66+
}
67+
return val;
3868
}
3969

4070
if (t == typeof(string_id))
@@ -49,82 +79,97 @@ public static object ReadT(ReadOnlySpan<byte> data, Type t, int offset, int arrL
4979

5080
if (t.IsArray)
5181
{
52-
var elementType = t.GetElementType() ?? throw new ArgumentNullException(t.Name);
53-
var size = ByteHelpers.GetObjectSize(elementType);
82+
return ReadArray(data, t, offset, arrLength, isVariableLoad);
83+
}
5484

55-
var arr = Array.CreateInstance(elementType, arrLength);
56-
for (var i = 0; i < arrLength; i++)
57-
{
58-
arr.SetValue(ReadT(data, elementType, offset + (i * size)), i); // why pass 'i' in here?
59-
}
85+
if (t.IsEnum) // this is so big because we need special handling for 'flags' enums
86+
{
87+
return ReadEnum(data, t, offset);
88+
}
6089

61-
return arr;
90+
if (t.IsClass)
91+
{
92+
return ReadClass(data, t, offset);
6293
}
6394

64-
if (t.IsEnum) // this is so big because we need special handling for 'flags' enums
95+
throw new NotImplementedException(t.ToString());
96+
}
97+
98+
private static object ReadArray(ReadOnlySpan<byte> data, Type t, int offset, int arrLength, bool isVariableLoad)
99+
{
100+
var elementType = t.GetElementType() ?? throw new ArgumentNullException(t.Name);
101+
var size = ByteHelpers.GetObjectSize(elementType);
102+
103+
var arr = Array.CreateInstance(elementType, arrLength);
104+
for (var i = 0; i < arrLength; i++)
65105
{
66-
var underlyingType = t.GetEnumUnderlyingType();
67-
var underlyingValue = ReadT(data, underlyingType, offset);
106+
arr.SetValue(ReadT(data, elementType, offset + (i * size), isVariableLoad: isVariableLoad), i); // why pass 'i' in here?
107+
}
68108

69-
if (!t.IsDefined(typeof(FlagsAttribute), inherit: false))
70-
{
71-
return Enum.ToObject(t, underlyingValue);
72-
}
109+
return arr;
110+
}
111+
112+
private static object ReadClass(ReadOnlySpan<byte> data, Type t, int offset)
113+
{
114+
if (t.Name == "ObjectHeader")
115+
{
116+
return ObjectHeader.Read(data[..ObjectHeader.StructLength]);
117+
}
118+
else if (t.Name == "S5Header")
119+
{
120+
return S5Header.Read(data[..S5Header.StructLength]);
121+
}
73122

74-
var enumValues = Enum.GetValues(t);
123+
var objectSize = ByteHelpers.GetObjectSize(t);
124+
return ReadLocoStruct(data[offset..(offset + objectSize)], t);
125+
}
75126

76-
if (underlyingType == typeof(int8_t) || underlyingType == typeof(int16_t) || underlyingType == typeof(int32_t))
77-
{
78-
var combinedValue = 0;
79-
foreach (var enumValue in enumValues)
80-
{
81-
var parsed = Enum.Parse(t, enumValue.ToString()!);
82-
var enumValueInt = Convert.ToInt32(parsed); // Convert to int
83-
if ((enumValueInt & Convert.ToInt32(underlyingValue)) != 0) // Convert to int
84-
{
85-
combinedValue |= enumValueInt;
86-
}
87-
}
127+
private static object ReadEnum(ReadOnlySpan<byte> data, Type t, int offset)
128+
{
129+
var underlyingType = t.GetEnumUnderlyingType();
130+
var underlyingValue = ReadT(data, underlyingType, offset);
88131

89-
return Enum.ToObject(t, combinedValue);
90-
}
91-
else if (underlyingType == typeof(uint8_t) || underlyingType == typeof(uint16_t) || underlyingType == typeof(uint32_t))
132+
if (!t.IsDefined(typeof(FlagsAttribute), inherit: false))
133+
{
134+
return Enum.ToObject(t, underlyingValue);
135+
}
136+
137+
var enumValues = Enum.GetValues(t);
138+
139+
if (underlyingType == typeof(int8_t) || underlyingType == typeof(int16_t) || underlyingType == typeof(int32_t))
140+
{
141+
var combinedValue = 0;
142+
foreach (var enumValue in enumValues)
92143
{
93-
var combinedValue = 0U;
94-
foreach (var enumValue in enumValues)
144+
var parsed = Enum.Parse(t, enumValue.ToString()!);
145+
var enumValueInt = Convert.ToInt32(parsed); // Convert to int
146+
if ((enumValueInt & Convert.ToInt32(underlyingValue)) != 0) // Convert to int
95147
{
96-
var parsed = Enum.Parse(t, enumValue.ToString()!);
97-
var enumValueInt = Convert.ToUInt32(parsed); // Convert to int
98-
if ((enumValueInt & Convert.ToUInt32(underlyingValue)) != 0) // Convert to int
99-
{
100-
combinedValue |= enumValueInt;
101-
}
148+
combinedValue |= enumValueInt;
102149
}
103-
104-
return Enum.ToObject(t, combinedValue);
105-
}
106-
else
107-
{
108-
throw new ArgumentOutOfRangeException(nameof(underlyingType), underlyingType, "unrecognised type");
109150
}
110-
}
111151

112-
if (t.IsClass)
152+
return Enum.ToObject(t, combinedValue);
153+
}
154+
else if (underlyingType == typeof(uint8_t) || underlyingType == typeof(uint16_t) || underlyingType == typeof(uint32_t))
113155
{
114-
if (t.Name == "ObjectHeader")
156+
var combinedValue = 0U;
157+
foreach (var enumValue in enumValues)
115158
{
116-
return ObjectHeader.Read(data[..ObjectHeader.StructLength]);
117-
}
118-
else if (t.Name == "S5Header")
119-
{
120-
return S5Header.Read(data[..S5Header.StructLength]);
159+
var parsed = Enum.Parse(t, enumValue.ToString()!);
160+
var enumValueInt = Convert.ToUInt32(parsed); // Convert to int
161+
if ((enumValueInt & Convert.ToUInt32(underlyingValue)) != 0) // Convert to int
162+
{
163+
combinedValue |= enumValueInt;
164+
}
121165
}
122166

123-
var objectSize = ByteHelpers.GetObjectSize(t);
124-
return ReadLocoStruct(data[offset..(offset + objectSize)], t);
167+
return Enum.ToObject(t, combinedValue);
168+
}
169+
else
170+
{
171+
throw new ArgumentOutOfRangeException(nameof(underlyingType), underlyingType, "unrecognised type");
125172
}
126-
127-
throw new NotImplementedException(t.ToString());
128173
}
129174

130175
public static T ReadLocoStruct<T>(ReadOnlySpan<byte> data) where T : class
@@ -201,7 +246,7 @@ public static ILocoStruct ReadLocoStruct(ReadOnlySpan<byte> data, Type t)
201246
continue;
202247
}
203248

204-
args.Add(ReadT(data, p.PropertyType, offsetAttr.Offset, arrLength));
249+
args.Add(ReadT(data, p.PropertyType, offsetAttr.Offset, arrLength, variableAttr != null));
205250
}
206251

207252
return (ILocoStruct?)Activator.CreateInstance(t, [.. args]) ?? throw new InvalidDataException("couldn't parse");

0 commit comments

Comments
 (0)