Skip to content

Commit

Permalink
Implement FileVersion VER 7.8
Browse files Browse the repository at this point in the history
Make Unit.Bird.drop_sites length variable

Add basic support for multiple file versions
  • Loading branch information
HSZemi committed May 5, 2024
1 parent fb41fbb commit 8c6b3eb
Show file tree
Hide file tree
Showing 17 changed files with 151 additions and 102 deletions.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ packages = ["src/genieutils"]

[project]
name = "genieutils-py"
version = "0.0.3"
version = "0.0.4"
authors = [
{ name = "SiegeEngineers", email = "[email protected]" },
]
Expand Down
5 changes: 3 additions & 2 deletions src/genieutils/civ.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from genieutils.common import ByteHandler, GenieClass
from genieutils.unit import Unit
from genieutils.versions import Version


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

def to_bytes(self) -> bytes:
def to_bytes(self, version: Version) -> bytes:
return b''.join([
self.write_int_8(self.player_type),
self.write_debug_string(self.name),
Expand All @@ -51,5 +52,5 @@ def to_bytes(self) -> bytes:
self.write_int_8(self.icon_set),
self.write_int_16(len(self.units)),
self.write_int_32_array(self.unit_pointers),
self.write_class_array(self.units),
self.write_class_array(self.units, version),
])
16 changes: 10 additions & 6 deletions src/genieutils/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from typing import TypeVar, Sequence

from genieutils.datatypes import Int, Float, String
from genieutils.versions import Version

TILE_TYPE_COUNT = 19
TERRAIN_COUNT = 200
Expand Down Expand Up @@ -31,7 +32,7 @@ def from_bytes(cls, data: 'ByteHandler'):
def from_bytes_with_count(cls, data: 'ByteHandler', terrains_used_1: int):
raise NotImplementedError

def to_bytes(self) -> bytes:
def to_bytes(self, version: Version) -> bytes:
raise NotImplementedError

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

def write_int_16(self, value: int, signed=True) -> bytes:
def write_int_16(self, value: int, signed=True, if_=True) -> bytes:
if not if_:
return b''
return Int.to_bytes(value, length=2, signed=signed)

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

def write_class(self, value: 'GenieClass') -> bytes:
retval = value.to_bytes()
def write_class(self, value: 'GenieClass', version: Version) -> bytes:
retval = value.to_bytes(version)
if retval:
return retval
return b''

def write_class_array(self, value: Sequence['GenieClass | None']) -> bytes:
retval = b''.join(self.write_class(v) for v in value if v is not None)
def write_class_array(self, value: Sequence['GenieClass | None'], version: Version) -> bytes:
retval = b''.join(self.write_class(v, version) for v in value if v is not None)
if retval:
return retval
return b''
Expand All @@ -86,6 +89,7 @@ class ByteHandler:
def __init__(self, content: memoryview):
self.content = content
self.offset = 0
self.version: Version = Version.UNDEFINED

def consume_range(self, length: int) -> memoryview:
start = self.offset
Expand Down
27 changes: 15 additions & 12 deletions src/genieutils/datfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from genieutils.terrainblock import TerrainBlock
from genieutils.terrainrestriction import TerrainRestriction
from genieutils.unitheaders import UnitHeaders
from genieutils.versions import Version


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

def to_bytes(self) -> bytes:
def to_bytes(self, version: Version = Version.UNDEFINED) -> bytes:
version = Version(self.version)
terrain_restrictions_size = len(self.terrain_restrictions)
assert len(self.float_ptr_terrain_tables) == len(self.terrain_pass_graphic_pointers) == terrain_restrictions_size
terrains_used = 0
Expand All @@ -128,30 +131,30 @@ def to_bytes(self) -> bytes:
self.write_int_16(terrains_used),
self.write_int_32_array(self.float_ptr_terrain_tables),
self.write_int_32_array(self.terrain_pass_graphic_pointers),
self.write_class_array(self.terrain_restrictions),
self.write_class_array(self.terrain_restrictions, version),
self.write_int_16(len(self.player_colours)),
self.write_class_array(self.player_colours),
self.write_class_array(self.player_colours, version),
self.write_int_16(len(self.sounds)),
self.write_class_array(self.sounds),
self.write_class_array(self.sounds, version),
self.write_int_16(len(self.graphics)),
self.write_int_32_array(self.graphic_pointers),
self.write_class_array(self.graphics),
self.write_class(self.terrain_block),
self.write_class(self.random_maps),
self.write_class_array(self.graphics, version),
self.write_class(self.terrain_block, version),
self.write_class(self.random_maps, version),
self.write_int_32(len(self.effects)),
self.write_class_array(self.effects),
self.write_class_array(self.effects, version),
self.write_int_32(len(self.unit_headers)),
self.write_class_array(self.unit_headers),
self.write_class_array(self.unit_headers, version),
self.write_int_16(len(self.civs)),
self.write_class_array(self.civs),
self.write_class_array(self.civs, version),
self.write_int_16(len(self.techs)),
self.write_class_array(self.techs),
self.write_class_array(self.techs, version),
self.write_int_32(self.time_slice),
self.write_int_32(self.unit_kill_rate),
self.write_int_32(self.unit_kill_total),
self.write_int_32(self.unit_hit_point_rate),
self.write_int_32(self.unit_hit_point_total),
self.write_int_32(self.razing_kill_rate),
self.write_int_32(self.razing_kill_total),
self.write_class(self.tech_tree),
self.write_class(self.tech_tree, version),
])
7 changes: 4 additions & 3 deletions src/genieutils/effect.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from dataclasses import dataclass

from genieutils.common import ByteHandler, GenieClass
from genieutils.versions import Version


@dataclass
Expand All @@ -21,7 +22,7 @@ def from_bytes(cls, content: ByteHandler) -> 'EffectCommand':
d=content.read_float(),
)

def to_bytes(self) -> bytes:
def to_bytes(self, version: Version) -> bytes:
return b''.join([
self.write_int_8(self.type),
self.write_int_16(self.a),
Expand All @@ -46,9 +47,9 @@ def from_bytes(cls, content: ByteHandler) -> 'Effect':
effect_commands=effect_commands,
)

def to_bytes(self) -> bytes:
def to_bytes(self, version: Version) -> bytes:
return b''.join([
self.write_debug_string(self.name),
self.write_int_16(len(self.effect_commands)),
self.write_class_array(self.effect_commands),
self.write_class_array(self.effect_commands, version),
])
11 changes: 6 additions & 5 deletions src/genieutils/graphic.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from dataclasses import dataclass

from genieutils.common import ByteHandler, GenieClass
from genieutils.versions import Version


@dataclass
Expand All @@ -25,7 +26,7 @@ def from_bytes(cls, content: ByteHandler) -> 'GraphicDelta':
padding_2=content.read_int_16(),
)

def to_bytes(self) -> bytes:
def to_bytes(self, version: Version) -> bytes:
return b''.join([
self.write_int_16(self.graphic_id),
self.write_int_16(self.padding_1),
Expand Down Expand Up @@ -63,7 +64,7 @@ def from_bytes(cls, content: ByteHandler) -> 'GraphicAngleSound':
wwise_sound_id_3=content.read_int_32(),
)

def to_bytes(self) -> bytes:
def to_bytes(self, version: Version) -> bytes:
return b''.join([
self.write_int_16(self.frame_num),
self.write_int_16(self.sound_id),
Expand Down Expand Up @@ -158,7 +159,7 @@ def from_bytes(cls, content: ByteHandler) -> 'Graphic':
angle_sounds=angle_sounds,
)

def to_bytes(self) -> bytes:
def to_bytes(self, version: Version) -> bytes:
return b''.join([
self.write_debug_string(self.name),
self.write_debug_string(self.file_name),
Expand All @@ -183,6 +184,6 @@ def to_bytes(self) -> bytes:
self.write_int_16(self.id),
self.write_int_8(self.mirroring_mode),
self.write_int_8(self.editor_flag),
self.write_class_array(self.deltas),
self.write_class_array(self.angle_sounds),
self.write_class_array(self.deltas, version),
self.write_class_array(self.angle_sounds, version),
])
3 changes: 2 additions & 1 deletion src/genieutils/playercolour.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from dataclasses import dataclass

from genieutils.common import ByteHandler, GenieClass
from genieutils.versions import Version


@dataclass
Expand Down Expand Up @@ -29,7 +30,7 @@ def from_bytes(cls, content: ByteHandler) -> 'PlayerColour':
statistics_text=content.read_int_32(),
)

def to_bytes(self) -> bytes:
def to_bytes(self, version: Version) -> bytes:
return b''.join([
self.write_int_32(self.id),
self.write_int_32(self.player_color_base),
Expand Down
25 changes: 13 additions & 12 deletions src/genieutils/randommaps.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from dataclasses import dataclass

from genieutils.common import ByteHandler, GenieClass
from genieutils.versions import Version


@dataclass
Expand Down Expand Up @@ -37,7 +38,7 @@ def from_bytes(cls, content: ByteHandler) -> 'MapUnit':
max_distance_to_players=content.read_int_32(),
)

def to_bytes(self) -> bytes:
def to_bytes(self, version: Version) -> bytes:
return b''.join([
self.write_int_32(self.unit),
self.write_int_32(self.host_terrain),
Expand Down Expand Up @@ -75,7 +76,7 @@ def from_bytes(cls, content: ByteHandler) -> 'MapTerrain':
clumpiness=content.read_int_32(),
)

def to_bytes(self) -> bytes:
def to_bytes(self, version: Version) -> bytes:
return b''.join([
self.write_int_32(self.proportion),
self.write_int_32(self.terrain),
Expand Down Expand Up @@ -124,7 +125,7 @@ def from_bytes(cls, content: ByteHandler) -> 'MapLand':
clumpiness=content.read_int_32(),
)

def to_bytes(self) -> bytes:
def to_bytes(self, version: Version) -> bytes:
return b''.join([
self.write_int_32(self.land_id),
self.write_int_32(self.terrain, signed=False),
Expand Down Expand Up @@ -164,7 +165,7 @@ def from_bytes(cls, content: ByteHandler) -> 'MapElevation':
tile_spacing=content.read_int_32(),
)

def to_bytes(self) -> bytes:
def to_bytes(self, version: Version) -> bytes:
return b''.join([
self.write_int_32(self.proportion),
self.write_int_32(self.terrain),
Expand Down Expand Up @@ -249,7 +250,7 @@ def from_bytes(cls, content: ByteHandler) -> 'MapInfo':
map_elevations=map_elevations,
)

def to_bytes(self) -> bytes:
def to_bytes(self, version: Version) -> bytes:
return b''.join([
self.write_int_32(self.map_id),
self.write_int_32(self.border_south_west),
Expand All @@ -263,16 +264,16 @@ def to_bytes(self) -> bytes:
self.write_int_32(self.unused_id),
self.write_int_32(self.map_lands_size, signed=False),
self.write_int_32(self.map_lands_ptr),
self.write_class_array(self.map_lands),
self.write_class_array(self.map_lands, version),
self.write_int_32(self.map_terrains_size, signed=False),
self.write_int_32(self.map_terrains_ptr),
self.write_class_array(self.map_terrains),
self.write_class_array(self.map_terrains, version),
self.write_int_32(self.map_units_size, signed=False),
self.write_int_32(self.map_units_ptr),
self.write_class_array(self.map_units),
self.write_class_array(self.map_units, version),
self.write_int_32(self.map_elevations_size, signed=False),
self.write_int_32(self.map_elevations_ptr),
self.write_class_array(self.map_elevations),
self.write_class_array(self.map_elevations, version),
])


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

def to_bytes(self) -> bytes:
def to_bytes(self, version: Version) -> bytes:
assert len(self.map_info_1) == len(self.map_info_2)
return b''.join([
self.write_int_32(len(self.map_info_1), signed=False),
self.write_int_32(self.random_maps_ptr),
self.write_class_array(self.map_info_1),
self.write_class_array(self.map_info_2),
self.write_class_array(self.map_info_1, version),
self.write_class_array(self.map_info_2, version),
])
7 changes: 4 additions & 3 deletions src/genieutils/sound.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from dataclasses import dataclass

from genieutils.common import ByteHandler, GenieClass
from genieutils.versions import Version


@dataclass
Expand All @@ -21,7 +22,7 @@ def from_bytes(cls, content: ByteHandler) -> 'SoundItem':
icon_set=content.read_int_16(),
)

def to_bytes(self) -> bytes:
def to_bytes(self, version: Version) -> bytes:
return b''.join([
self.write_debug_string(self.filename),
self.write_int_32(self.resource_id),
Expand Down Expand Up @@ -55,12 +56,12 @@ def from_bytes(cls, content: ByteHandler) -> 'Sound':
items=items,
)

def to_bytes(self) -> bytes:
def to_bytes(self, version: Version) -> bytes:
return b''.join([
self.write_int_16(self.id),
self.write_int_16(self.play_delay),
self.write_int_16(len(self.items)),
self.write_int_32(self.cache_time),
self.write_int_16(self.total_probability),
self.write_class_array(self.items),
self.write_class_array(self.items, version),
])
3 changes: 2 additions & 1 deletion src/genieutils/task.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from dataclasses import dataclass

from genieutils.common import ByteHandler, GenieClass
from genieutils.versions import Version


@dataclass
Expand Down Expand Up @@ -73,7 +74,7 @@ def from_bytes(cls, content: ByteHandler) -> 'Task':
wwise_resource_deposit_sound_id=content.read_int_32(),
)

def to_bytes(self) -> bytes:
def to_bytes(self, version: Version) -> bytes:
return b''.join([
self.write_int_16(self.task_type),
self.write_int_16(self.id),
Expand Down
Loading

0 comments on commit 8c6b3eb

Please sign in to comment.