Skip to content

Commit e6340f6

Browse files
committed
Make list handling more straightforward
- do not store parsed list lengths - use list lengths calculated via len() for serialisation - assert equality of list lengths that are parsed with equal length
1 parent d5ea266 commit e6340f6

File tree

12 files changed

+75
-139
lines changed

12 files changed

+75
-139
lines changed

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ from genieutils.datfile import DatFile
4444

4545
data = DatFile.parse('path/to/empires2_x2_p1.dat')
4646
for civ in data.civs:
47-
civ.units[434].bird.task_size -= 1
4847
civ.units[434].bird.tasks.pop()
4948
data.save('path/to/modded/empires2_x2_p1.dat')
5049
```

src/genieutils/civ.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,10 @@
88
class Civ(GenieClass):
99
player_type: int
1010
name: str
11-
resources_size: int
1211
tech_tree_id: int
1312
team_bonus_id: int
1413
resources: list[float]
1514
icon_set: int
16-
units_size: int
17-
unit_pointers: list[int]
1815
units: list[Unit | None]
1916

2017
@classmethod
@@ -32,26 +29,27 @@ def from_bytes(cls, content: ByteHandler) -> 'Civ':
3229
return cls(
3330
player_type=player_type,
3431
name=name,
35-
resources_size=resources_size,
3632
tech_tree_id=tech_tree_id,
3733
team_bonus_id=team_bonus_id,
3834
resources=resources,
3935
icon_set=icon_set,
40-
units_size=units_size,
41-
unit_pointers=unit_pointers,
4236
units=units,
4337
)
4438

39+
@property
40+
def unit_pointers(self) -> list[int]:
41+
return [(0 if u is None else 1) for u in self.units]
42+
4543
def to_bytes(self) -> bytes:
4644
return b''.join([
4745
self.write_int_8(self.player_type),
4846
self.write_debug_string(self.name),
49-
self.write_int_16(self.resources_size),
47+
self.write_int_16(len(self.resources)),
5048
self.write_int_16(self.tech_tree_id),
5149
self.write_int_16(self.team_bonus_id),
5250
self.write_float_array(self.resources),
5351
self.write_int_8(self.icon_set),
54-
self.write_int_16(self.units_size),
52+
self.write_int_16(len(self.units)),
5553
self.write_int_32_array(self.unit_pointers),
56-
self.write_class_array_with_pointers(self.unit_pointers, self.units),
54+
self.write_class_array(self.units),
5755
])

src/genieutils/common.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -72,14 +72,8 @@ def write_class(self, value: 'GenieClass') -> bytes:
7272
return retval
7373
return b''
7474

75-
def write_class_array(self, value: Sequence['GenieClass']) -> bytes:
76-
retval = b''.join(self.write_class(v) for v in value)
77-
if retval:
78-
return retval
79-
return b''
80-
81-
def write_class_array_with_pointers(self, pointers: list[int], value: list['GenieClass']) -> bytes:
82-
retval = b''.join(self.write_class(v) for i, v in enumerate(value) if pointers[i])
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)
8377
if retval:
8478
return retval
8579
return b''

src/genieutils/datfile.py

Lines changed: 41 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -20,27 +20,17 @@
2020
@dataclass
2121
class DatFile(GenieClass):
2222
version: str
23-
terrain_restrictions_size: int
24-
terrains_used_1: int
2523
float_ptr_terrain_tables: list[int]
2624
terrain_pass_graphic_pointers: list[int]
2725
terrain_restrictions: list[TerrainRestriction]
28-
player_colours_size: int
2926
player_colours: list[PlayerColour]
30-
sounds_size: int
3127
sounds: list[Sound]
32-
graphics_size: int
33-
graphic_pointers: list[int]
3428
graphics: list[Graphic | None]
3529
terrain_block: TerrainBlock
3630
random_maps: RandomMaps
37-
effects_size: int
3831
effects: list[Effect]
39-
unit_headers_size: int
4032
unit_headers: list[UnitHeaders]
41-
civs_size: int
4233
civs: list[Civ]
43-
techs_size: int
4434
techs: list[Tech]
4535
time_slice: int
4636
unit_kill_rate: int
@@ -98,63 +88,63 @@ def from_bytes(cls, content: ByteHandler) -> 'DatFile':
9888
razing_kill_total = content.read_int_32()
9989
tech_tree = content.read_class(TechTree)
10090
return cls(
101-
version,
102-
terrain_restrictions_size,
103-
terrains_used_1,
104-
float_ptr_terrain_tables,
105-
terrain_pass_graphic_pointers,
106-
terrain_restrictions,
107-
player_colours_size,
108-
player_colours,
109-
sounds_size,
110-
sounds,
111-
graphics_size,
112-
graphic_pointers,
113-
graphics,
114-
terrain_block,
115-
random_maps,
116-
effects_size,
117-
effects,
118-
unit_headers_size,
119-
unit_headers,
120-
civs_size,
121-
civs,
122-
techs_size,
123-
techs,
124-
time_slice,
125-
unit_kill_rate,
126-
unit_kill_total,
127-
unit_hit_point_rate,
128-
unit_hit_point_total,
129-
razing_kill_rate,
130-
razing_kill_total,
131-
tech_tree,
91+
version=version,
92+
float_ptr_terrain_tables=float_ptr_terrain_tables,
93+
terrain_pass_graphic_pointers=terrain_pass_graphic_pointers,
94+
terrain_restrictions=terrain_restrictions,
95+
player_colours=player_colours,
96+
sounds=sounds,
97+
graphics=graphics,
98+
terrain_block=terrain_block,
99+
random_maps=random_maps,
100+
effects=effects,
101+
unit_headers=unit_headers,
102+
civs=civs,
103+
techs=techs,
104+
time_slice=time_slice,
105+
unit_kill_rate=unit_kill_rate,
106+
unit_kill_total=unit_kill_total,
107+
unit_hit_point_rate=unit_hit_point_rate,
108+
unit_hit_point_total=unit_hit_point_total,
109+
razing_kill_rate=razing_kill_rate,
110+
razing_kill_total=razing_kill_total,
111+
tech_tree=tech_tree,
132112
)
133113

114+
@property
115+
def graphic_pointers(self) -> list[int]:
116+
return [(0 if g is None else 1) for g in self.graphics]
117+
134118
def to_bytes(self) -> bytes:
119+
terrain_restrictions_size = len(self.terrain_restrictions)
120+
assert len(self.float_ptr_terrain_tables) == len(self.terrain_pass_graphic_pointers) == terrain_restrictions_size
121+
terrains_used = 0
122+
if self.terrain_restrictions:
123+
terrains_used = len(self.terrain_restrictions[0].passable_buildable_dmg_multiplier)
124+
135125
return b''.join([
136126
self.write_string(8, self.version),
137-
self.write_int_16(self.terrain_restrictions_size),
138-
self.write_int_16(self.terrains_used_1),
127+
self.write_int_16(terrain_restrictions_size),
128+
self.write_int_16(terrains_used),
139129
self.write_int_32_array(self.float_ptr_terrain_tables),
140130
self.write_int_32_array(self.terrain_pass_graphic_pointers),
141131
self.write_class_array(self.terrain_restrictions),
142-
self.write_int_16(self.player_colours_size),
132+
self.write_int_16(len(self.player_colours)),
143133
self.write_class_array(self.player_colours),
144-
self.write_int_16(self.sounds_size),
134+
self.write_int_16(len(self.sounds)),
145135
self.write_class_array(self.sounds),
146-
self.write_int_16(self.graphics_size),
136+
self.write_int_16(len(self.graphics)),
147137
self.write_int_32_array(self.graphic_pointers),
148-
self.write_class_array_with_pointers(self.graphic_pointers, self.graphics),
138+
self.write_class_array(self.graphics),
149139
self.write_class(self.terrain_block),
150140
self.write_class(self.random_maps),
151-
self.write_int_32(self.effects_size),
141+
self.write_int_32(len(self.effects)),
152142
self.write_class_array(self.effects),
153-
self.write_int_32(self.unit_headers_size),
143+
self.write_int_32(len(self.unit_headers)),
154144
self.write_class_array(self.unit_headers),
155-
self.write_int_16(self.civs_size),
145+
self.write_int_16(len(self.civs)),
156146
self.write_class_array(self.civs),
157-
self.write_int_16(self.techs_size),
147+
self.write_int_16(len(self.techs)),
158148
self.write_class_array(self.techs),
159149
self.write_int_32(self.time_slice),
160150
self.write_int_32(self.unit_kill_rate),

src/genieutils/effect.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ def to_bytes(self) -> bytes:
3434
@dataclass
3535
class Effect(GenieClass):
3636
name: str
37-
effect_command_count: int
3837
effect_commands: list[EffectCommand]
3938

4039
@classmethod
@@ -44,13 +43,12 @@ def from_bytes(cls, content: ByteHandler) -> 'Effect':
4443
effect_commands = content.read_class_array(EffectCommand, effect_command_count)
4544
return cls(
4645
name=name,
47-
effect_command_count=effect_command_count,
4846
effect_commands=effect_commands,
4947
)
5048

5149
def to_bytes(self) -> bytes:
5250
return b''.join([
5351
self.write_debug_string(self.name),
54-
self.write_int_16(self.effect_command_count),
52+
self.write_int_16(len(self.effect_commands)),
5553
self.write_class_array(self.effect_commands),
5654
])

src/genieutils/graphic.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,6 @@ class Graphic(GenieClass):
8989
player_color: int
9090
transparent_selection: int
9191
coordinates: tuple[int, int, int, int]
92-
delta_count: int
9392
sound_id: int
9493
wwise_sound_id: int
9594
angle_sounds_used: int
@@ -143,7 +142,6 @@ def from_bytes(cls, content: ByteHandler) -> 'Graphic':
143142
player_color=player_color,
144143
transparent_selection=transparent_selection,
145144
coordinates=coordinates,
146-
delta_count=delta_count,
147145
sound_id=sound_id,
148146
wwise_sound_id=wwise_sound_id,
149147
angle_sounds_used=angle_sounds_used,
@@ -172,7 +170,7 @@ def to_bytes(self) -> bytes:
172170
self.write_int_16(self.player_color),
173171
self.write_int_8(self.transparent_selection),
174172
self.write_int_16_array(self.coordinates),
175-
self.write_int_16(self.delta_count),
173+
self.write_int_16(len(self.deltas)),
176174
self.write_int_16(self.sound_id),
177175
self.write_int_32(self.wwise_sound_id),
178176
self.write_int_8(self.angle_sounds_used),

src/genieutils/randommaps.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,6 @@ def to_bytes(self) -> bytes:
278278

279279
@dataclass
280280
class RandomMaps(GenieClass):
281-
random_map_count: int
282281
random_maps_ptr: int
283282
map_info_1: list[MapInfo]
284283
map_info_2: list[MapInfo]
@@ -290,15 +289,15 @@ def from_bytes(cls, content: ByteHandler) -> 'RandomMaps':
290289
map_info_1 = content.read_class_array(MapInfo, random_map_count)
291290
map_info_2 = content.read_class_array(MapInfo, random_map_count)
292291
return cls(
293-
random_map_count=random_map_count,
294292
random_maps_ptr=random_maps_ptr,
295293
map_info_1=map_info_1,
296294
map_info_2=map_info_2,
297295
)
298296

299297
def to_bytes(self) -> bytes:
298+
assert len(self.map_info_1) == len(self.map_info_2)
300299
return b''.join([
301-
self.write_int_32(self.random_map_count, signed=False),
300+
self.write_int_32(len(self.map_info_1), signed=False),
302301
self.write_int_32(self.random_maps_ptr),
303302
self.write_class_array(self.map_info_1),
304303
self.write_class_array(self.map_info_2),

src/genieutils/sound.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ def to_bytes(self) -> bytes:
3535
class Sound(GenieClass):
3636
id: int
3737
play_delay: int
38-
items_size: int
3938
cache_time: int
4039
total_probability: int
4140
items: list[SoundItem]
@@ -51,7 +50,6 @@ def from_bytes(cls, content: ByteHandler) -> 'Sound':
5150
return cls(
5251
id=id_,
5352
play_delay=play_delay,
54-
items_size=items_size,
5553
cache_time=cache_time,
5654
total_probability=total_probability,
5755
items=items,
@@ -61,7 +59,7 @@ def to_bytes(self) -> bytes:
6159
return b''.join([
6260
self.write_int_16(self.id),
6361
self.write_int_16(self.play_delay),
64-
self.write_int_16(self.items_size),
62+
self.write_int_16(len(self.items)),
6563
self.write_int_32(self.cache_time),
6664
self.write_int_16(self.total_probability),
6765
self.write_class_array(self.items),

0 commit comments

Comments
 (0)