Skip to content

Commit 8c6b3eb

Browse files
committed
Implement FileVersion VER 7.8
Make Unit.Bird.drop_sites length variable Add basic support for multiple file versions
1 parent fb41fbb commit 8c6b3eb

File tree

17 files changed

+151
-102
lines changed

17 files changed

+151
-102
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ packages = ["src/genieutils"]
77

88
[project]
99
name = "genieutils-py"
10-
version = "0.0.3"
10+
version = "0.0.4"
1111
authors = [
1212
{ name = "SiegeEngineers", email = "[email protected]" },
1313
]

src/genieutils/civ.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from genieutils.common import ByteHandler, GenieClass
44
from genieutils.unit import Unit
5+
from genieutils.versions import Version
56

67

78
@dataclass
@@ -40,7 +41,7 @@ def from_bytes(cls, content: ByteHandler) -> 'Civ':
4041
def unit_pointers(self) -> list[int]:
4142
return [(0 if u is None else 1) for u in self.units]
4243

43-
def to_bytes(self) -> bytes:
44+
def to_bytes(self, version: Version) -> bytes:
4445
return b''.join([
4546
self.write_int_8(self.player_type),
4647
self.write_debug_string(self.name),
@@ -51,5 +52,5 @@ def to_bytes(self) -> bytes:
5152
self.write_int_8(self.icon_set),
5253
self.write_int_16(len(self.units)),
5354
self.write_int_32_array(self.unit_pointers),
54-
self.write_class_array(self.units),
55+
self.write_class_array(self.units, version),
5556
])

src/genieutils/common.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from typing import TypeVar, Sequence
44

55
from genieutils.datatypes import Int, Float, String
6+
from genieutils.versions import Version
67

78
TILE_TYPE_COUNT = 19
89
TERRAIN_COUNT = 200
@@ -31,7 +32,7 @@ def from_bytes(cls, data: 'ByteHandler'):
3132
def from_bytes_with_count(cls, data: 'ByteHandler', terrains_used_1: int):
3233
raise NotImplementedError
3334

34-
def to_bytes(self) -> bytes:
35+
def to_bytes(self, version: Version) -> bytes:
3536
raise NotImplementedError
3637

3738
def write_debug_string(self, value: str) -> bytes:
@@ -48,7 +49,9 @@ def write_int_8(self, value: int) -> bytes:
4849
def write_int_8_array(self, value: Sequence[int]) -> bytes:
4950
return b''.join(self.write_int_8(v) for v in value)
5051

51-
def write_int_16(self, value: int, signed=True) -> bytes:
52+
def write_int_16(self, value: int, signed=True, if_=True) -> bytes:
53+
if not if_:
54+
return b''
5255
return Int.to_bytes(value, length=2, signed=signed)
5356

5457
def write_int_16_array(self, value: Sequence[int]) -> bytes:
@@ -66,14 +69,14 @@ def write_float(self, value: float) -> bytes:
6669
def write_float_array(self, value: Sequence[float]) -> bytes:
6770
return b''.join(self.write_float(v) for v in value)
6871

69-
def write_class(self, value: 'GenieClass') -> bytes:
70-
retval = value.to_bytes()
72+
def write_class(self, value: 'GenieClass', version: Version) -> bytes:
73+
retval = value.to_bytes(version)
7174
if retval:
7275
return retval
7376
return b''
7477

75-
def write_class_array(self, value: Sequence['GenieClass | None']) -> bytes:
76-
retval = b''.join(self.write_class(v) for v in value if v is not None)
78+
def write_class_array(self, value: Sequence['GenieClass | None'], version: Version) -> bytes:
79+
retval = b''.join(self.write_class(v, version) for v in value if v is not None)
7780
if retval:
7881
return retval
7982
return b''
@@ -86,6 +89,7 @@ class ByteHandler:
8689
def __init__(self, content: memoryview):
8790
self.content = content
8891
self.offset = 0
92+
self.version: Version = Version.UNDEFINED
8993

9094
def consume_range(self, length: int) -> memoryview:
9195
start = self.offset

src/genieutils/datfile.py

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from genieutils.terrainblock import TerrainBlock
1616
from genieutils.terrainrestriction import TerrainRestriction
1717
from genieutils.unitheaders import UnitHeaders
18+
from genieutils.versions import Version
1819

1920

2021
@dataclass
@@ -56,6 +57,7 @@ def save(self, target_file: Path | PathLike | str):
5657
@classmethod
5758
def from_bytes(cls, content: ByteHandler) -> 'DatFile':
5859
version = content.read_string(8)
60+
content.version = Version(version)
5961
terrain_restrictions_size = content.read_int_16()
6062
terrains_used_1 = content.read_int_16()
6163
float_ptr_terrain_tables = content.read_int_32_array(terrain_restrictions_size)
@@ -115,7 +117,8 @@ def from_bytes(cls, content: ByteHandler) -> 'DatFile':
115117
def graphic_pointers(self) -> list[int]:
116118
return [(0 if g is None else 1) for g in self.graphics]
117119

118-
def to_bytes(self) -> bytes:
120+
def to_bytes(self, version: Version = Version.UNDEFINED) -> bytes:
121+
version = Version(self.version)
119122
terrain_restrictions_size = len(self.terrain_restrictions)
120123
assert len(self.float_ptr_terrain_tables) == len(self.terrain_pass_graphic_pointers) == terrain_restrictions_size
121124
terrains_used = 0
@@ -128,30 +131,30 @@ def to_bytes(self) -> bytes:
128131
self.write_int_16(terrains_used),
129132
self.write_int_32_array(self.float_ptr_terrain_tables),
130133
self.write_int_32_array(self.terrain_pass_graphic_pointers),
131-
self.write_class_array(self.terrain_restrictions),
134+
self.write_class_array(self.terrain_restrictions, version),
132135
self.write_int_16(len(self.player_colours)),
133-
self.write_class_array(self.player_colours),
136+
self.write_class_array(self.player_colours, version),
134137
self.write_int_16(len(self.sounds)),
135-
self.write_class_array(self.sounds),
138+
self.write_class_array(self.sounds, version),
136139
self.write_int_16(len(self.graphics)),
137140
self.write_int_32_array(self.graphic_pointers),
138-
self.write_class_array(self.graphics),
139-
self.write_class(self.terrain_block),
140-
self.write_class(self.random_maps),
141+
self.write_class_array(self.graphics, version),
142+
self.write_class(self.terrain_block, version),
143+
self.write_class(self.random_maps, version),
141144
self.write_int_32(len(self.effects)),
142-
self.write_class_array(self.effects),
145+
self.write_class_array(self.effects, version),
143146
self.write_int_32(len(self.unit_headers)),
144-
self.write_class_array(self.unit_headers),
147+
self.write_class_array(self.unit_headers, version),
145148
self.write_int_16(len(self.civs)),
146-
self.write_class_array(self.civs),
149+
self.write_class_array(self.civs, version),
147150
self.write_int_16(len(self.techs)),
148-
self.write_class_array(self.techs),
151+
self.write_class_array(self.techs, version),
149152
self.write_int_32(self.time_slice),
150153
self.write_int_32(self.unit_kill_rate),
151154
self.write_int_32(self.unit_kill_total),
152155
self.write_int_32(self.unit_hit_point_rate),
153156
self.write_int_32(self.unit_hit_point_total),
154157
self.write_int_32(self.razing_kill_rate),
155158
self.write_int_32(self.razing_kill_total),
156-
self.write_class(self.tech_tree),
159+
self.write_class(self.tech_tree, version),
157160
])

src/genieutils/effect.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from dataclasses import dataclass
22

33
from genieutils.common import ByteHandler, GenieClass
4+
from genieutils.versions import Version
45

56

67
@dataclass
@@ -21,7 +22,7 @@ def from_bytes(cls, content: ByteHandler) -> 'EffectCommand':
2122
d=content.read_float(),
2223
)
2324

24-
def to_bytes(self) -> bytes:
25+
def to_bytes(self, version: Version) -> bytes:
2526
return b''.join([
2627
self.write_int_8(self.type),
2728
self.write_int_16(self.a),
@@ -46,9 +47,9 @@ def from_bytes(cls, content: ByteHandler) -> 'Effect':
4647
effect_commands=effect_commands,
4748
)
4849

49-
def to_bytes(self) -> bytes:
50+
def to_bytes(self, version: Version) -> bytes:
5051
return b''.join([
5152
self.write_debug_string(self.name),
5253
self.write_int_16(len(self.effect_commands)),
53-
self.write_class_array(self.effect_commands),
54+
self.write_class_array(self.effect_commands, version),
5455
])

src/genieutils/graphic.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from dataclasses import dataclass
22

33
from genieutils.common import ByteHandler, GenieClass
4+
from genieutils.versions import Version
45

56

67
@dataclass
@@ -25,7 +26,7 @@ def from_bytes(cls, content: ByteHandler) -> 'GraphicDelta':
2526
padding_2=content.read_int_16(),
2627
)
2728

28-
def to_bytes(self) -> bytes:
29+
def to_bytes(self, version: Version) -> bytes:
2930
return b''.join([
3031
self.write_int_16(self.graphic_id),
3132
self.write_int_16(self.padding_1),
@@ -63,7 +64,7 @@ def from_bytes(cls, content: ByteHandler) -> 'GraphicAngleSound':
6364
wwise_sound_id_3=content.read_int_32(),
6465
)
6566

66-
def to_bytes(self) -> bytes:
67+
def to_bytes(self, version: Version) -> bytes:
6768
return b''.join([
6869
self.write_int_16(self.frame_num),
6970
self.write_int_16(self.sound_id),
@@ -158,7 +159,7 @@ def from_bytes(cls, content: ByteHandler) -> 'Graphic':
158159
angle_sounds=angle_sounds,
159160
)
160161

161-
def to_bytes(self) -> bytes:
162+
def to_bytes(self, version: Version) -> bytes:
162163
return b''.join([
163164
self.write_debug_string(self.name),
164165
self.write_debug_string(self.file_name),
@@ -183,6 +184,6 @@ def to_bytes(self) -> bytes:
183184
self.write_int_16(self.id),
184185
self.write_int_8(self.mirroring_mode),
185186
self.write_int_8(self.editor_flag),
186-
self.write_class_array(self.deltas),
187-
self.write_class_array(self.angle_sounds),
187+
self.write_class_array(self.deltas, version),
188+
self.write_class_array(self.angle_sounds, version),
188189
])

src/genieutils/playercolour.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from dataclasses import dataclass
22

33
from genieutils.common import ByteHandler, GenieClass
4+
from genieutils.versions import Version
45

56

67
@dataclass
@@ -29,7 +30,7 @@ def from_bytes(cls, content: ByteHandler) -> 'PlayerColour':
2930
statistics_text=content.read_int_32(),
3031
)
3132

32-
def to_bytes(self) -> bytes:
33+
def to_bytes(self, version: Version) -> bytes:
3334
return b''.join([
3435
self.write_int_32(self.id),
3536
self.write_int_32(self.player_color_base),

src/genieutils/randommaps.py

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from dataclasses import dataclass
22

33
from genieutils.common import ByteHandler, GenieClass
4+
from genieutils.versions import Version
45

56

67
@dataclass
@@ -37,7 +38,7 @@ def from_bytes(cls, content: ByteHandler) -> 'MapUnit':
3738
max_distance_to_players=content.read_int_32(),
3839
)
3940

40-
def to_bytes(self) -> bytes:
41+
def to_bytes(self, version: Version) -> bytes:
4142
return b''.join([
4243
self.write_int_32(self.unit),
4344
self.write_int_32(self.host_terrain),
@@ -75,7 +76,7 @@ def from_bytes(cls, content: ByteHandler) -> 'MapTerrain':
7576
clumpiness=content.read_int_32(),
7677
)
7778

78-
def to_bytes(self) -> bytes:
79+
def to_bytes(self, version: Version) -> bytes:
7980
return b''.join([
8081
self.write_int_32(self.proportion),
8182
self.write_int_32(self.terrain),
@@ -124,7 +125,7 @@ def from_bytes(cls, content: ByteHandler) -> 'MapLand':
124125
clumpiness=content.read_int_32(),
125126
)
126127

127-
def to_bytes(self) -> bytes:
128+
def to_bytes(self, version: Version) -> bytes:
128129
return b''.join([
129130
self.write_int_32(self.land_id),
130131
self.write_int_32(self.terrain, signed=False),
@@ -164,7 +165,7 @@ def from_bytes(cls, content: ByteHandler) -> 'MapElevation':
164165
tile_spacing=content.read_int_32(),
165166
)
166167

167-
def to_bytes(self) -> bytes:
168+
def to_bytes(self, version: Version) -> bytes:
168169
return b''.join([
169170
self.write_int_32(self.proportion),
170171
self.write_int_32(self.terrain),
@@ -249,7 +250,7 @@ def from_bytes(cls, content: ByteHandler) -> 'MapInfo':
249250
map_elevations=map_elevations,
250251
)
251252

252-
def to_bytes(self) -> bytes:
253+
def to_bytes(self, version: Version) -> bytes:
253254
return b''.join([
254255
self.write_int_32(self.map_id),
255256
self.write_int_32(self.border_south_west),
@@ -263,16 +264,16 @@ def to_bytes(self) -> bytes:
263264
self.write_int_32(self.unused_id),
264265
self.write_int_32(self.map_lands_size, signed=False),
265266
self.write_int_32(self.map_lands_ptr),
266-
self.write_class_array(self.map_lands),
267+
self.write_class_array(self.map_lands, version),
267268
self.write_int_32(self.map_terrains_size, signed=False),
268269
self.write_int_32(self.map_terrains_ptr),
269-
self.write_class_array(self.map_terrains),
270+
self.write_class_array(self.map_terrains, version),
270271
self.write_int_32(self.map_units_size, signed=False),
271272
self.write_int_32(self.map_units_ptr),
272-
self.write_class_array(self.map_units),
273+
self.write_class_array(self.map_units, version),
273274
self.write_int_32(self.map_elevations_size, signed=False),
274275
self.write_int_32(self.map_elevations_ptr),
275-
self.write_class_array(self.map_elevations),
276+
self.write_class_array(self.map_elevations, version),
276277
])
277278

278279

@@ -294,11 +295,11 @@ def from_bytes(cls, content: ByteHandler) -> 'RandomMaps':
294295
map_info_2=map_info_2,
295296
)
296297

297-
def to_bytes(self) -> bytes:
298+
def to_bytes(self, version: Version) -> bytes:
298299
assert len(self.map_info_1) == len(self.map_info_2)
299300
return b''.join([
300301
self.write_int_32(len(self.map_info_1), signed=False),
301302
self.write_int_32(self.random_maps_ptr),
302-
self.write_class_array(self.map_info_1),
303-
self.write_class_array(self.map_info_2),
303+
self.write_class_array(self.map_info_1, version),
304+
self.write_class_array(self.map_info_2, version),
304305
])

src/genieutils/sound.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from dataclasses import dataclass
22

33
from genieutils.common import ByteHandler, GenieClass
4+
from genieutils.versions import Version
45

56

67
@dataclass
@@ -21,7 +22,7 @@ def from_bytes(cls, content: ByteHandler) -> 'SoundItem':
2122
icon_set=content.read_int_16(),
2223
)
2324

24-
def to_bytes(self) -> bytes:
25+
def to_bytes(self, version: Version) -> bytes:
2526
return b''.join([
2627
self.write_debug_string(self.filename),
2728
self.write_int_32(self.resource_id),
@@ -55,12 +56,12 @@ def from_bytes(cls, content: ByteHandler) -> 'Sound':
5556
items=items,
5657
)
5758

58-
def to_bytes(self) -> bytes:
59+
def to_bytes(self, version: Version) -> bytes:
5960
return b''.join([
6061
self.write_int_16(self.id),
6162
self.write_int_16(self.play_delay),
6263
self.write_int_16(len(self.items)),
6364
self.write_int_32(self.cache_time),
6465
self.write_int_16(self.total_probability),
65-
self.write_class_array(self.items),
66+
self.write_class_array(self.items, version),
6667
])

src/genieutils/task.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from dataclasses import dataclass
22

33
from genieutils.common import ByteHandler, GenieClass
4+
from genieutils.versions import Version
45

56

67
@dataclass
@@ -73,7 +74,7 @@ def from_bytes(cls, content: ByteHandler) -> 'Task':
7374
wwise_resource_deposit_sound_id=content.read_int_32(),
7475
)
7576

76-
def to_bytes(self) -> bytes:
77+
def to_bytes(self, version: Version) -> bytes:
7778
return b''.join([
7879
self.write_int_16(self.task_type),
7980
self.write_int_16(self.id),

0 commit comments

Comments
 (0)