arkparser 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. arkparser/__init__.py +117 -0
  2. arkparser/common/__init__.py +72 -0
  3. arkparser/common/binary_reader.py +402 -0
  4. arkparser/common/exceptions.py +99 -0
  5. arkparser/common/map_config.py +166 -0
  6. arkparser/common/types.py +249 -0
  7. arkparser/common/version_detection.py +195 -0
  8. arkparser/data_models.py +801 -0
  9. arkparser/export.py +485 -0
  10. arkparser/files/__init__.py +25 -0
  11. arkparser/files/base.py +309 -0
  12. arkparser/files/cloud_inventory.py +259 -0
  13. arkparser/files/profile.py +205 -0
  14. arkparser/files/tribe.py +155 -0
  15. arkparser/files/world_save.py +699 -0
  16. arkparser/game_objects/__init__.py +32 -0
  17. arkparser/game_objects/container.py +180 -0
  18. arkparser/game_objects/game_object.py +273 -0
  19. arkparser/game_objects/location.py +87 -0
  20. arkparser/models/__init__.py +29 -0
  21. arkparser/models/character.py +227 -0
  22. arkparser/models/creature.py +642 -0
  23. arkparser/models/item.py +207 -0
  24. arkparser/models/player.py +263 -0
  25. arkparser/models/stats.py +226 -0
  26. arkparser/models/structure.py +176 -0
  27. arkparser/models/tribe.py +291 -0
  28. arkparser/properties/__init__.py +77 -0
  29. arkparser/properties/base.py +329 -0
  30. arkparser/properties/byte_property.py +230 -0
  31. arkparser/properties/compound.py +1125 -0
  32. arkparser/properties/primitives.py +803 -0
  33. arkparser/properties/registry.py +236 -0
  34. arkparser/py.typed +0 -0
  35. arkparser/structs/__init__.py +60 -0
  36. arkparser/structs/base.py +63 -0
  37. arkparser/structs/colors.py +108 -0
  38. arkparser/structs/misc.py +133 -0
  39. arkparser/structs/property_list.py +101 -0
  40. arkparser/structs/registry.py +140 -0
  41. arkparser/structs/vectors.py +221 -0
  42. arkparser-0.1.0.dist-info/METADATA +833 -0
  43. arkparser-0.1.0.dist-info/RECORD +46 -0
  44. arkparser-0.1.0.dist-info/WHEEL +5 -0
  45. arkparser-0.1.0.dist-info/licenses/LICENSE +21 -0
  46. arkparser-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,140 @@
1
+ """
2
+ Struct Registry - Type Dispatch for Struct Reading.
3
+
4
+ This module provides the central registry for reading structs from binary data.
5
+ It dispatches to the appropriate struct class based on the struct type name.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import typing as t
11
+
12
+ from ..common.binary_reader import BinaryReader
13
+ from .base import Struct
14
+ from .colors import Color, LinearColor
15
+ from .misc import CustomItemDataRef, Guid, UniqueNetIdRepl
16
+ from .property_list import StructPropertyList
17
+ from .vectors import IntPoint, IntVector, Quat, Rotator, Vector, Vector2D
18
+
19
+ # Type alias for struct reader functions
20
+ StructReader = t.Callable[[BinaryReader, bool], Struct]
21
+
22
+
23
+ # Registry mapping struct type names to their classes
24
+ STRUCT_REGISTRY: dict[str, type[Struct]] = {
25
+ # Vectors and transforms
26
+ "Vector": Vector,
27
+ "Vector2D": Vector2D,
28
+ "Rotator": Rotator,
29
+ "Quat": Quat,
30
+ "IntPoint": IntPoint,
31
+ "IntVector": IntVector,
32
+ # Colors
33
+ "Color": Color,
34
+ "LinearColor": LinearColor,
35
+ # Misc
36
+ "UniqueNetIdRepl": UniqueNetIdRepl,
37
+ "Guid": Guid,
38
+ "CustomItemDataRef": CustomItemDataRef,
39
+ }
40
+
41
+
42
+ # Some array names map to specific struct types
43
+ # Used when reading struct arrays where the element type isn't explicitly stated
44
+ ARRAY_NAME_TO_STRUCT_TYPE: dict[str, str] = {
45
+ "CustomColors": "Color",
46
+ "CustomColours_60_7D3267C846B277953C0C41AEBD54FBCB": "LinearColor",
47
+ }
48
+
49
+
50
+ def read_struct(
51
+ reader: BinaryReader,
52
+ struct_type: str,
53
+ is_asa: bool = False,
54
+ has_index_prefix: bool = False,
55
+ name_table: list[str] | None = None,
56
+ worldsave_format: bool = False,
57
+ ) -> Struct:
58
+ """
59
+ Read a struct from the binary reader.
60
+
61
+ Dispatches to the appropriate struct class based on the type name.
62
+ Unknown struct types are read as property lists.
63
+
64
+ Args:
65
+ reader: The binary reader positioned at the struct data.
66
+ struct_type: The struct type name (e.g., "Vector", "Color").
67
+ is_asa: True for ASA format, False for ASE.
68
+ has_index_prefix: True if struct data starts with an int32 array index
69
+ (common in ASA indexed struct properties after the first element).
70
+ name_table: Optional name table for world saves (version 6+).
71
+ worldsave_format: True for ASA WorldSave SQLite object format.
72
+
73
+ Returns:
74
+ The parsed Struct object.
75
+ """
76
+ # Skip the array index prefix if present (ASA indexed struct properties)
77
+ if has_index_prefix:
78
+ _array_index = reader.read_int32()
79
+
80
+ # Look up the struct type in the registry
81
+ struct_class = STRUCT_REGISTRY.get(struct_type)
82
+
83
+ if struct_class is not None:
84
+ # Native struct - use the class's read method
85
+ result = struct_class.read(reader, is_asa, worldsave_format=worldsave_format)
86
+ # Note: Unlike property lists, native structs do NOT have a trailing
87
+ # null byte - the StructProperty extra_byte is read before this function
88
+ # is called, and the struct data is exactly data_size bytes.
89
+ return result
90
+ else:
91
+ # Unknown struct type - read as property list
92
+ return StructPropertyList.read(
93
+ reader,
94
+ is_asa,
95
+ struct_type=struct_type,
96
+ name_table=name_table,
97
+ worldsave_format=worldsave_format,
98
+ )
99
+
100
+
101
+ def read_struct_for_array(
102
+ reader: BinaryReader,
103
+ array_name: str,
104
+ is_asa: bool = False,
105
+ name_table: list[str] | None = None,
106
+ ) -> Struct:
107
+ """
108
+ Read a struct element from a struct array.
109
+
110
+ Uses the array name to determine the struct type if possible.
111
+ For unknown array names, reads as property list.
112
+
113
+ Args:
114
+ reader: The binary reader positioned at the struct data.
115
+ array_name: The name of the array property.
116
+ is_asa: True for ASA format, False for ASE.
117
+ name_table: Optional name table for world saves (version 6+).
118
+
119
+ Returns:
120
+ The parsed Struct object.
121
+ """
122
+ # Try to map array name to struct type
123
+ struct_type = ARRAY_NAME_TO_STRUCT_TYPE.get(array_name)
124
+
125
+ if struct_type:
126
+ # Known array type
127
+ return read_struct(reader, struct_type, is_asa, name_table=name_table)
128
+ else:
129
+ # Unknown array - read as property list
130
+ return StructPropertyList.read(reader, is_asa, struct_type="PropertyList", name_table=name_table)
131
+
132
+
133
+ def is_native_struct(struct_type: str) -> bool:
134
+ """Check if a struct type is a known native struct."""
135
+ return struct_type in STRUCT_REGISTRY
136
+
137
+
138
+ def get_array_struct_type(array_name: str) -> str | None:
139
+ """Get the struct type for a named array, if known."""
140
+ return ARRAY_NAME_TO_STRUCT_TYPE.get(array_name)
@@ -0,0 +1,221 @@
1
+ """
2
+ Vector and Rotation Structs.
3
+
4
+ Geometric structs used for positions, rotations, and quaternions.
5
+ Note: ASE uses Float (4 bytes), ASA uses Double (8 bytes) for vectors.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import typing as t
11
+ from dataclasses import dataclass
12
+
13
+ from .base import NativeStruct
14
+
15
+ if t.TYPE_CHECKING:
16
+ from ..common.binary_reader import BinaryReader
17
+
18
+
19
+ @dataclass
20
+ class Vector(NativeStruct):
21
+ """
22
+ 3D Vector (x, y, z).
23
+
24
+ ASE: 3 x Float (12 bytes)
25
+ ASA: 3 x Double (24 bytes)
26
+ """
27
+
28
+ x: float = 0.0
29
+ y: float = 0.0
30
+ z: float = 0.0
31
+
32
+ @property
33
+ def struct_type(self) -> str:
34
+ return "Vector"
35
+
36
+ def to_dict(self) -> dict[str, float]:
37
+ return {"x": self.x, "y": self.y, "z": self.z}
38
+
39
+ @classmethod
40
+ def read(cls, reader: BinaryReader, is_asa: bool = False, **kwargs: t.Any) -> Vector:
41
+ """Read a Vector from the archive."""
42
+ if is_asa:
43
+ return cls(
44
+ x=reader.read_double(),
45
+ y=reader.read_double(),
46
+ z=reader.read_double(),
47
+ )
48
+ else:
49
+ return cls(
50
+ x=reader.read_float(),
51
+ y=reader.read_float(),
52
+ z=reader.read_float(),
53
+ )
54
+
55
+
56
+ @dataclass
57
+ class Vector2D(NativeStruct):
58
+ """
59
+ 2D Vector (x, y).
60
+
61
+ ASE: 2 x Float (8 bytes)
62
+ ASA: 2 x Double (16 bytes)
63
+ """
64
+
65
+ x: float = 0.0
66
+ y: float = 0.0
67
+
68
+ @property
69
+ def struct_type(self) -> str:
70
+ return "Vector2D"
71
+
72
+ def to_dict(self) -> dict[str, float]:
73
+ return {"x": self.x, "y": self.y}
74
+
75
+ @classmethod
76
+ def read(cls, reader: BinaryReader, is_asa: bool = False, **kwargs: t.Any) -> Vector2D:
77
+ """Read a Vector2D from the archive."""
78
+ if is_asa:
79
+ return cls(
80
+ x=reader.read_double(),
81
+ y=reader.read_double(),
82
+ )
83
+ else:
84
+ return cls(
85
+ x=reader.read_float(),
86
+ y=reader.read_float(),
87
+ )
88
+
89
+
90
+ @dataclass
91
+ class Rotator(NativeStruct):
92
+ """
93
+ Rotation angles (pitch, yaw, roll).
94
+
95
+ ASE: 3 x Float (12 bytes)
96
+ ASA: 3 x Double (24 bytes)
97
+ """
98
+
99
+ pitch: float = 0.0
100
+ yaw: float = 0.0
101
+ roll: float = 0.0
102
+
103
+ @property
104
+ def struct_type(self) -> str:
105
+ return "Rotator"
106
+
107
+ def to_dict(self) -> dict[str, float]:
108
+ return {"pitch": self.pitch, "yaw": self.yaw, "roll": self.roll}
109
+
110
+ @classmethod
111
+ def read(cls, reader: BinaryReader, is_asa: bool = False, **kwargs: t.Any) -> Rotator:
112
+ """Read a Rotator from the archive."""
113
+ if is_asa:
114
+ return cls(
115
+ pitch=reader.read_double(),
116
+ yaw=reader.read_double(),
117
+ roll=reader.read_double(),
118
+ )
119
+ else:
120
+ return cls(
121
+ pitch=reader.read_float(),
122
+ yaw=reader.read_float(),
123
+ roll=reader.read_float(),
124
+ )
125
+
126
+
127
+ @dataclass
128
+ class Quat(NativeStruct):
129
+ """
130
+ Quaternion rotation (x, y, z, w).
131
+
132
+ ASE: 4 x Float (16 bytes)
133
+ ASA: 4 x Float (16 bytes) for non-worldsave, 4 x Double (32 bytes) for worldsave
134
+ """
135
+
136
+ x: float = 0.0
137
+ y: float = 0.0
138
+ z: float = 0.0
139
+ w: float = 0.0
140
+
141
+ @property
142
+ def struct_type(self) -> str:
143
+ return "Quat"
144
+
145
+ def to_dict(self) -> dict[str, float]:
146
+ return {"x": self.x, "y": self.y, "z": self.z, "w": self.w}
147
+
148
+ @classmethod
149
+ def read(cls, reader: BinaryReader, is_asa: bool = False, worldsave_format: bool = False, **kwargs: t.Any) -> Quat:
150
+ """Read a Quat from the archive."""
151
+ if worldsave_format:
152
+ # WorldSave uses doubles for quaternions
153
+ return cls(
154
+ x=reader.read_double(),
155
+ y=reader.read_double(),
156
+ z=reader.read_double(),
157
+ w=reader.read_double(),
158
+ )
159
+ else:
160
+ return cls(
161
+ x=reader.read_float(),
162
+ y=reader.read_float(),
163
+ z=reader.read_float(),
164
+ w=reader.read_float(),
165
+ )
166
+
167
+
168
+ @dataclass
169
+ class IntPoint(NativeStruct):
170
+ """
171
+ 2D Integer point (x, y).
172
+
173
+ Always 2 x Int32 (8 bytes).
174
+ """
175
+
176
+ x: int = 0
177
+ y: int = 0
178
+
179
+ @property
180
+ def struct_type(self) -> str:
181
+ return "IntPoint"
182
+
183
+ def to_dict(self) -> dict[str, int]:
184
+ return {"x": self.x, "y": self.y}
185
+
186
+ @classmethod
187
+ def read(cls, reader: BinaryReader, is_asa: bool = False, **kwargs: t.Any) -> IntPoint:
188
+ """Read an IntPoint from the archive."""
189
+ return cls(
190
+ x=reader.read_int32(),
191
+ y=reader.read_int32(),
192
+ )
193
+
194
+
195
+ @dataclass
196
+ class IntVector(NativeStruct):
197
+ """
198
+ 3D Integer vector (x, y, z).
199
+
200
+ Always 3 x Int32 (12 bytes).
201
+ """
202
+
203
+ x: int = 0
204
+ y: int = 0
205
+ z: int = 0
206
+
207
+ @property
208
+ def struct_type(self) -> str:
209
+ return "IntVector"
210
+
211
+ def to_dict(self) -> dict[str, int]:
212
+ return {"x": self.x, "y": self.y, "z": self.z}
213
+
214
+ @classmethod
215
+ def read(cls, reader: BinaryReader, is_asa: bool = False, **kwargs: t.Any) -> IntVector:
216
+ """Read an IntVector from the archive."""
217
+ return cls(
218
+ x=reader.read_int32(),
219
+ y=reader.read_int32(),
220
+ z=reader.read_int32(),
221
+ )