arkparser 0.4.6__tar.gz → 0.5.3__tar.gz

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 (61) hide show
  1. {arkparser-0.4.6 → arkparser-0.5.3}/PKG-INFO +11 -13
  2. {arkparser-0.4.6 → arkparser-0.5.3}/README.md +10 -12
  3. {arkparser-0.4.6 → arkparser-0.5.3}/arkparser/__init__.py +98 -98
  4. {arkparser-0.4.6 → arkparser-0.5.3}/arkparser/common/normalization.py +6 -0
  5. {arkparser-0.4.6 → arkparser-0.5.3}/arkparser/data_models.py +8 -0
  6. {arkparser-0.4.6 → arkparser-0.5.3}/arkparser/export.py +890 -311
  7. {arkparser-0.4.6 → arkparser-0.5.3}/arkparser/files/profile.py +19 -3
  8. {arkparser-0.4.6 → arkparser-0.5.3}/arkparser/files/world_save.py +17 -34
  9. {arkparser-0.4.6 → arkparser-0.5.3}/arkparser/game_objects/game_object.py +40 -20
  10. {arkparser-0.4.6 → arkparser-0.5.3}/arkparser/properties/base.py +2 -2
  11. {arkparser-0.4.6 → arkparser-0.5.3}/arkparser/properties/byte_property.py +1 -1
  12. {arkparser-0.4.6 → arkparser-0.5.3}/arkparser/properties/compound.py +14 -13
  13. {arkparser-0.4.6 → arkparser-0.5.3}/arkparser/properties/primitives.py +14 -14
  14. {arkparser-0.4.6 → arkparser-0.5.3}/arkparser/properties/registry.py +18 -5
  15. {arkparser-0.4.6 → arkparser-0.5.3}/arkparser.egg-info/PKG-INFO +11 -13
  16. {arkparser-0.4.6 → arkparser-0.5.3}/pyproject.toml +86 -86
  17. {arkparser-0.4.6 → arkparser-0.5.3}/tests/test_review_fixes.py +20 -4
  18. {arkparser-0.4.6 → arkparser-0.5.3}/LICENSE +0 -0
  19. {arkparser-0.4.6 → arkparser-0.5.3}/arkparser/common/__init__.py +0 -0
  20. {arkparser-0.4.6 → arkparser-0.5.3}/arkparser/common/binary_reader.py +0 -0
  21. {arkparser-0.4.6 → arkparser-0.5.3}/arkparser/common/exceptions.py +0 -0
  22. {arkparser-0.4.6 → arkparser-0.5.3}/arkparser/common/map_config.py +0 -0
  23. {arkparser-0.4.6 → arkparser-0.5.3}/arkparser/common/types.py +0 -0
  24. {arkparser-0.4.6 → arkparser-0.5.3}/arkparser/common/version_detection.py +0 -0
  25. {arkparser-0.4.6 → arkparser-0.5.3}/arkparser/files/__init__.py +0 -0
  26. {arkparser-0.4.6 → arkparser-0.5.3}/arkparser/files/base.py +0 -0
  27. {arkparser-0.4.6 → arkparser-0.5.3}/arkparser/files/cloud_inventory.py +0 -0
  28. {arkparser-0.4.6 → arkparser-0.5.3}/arkparser/files/tribe.py +0 -0
  29. {arkparser-0.4.6 → arkparser-0.5.3}/arkparser/game_objects/__init__.py +0 -0
  30. {arkparser-0.4.6 → arkparser-0.5.3}/arkparser/game_objects/container.py +0 -0
  31. {arkparser-0.4.6 → arkparser-0.5.3}/arkparser/game_objects/location.py +0 -0
  32. {arkparser-0.4.6 → arkparser-0.5.3}/arkparser/properties/__init__.py +0 -0
  33. {arkparser-0.4.6 → arkparser-0.5.3}/arkparser/structs/__init__.py +0 -0
  34. {arkparser-0.4.6 → arkparser-0.5.3}/arkparser/structs/base.py +0 -0
  35. {arkparser-0.4.6 → arkparser-0.5.3}/arkparser/structs/colors.py +0 -0
  36. {arkparser-0.4.6 → arkparser-0.5.3}/arkparser/structs/misc.py +0 -0
  37. {arkparser-0.4.6 → arkparser-0.5.3}/arkparser/structs/property_list.py +0 -0
  38. {arkparser-0.4.6 → arkparser-0.5.3}/arkparser/structs/registry.py +0 -0
  39. {arkparser-0.4.6 → arkparser-0.5.3}/arkparser/structs/vectors.py +0 -0
  40. {arkparser-0.4.6 → arkparser-0.5.3}/arkparser.egg-info/SOURCES.txt +0 -0
  41. {arkparser-0.4.6 → arkparser-0.5.3}/arkparser.egg-info/dependency_links.txt +0 -0
  42. {arkparser-0.4.6 → arkparser-0.5.3}/arkparser.egg-info/requires.txt +0 -0
  43. {arkparser-0.4.6 → arkparser-0.5.3}/arkparser.egg-info/top_level.txt +0 -0
  44. {arkparser-0.4.6 → arkparser-0.5.3}/setup.cfg +0 -0
  45. {arkparser-0.4.6 → arkparser-0.5.3}/tests/test_asa_header_position.py +0 -0
  46. {arkparser-0.4.6 → arkparser-0.5.3}/tests/test_asa_name_table.py +0 -0
  47. {arkparser-0.4.6 → arkparser-0.5.3}/tests/test_ase_cluster_drift.py +0 -0
  48. {arkparser-0.4.6 → arkparser-0.5.3}/tests/test_binary_reader.py +0 -0
  49. {arkparser-0.4.6 → arkparser-0.5.3}/tests/test_binary_reader_layouts.py +0 -0
  50. {arkparser-0.4.6 → arkparser-0.5.3}/tests/test_cloud_export.py +0 -0
  51. {arkparser-0.4.6 → arkparser-0.5.3}/tests/test_cloud_inventory.py +0 -0
  52. {arkparser-0.4.6 → arkparser-0.5.3}/tests/test_cryopod_export.py +0 -0
  53. {arkparser-0.4.6 → arkparser-0.5.3}/tests/test_current_stats.py +0 -0
  54. {arkparser-0.4.6 → arkparser-0.5.3}/tests/test_data_models.py +0 -0
  55. {arkparser-0.4.6 → arkparser-0.5.3}/tests/test_export.py +0 -0
  56. {arkparser-0.4.6 → arkparser-0.5.3}/tests/test_game_objects.py +0 -0
  57. {arkparser-0.4.6 → arkparser-0.5.3}/tests/test_profile.py +0 -0
  58. {arkparser-0.4.6 → arkparser-0.5.3}/tests/test_tribe.py +0 -0
  59. {arkparser-0.4.6 → arkparser-0.5.3}/tests/test_v13_property_layouts.py +0 -0
  60. {arkparser-0.4.6 → arkparser-0.5.3}/tests/test_version_detection.py +0 -0
  61. {arkparser-0.4.6 → arkparser-0.5.3}/tests/test_world_save.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: arkparser
3
- Version: 0.4.6
3
+ Version: 0.5.3
4
4
  Summary: Pure Python parser for ARK: Survival Evolved and ARK: Survival Ascended save files
5
5
  Author: Vertyco
6
6
  License-Expression: MIT
@@ -56,7 +56,7 @@ A pure-Python library for parsing ARK: Survival Evolved (ASE) and ARK: Survival
56
56
  - **Cloud Inventory / Obelisk**: uploaded creatures, items, cryopod contents
57
57
  - **World Saves** (`.ark`): full map state (creatures, structures, items, players)
58
58
  - **Dual format**: automatic ASE (v5-12) / ASA (v13-14+, SQLite) detection
59
- - **Legacy-parity export**: drop-in JSON output matching `ASVExport.exe` schema, plus parser-only extras under `extra_*` keys
59
+ - **Legacy-parity export**: drop-in JSON output matching `ASVExport.exe` schema, plus parser-only extras under descriptive snake_case keys (no namespace prefix; never overloading a legacy key)
60
60
  - **Fast**: pure-Python `BinaryReader` (`int.from_bytes` + `struct.Struct` unpackers, slots-based dataclasses), a 30 MB ASE save (65k objects) loads in ~3s on CPython 3.14
61
61
 
62
62
  ## Installation
@@ -297,11 +297,8 @@ For the richest output, hand `export_players` **both**, assemble a wrapper for e
297
297
  | `tribe` | legacy | tribe name |
298
298
  | `sex` | legacy | `"Female"` / `"Male"` |
299
299
  | `lvl` | legacy | `BaseCharacterLevel + ExtraCharacterLevel` |
300
- | `lat`, `lon` | legacy | `0.0` (profile/cluster files carry no in-world location) |
301
300
  | `hp` .. `craft`, `fort` | legacy | `NumberOfLevelUpPointsApplied[i]` (10 visible stats) |
302
301
  | `torp`, `temp` | added | same source, indices legacy never emitted |
303
- | `active` | legacy | last-active datetime; currently `null` (placeholder for parity) |
304
- | `ccc` | legacy | `"0 0 0"` (no in-world position from profile/cluster source) |
305
302
  | `achievements` | legacy | reserved array (currently empty for parity) |
306
303
  | `netAddress` | legacy (now populated) | Last client IP ARK persisted (`SavedNetworkAddress` in profile `MyData`). Legacy ASVExport reads the same field (ContentPlayer.cs:157 ASE / :341 ASA). `""` when the profile lacks it (e.g. never-played placeholders). ASA stores a clean IPv4/IPv6 string; some ASE saves store an engine-truncated value (e.g. `"[2001"`) reproduced verbatim, matching legacy. |
307
304
  | `steamid`, `dataFile` | legacy | platform net id and `{steamid}.arkprofile` filename |
@@ -325,12 +322,14 @@ For the richest output, hand `export_players` **both**, assemble a wrapper for e
325
322
 
326
323
  #### `ASV_Tribes` schema
327
324
 
325
+ `ASV_Tribes` (and `ASV_TribeLogs` / `ASV_Players`, which iterate the same list) is a **superset** of the loaded `.arktribe` files, mirroring legacy `ContentContainer`: it seeds two sentinels (`[ASV Unclaimed]` id `2000000000`, `[ASV Abandoned]` id `-2147483648`), adds every file-backed tribe, then synthesizes a stub tribe for each player profile not already in a tribe (`Tribe of <name>`, id = `PlayerDataID`), each distinct structure `TargetingTeam` (`>= 50000`), and each distinct in-world tame `TargetingTeam` — deduped by id. Cross-server tribes that exist **only** in cluster cloud-inventory files (no map presence) are not synthesized.
326
+
328
327
  | Field | Origin | Source / formula |
329
328
  |---|---|---|
330
- | `tribeid` | legacy | `TribeID` / parser `Tribe.tribe_id` |
331
- | `tribe` | legacy | tribe name |
332
- | `players` | legacy | member count |
333
- | `members` | legacy | list of `{ign, lvl, playerid, playername, steamid}`. `lvl` and `steamid` only populate when a matching `.arkprofile` is loaded into `save.profiles`, the `.arktribe` file itself doesn't carry per-member level / platform id, so the parser cross-references by `player_id`. Empty (`""`) when no profile is available for that member. |
329
+ | `tribeid` | legacy | `TribeID` / parser `Tribe.tribe_id`, or a synthesized stub/sentinel id (see above) |
330
+ | `tribe` | legacy | tribe name (file `TribeName`; for stubs, `OwnerName`/`TamerString`; or `Tribe of <character>` for a solo) |
331
+ | `players` | legacy | count of players **allocated** to this tribe (profiles whose explicit team / membership / solo id resolves here, plus member back-fill), matching legacy `Players.Count` — not the raw `.arktribe` member count |
332
+ | `members` | legacy | list of `{ign, lvl, playerid, playername, steamid}` built from the allocated players. `lvl` and `steamid` populate from the matching `.arkprofile`; members with no profile (back-filled from the tribe's member list) carry `lvl=0`, `steamid=""`. |
334
333
  | `tames`, `structures` | legacy | counts derived from `WorldSave` (creatures + structures whose `TargetingTeam` matches) |
335
334
  | `uploadedTames` | legacy | reserved (currently `0`) |
336
335
  | `active` | legacy | ISO 8601 datetime of the most recent tribe log entry, converted from in-game "Day N, HH:MM:SS" via the save's anchor. `null` when no parseable log entries or anchors are missing. |
@@ -352,9 +351,9 @@ For the richest output, hand `export_players` **both**, assemble a wrapper for e
352
351
  | Field | Origin | Source / formula |
353
352
  |---|---|---|
354
353
  | `id` | added | `GameObject.id`, internal numeric identifier. Cross-references the values in other records' `linked_structures` / `saddle_structures` / `attached_dino_id` lists. |
355
- | `tribeid` | legacy | `TargetingTeam` |
356
- | `tribe` | legacy | `OwnerName` |
357
- | `struct` | legacy | `GameObject.class_name` |
354
+ | `tribeid` | legacy | `TargetingTeam` for player-owned structures (`>= 50000`). Unowned structures fall to the synthetic `[ASV Abandoned]` tribe id `-2147483648` (legacy `int.MinValue`), mirroring `ContentContainer.cs`. |
355
+ | `tribe` | legacy | resolved owning-tribe name: the loaded `.arktribe` `TribeName`, else the structure's `OwnerName` / `TamerString`; `"[ASV Abandoned]"` for unowned. (Legacy emits the resolved tribe name, not the raw `OwnerName`.) |
356
+ | `struct` | legacy | `GameObject.class_name`. Unowned map elements / crates / debug actors (`Button_*`, `*Vein_*`, `*Nest_*`, `*Beaver*`, `BeeHive_C`, `ArtifactCrate_*`, `TributeTerminal_*`, `SupplyCrate_*`) are excluded — surfaced via `ASV_MapStructures` instead, matching legacy's abandoned-structure filter. |
358
357
  | `name` | legacy | `BoxName` (empty when it matches the class name, mirroring legacy's no-rename strip) |
359
358
  | `locked` | legacy | `bIsPinLocked` or `bIsLocked` |
360
359
  | `created` | legacy (richer) | ISO 8601 datetime with the local TZ of the parser machine, computed `save.file_mtime + (OriginalCreationTime - game_time)` (mirrors legacy `ContentContainer.GetApproxDateTimeOf`). `null` when the anchors are missing. |
@@ -362,7 +361,6 @@ For the richest output, hand `export_players` **both**, assemble a wrapper for e
362
361
  | `lat`, `lon`, `ccc` | legacy | location via `MapConfig`, **rounded to 2 decimals** (parser-only nicety, not legacy parity) |
363
362
  | `isSwitchedOn` | legacy | `bContainerActivated`, emitted only when the structure is powered (`bIsPowered` or `bHasFuel`); omitted otherwise. Mirrors legacy `ContentStructure.cs` / `ContentPack.cs` (`IsSwitchedOn.HasValue`). |
364
363
  | `decay_reset` | added | `bHasResetDecayTime` |
365
- | `last_ally_in_range_seconds` | added | raw `LastInAllyRangeTime` / `LastInAllyRangeTimeSerialized` / `LastInAllyRangeSerialized` (in-game seconds, float) |
366
364
  | `last_ally_in_range` | added | ISO 8601 datetime with local TZ. `null` when the save lacks the anchors. |
367
365
  | `painting_id` | added | `UniquePaintingId` |
368
366
  | `feeding_inclusions` | added | `FeedingDinoList` class names when `DinoFeedingListType == 1` (ASA feeding troughs) |
@@ -22,7 +22,7 @@ A pure-Python library for parsing ARK: Survival Evolved (ASE) and ARK: Survival
22
22
  - **Cloud Inventory / Obelisk**: uploaded creatures, items, cryopod contents
23
23
  - **World Saves** (`.ark`): full map state (creatures, structures, items, players)
24
24
  - **Dual format**: automatic ASE (v5-12) / ASA (v13-14+, SQLite) detection
25
- - **Legacy-parity export**: drop-in JSON output matching `ASVExport.exe` schema, plus parser-only extras under `extra_*` keys
25
+ - **Legacy-parity export**: drop-in JSON output matching `ASVExport.exe` schema, plus parser-only extras under descriptive snake_case keys (no namespace prefix; never overloading a legacy key)
26
26
  - **Fast**: pure-Python `BinaryReader` (`int.from_bytes` + `struct.Struct` unpackers, slots-based dataclasses), a 30 MB ASE save (65k objects) loads in ~3s on CPython 3.14
27
27
 
28
28
  ## Installation
@@ -263,11 +263,8 @@ For the richest output, hand `export_players` **both**, assemble a wrapper for e
263
263
  | `tribe` | legacy | tribe name |
264
264
  | `sex` | legacy | `"Female"` / `"Male"` |
265
265
  | `lvl` | legacy | `BaseCharacterLevel + ExtraCharacterLevel` |
266
- | `lat`, `lon` | legacy | `0.0` (profile/cluster files carry no in-world location) |
267
266
  | `hp` .. `craft`, `fort` | legacy | `NumberOfLevelUpPointsApplied[i]` (10 visible stats) |
268
267
  | `torp`, `temp` | added | same source, indices legacy never emitted |
269
- | `active` | legacy | last-active datetime; currently `null` (placeholder for parity) |
270
- | `ccc` | legacy | `"0 0 0"` (no in-world position from profile/cluster source) |
271
268
  | `achievements` | legacy | reserved array (currently empty for parity) |
272
269
  | `netAddress` | legacy (now populated) | Last client IP ARK persisted (`SavedNetworkAddress` in profile `MyData`). Legacy ASVExport reads the same field (ContentPlayer.cs:157 ASE / :341 ASA). `""` when the profile lacks it (e.g. never-played placeholders). ASA stores a clean IPv4/IPv6 string; some ASE saves store an engine-truncated value (e.g. `"[2001"`) reproduced verbatim, matching legacy. |
273
270
  | `steamid`, `dataFile` | legacy | platform net id and `{steamid}.arkprofile` filename |
@@ -291,12 +288,14 @@ For the richest output, hand `export_players` **both**, assemble a wrapper for e
291
288
 
292
289
  #### `ASV_Tribes` schema
293
290
 
291
+ `ASV_Tribes` (and `ASV_TribeLogs` / `ASV_Players`, which iterate the same list) is a **superset** of the loaded `.arktribe` files, mirroring legacy `ContentContainer`: it seeds two sentinels (`[ASV Unclaimed]` id `2000000000`, `[ASV Abandoned]` id `-2147483648`), adds every file-backed tribe, then synthesizes a stub tribe for each player profile not already in a tribe (`Tribe of <name>`, id = `PlayerDataID`), each distinct structure `TargetingTeam` (`>= 50000`), and each distinct in-world tame `TargetingTeam` — deduped by id. Cross-server tribes that exist **only** in cluster cloud-inventory files (no map presence) are not synthesized.
292
+
294
293
  | Field | Origin | Source / formula |
295
294
  |---|---|---|
296
- | `tribeid` | legacy | `TribeID` / parser `Tribe.tribe_id` |
297
- | `tribe` | legacy | tribe name |
298
- | `players` | legacy | member count |
299
- | `members` | legacy | list of `{ign, lvl, playerid, playername, steamid}`. `lvl` and `steamid` only populate when a matching `.arkprofile` is loaded into `save.profiles`, the `.arktribe` file itself doesn't carry per-member level / platform id, so the parser cross-references by `player_id`. Empty (`""`) when no profile is available for that member. |
295
+ | `tribeid` | legacy | `TribeID` / parser `Tribe.tribe_id`, or a synthesized stub/sentinel id (see above) |
296
+ | `tribe` | legacy | tribe name (file `TribeName`; for stubs, `OwnerName`/`TamerString`; or `Tribe of <character>` for a solo) |
297
+ | `players` | legacy | count of players **allocated** to this tribe (profiles whose explicit team / membership / solo id resolves here, plus member back-fill), matching legacy `Players.Count` — not the raw `.arktribe` member count |
298
+ | `members` | legacy | list of `{ign, lvl, playerid, playername, steamid}` built from the allocated players. `lvl` and `steamid` populate from the matching `.arkprofile`; members with no profile (back-filled from the tribe's member list) carry `lvl=0`, `steamid=""`. |
300
299
  | `tames`, `structures` | legacy | counts derived from `WorldSave` (creatures + structures whose `TargetingTeam` matches) |
301
300
  | `uploadedTames` | legacy | reserved (currently `0`) |
302
301
  | `active` | legacy | ISO 8601 datetime of the most recent tribe log entry, converted from in-game "Day N, HH:MM:SS" via the save's anchor. `null` when no parseable log entries or anchors are missing. |
@@ -318,9 +317,9 @@ For the richest output, hand `export_players` **both**, assemble a wrapper for e
318
317
  | Field | Origin | Source / formula |
319
318
  |---|---|---|
320
319
  | `id` | added | `GameObject.id`, internal numeric identifier. Cross-references the values in other records' `linked_structures` / `saddle_structures` / `attached_dino_id` lists. |
321
- | `tribeid` | legacy | `TargetingTeam` |
322
- | `tribe` | legacy | `OwnerName` |
323
- | `struct` | legacy | `GameObject.class_name` |
320
+ | `tribeid` | legacy | `TargetingTeam` for player-owned structures (`>= 50000`). Unowned structures fall to the synthetic `[ASV Abandoned]` tribe id `-2147483648` (legacy `int.MinValue`), mirroring `ContentContainer.cs`. |
321
+ | `tribe` | legacy | resolved owning-tribe name: the loaded `.arktribe` `TribeName`, else the structure's `OwnerName` / `TamerString`; `"[ASV Abandoned]"` for unowned. (Legacy emits the resolved tribe name, not the raw `OwnerName`.) |
322
+ | `struct` | legacy | `GameObject.class_name`. Unowned map elements / crates / debug actors (`Button_*`, `*Vein_*`, `*Nest_*`, `*Beaver*`, `BeeHive_C`, `ArtifactCrate_*`, `TributeTerminal_*`, `SupplyCrate_*`) are excluded — surfaced via `ASV_MapStructures` instead, matching legacy's abandoned-structure filter. |
324
323
  | `name` | legacy | `BoxName` (empty when it matches the class name, mirroring legacy's no-rename strip) |
325
324
  | `locked` | legacy | `bIsPinLocked` or `bIsLocked` |
326
325
  | `created` | legacy (richer) | ISO 8601 datetime with the local TZ of the parser machine, computed `save.file_mtime + (OriginalCreationTime - game_time)` (mirrors legacy `ContentContainer.GetApproxDateTimeOf`). `null` when the anchors are missing. |
@@ -328,7 +327,6 @@ For the richest output, hand `export_players` **both**, assemble a wrapper for e
328
327
  | `lat`, `lon`, `ccc` | legacy | location via `MapConfig`, **rounded to 2 decimals** (parser-only nicety, not legacy parity) |
329
328
  | `isSwitchedOn` | legacy | `bContainerActivated`, emitted only when the structure is powered (`bIsPowered` or `bHasFuel`); omitted otherwise. Mirrors legacy `ContentStructure.cs` / `ContentPack.cs` (`IsSwitchedOn.HasValue`). |
330
329
  | `decay_reset` | added | `bHasResetDecayTime` |
331
- | `last_ally_in_range_seconds` | added | raw `LastInAllyRangeTime` / `LastInAllyRangeTimeSerialized` / `LastInAllyRangeSerialized` (in-game seconds, float) |
332
330
  | `last_ally_in_range` | added | ISO 8601 datetime with local TZ. `null` when the save lacks the anchors. |
333
331
  | `painting_id` | added | `UniquePaintingId` |
334
332
  | `feeding_inclusions` | added | `FeedingDinoList` class names when `DinoFeedingListType == 1` (ASA feeding troughs) |
@@ -1,98 +1,98 @@
1
- """
2
- ARK Save Parser - Parse ARK: Survival Evolved/Ascended save files.
3
-
4
- This package provides tools to parse various ARK save file formats:
5
-
6
- - Profile: Player profile data (.arkprofile)
7
- - Tribe: Tribe data (.arktribe)
8
- - CloudInventory: Obelisk/cloud inventory data (no extension)
9
- - WorldSave: World save data (.ark), auto-detects ASE binary and ASA SQLite
10
-
11
- Supports both ASE (ARK: Survival Evolved) and ASA (ARK: Survival Ascended)
12
- formats with automatic detection.
13
-
14
- Example usage:
15
- >>> from arkparser import Profile, Tribe, CloudInventory, WorldSave
16
- >>> profile = Profile.load("path/to/profile.arkprofile")
17
- >>> tribe = Tribe.load("path/to/tribe.arktribe")
18
- >>> inv = CloudInventory.load("path/to/obelisk_file")
19
- >>> save = WorldSave.load("path/to/TheIsland.ark") # ASE
20
- >>> save = WorldSave.load("path/to/Extinction_WP.ark") # ASA
21
- """
22
-
23
- from arkparser.common.exceptions import ArkParseError
24
- from arkparser.common.map_config import MapConfig, get_map_config, get_map_config_by_name
25
- from arkparser.common.version_detection import (
26
- ArkFileFormat,
27
- ArkFileType,
28
- detect_file_type,
29
- detect_format,
30
- )
31
- from arkparser.data_models import (
32
- CryopodCreature,
33
- DinoStats,
34
- UploadedCreature,
35
- UploadedItem,
36
- )
37
- from arkparser.export import (
38
- export_all,
39
- export_cloud_inventory,
40
- export_cluster_items,
41
- export_cluster_uploads,
42
- export_map_structures,
43
- export_players,
44
- export_structures,
45
- export_tamed,
46
- export_to_files,
47
- export_tribe_logs,
48
- export_tribes,
49
- export_wild,
50
- )
51
- from arkparser.files import CloudInventory, Profile, Tribe, WorldSave
52
- from arkparser.game_objects import GameObject, GameObjectContainer, LocationData
53
-
54
- # Convenience alias - users may know this as "Obelisk" from the game
55
- Obelisk = CloudInventory
56
-
57
- __all__ = [
58
- # File parsers
59
- "Profile",
60
- "Tribe",
61
- "CloudInventory",
62
- "Obelisk",
63
- "WorldSave",
64
- # Cloud-inventory data models
65
- "UploadedCreature",
66
- "UploadedItem",
67
- "CryopodCreature",
68
- "DinoStats",
69
- # Game objects
70
- "GameObject",
71
- "GameObjectContainer",
72
- "LocationData",
73
- # Map config
74
- "MapConfig",
75
- "get_map_config",
76
- "get_map_config_by_name",
77
- # Export
78
- "export_all",
79
- "export_tamed",
80
- "export_wild",
81
- "export_players",
82
- "export_tribes",
83
- "export_structures",
84
- "export_tribe_logs",
85
- "export_map_structures",
86
- "export_cluster_uploads",
87
- "export_cluster_items",
88
- "export_cloud_inventory",
89
- "export_to_files",
90
- # Utilities
91
- "detect_format",
92
- "detect_file_type",
93
- "ArkFileFormat",
94
- "ArkFileType",
95
- "ArkParseError",
96
- ]
97
-
98
- __version__ = "0.4.6"
1
+ """
2
+ ARK Save Parser - Parse ARK: Survival Evolved/Ascended save files.
3
+
4
+ This package provides tools to parse various ARK save file formats:
5
+
6
+ - Profile: Player profile data (.arkprofile)
7
+ - Tribe: Tribe data (.arktribe)
8
+ - CloudInventory: Obelisk/cloud inventory data (no extension)
9
+ - WorldSave: World save data (.ark), auto-detects ASE binary and ASA SQLite
10
+
11
+ Supports both ASE (ARK: Survival Evolved) and ASA (ARK: Survival Ascended)
12
+ formats with automatic detection.
13
+
14
+ Example usage:
15
+ >>> from arkparser import Profile, Tribe, CloudInventory, WorldSave
16
+ >>> profile = Profile.load("path/to/profile.arkprofile")
17
+ >>> tribe = Tribe.load("path/to/tribe.arktribe")
18
+ >>> inv = CloudInventory.load("path/to/obelisk_file")
19
+ >>> save = WorldSave.load("path/to/TheIsland.ark") # ASE
20
+ >>> save = WorldSave.load("path/to/Extinction_WP.ark") # ASA
21
+ """
22
+
23
+ from arkparser.common.exceptions import ArkParseError
24
+ from arkparser.common.map_config import MapConfig, get_map_config, get_map_config_by_name
25
+ from arkparser.common.version_detection import (
26
+ ArkFileFormat,
27
+ ArkFileType,
28
+ detect_file_type,
29
+ detect_format,
30
+ )
31
+ from arkparser.data_models import (
32
+ CryopodCreature,
33
+ DinoStats,
34
+ UploadedCreature,
35
+ UploadedItem,
36
+ )
37
+ from arkparser.export import (
38
+ export_all,
39
+ export_cloud_inventory,
40
+ export_cluster_items,
41
+ export_cluster_uploads,
42
+ export_map_structures,
43
+ export_players,
44
+ export_structures,
45
+ export_tamed,
46
+ export_to_files,
47
+ export_tribe_logs,
48
+ export_tribes,
49
+ export_wild,
50
+ )
51
+ from arkparser.files import CloudInventory, Profile, Tribe, WorldSave
52
+ from arkparser.game_objects import GameObject, GameObjectContainer, LocationData
53
+
54
+ # Convenience alias - users may know this as "Obelisk" from the game
55
+ Obelisk = CloudInventory
56
+
57
+ __all__ = [
58
+ # File parsers
59
+ "Profile",
60
+ "Tribe",
61
+ "CloudInventory",
62
+ "Obelisk",
63
+ "WorldSave",
64
+ # Cloud-inventory data models
65
+ "UploadedCreature",
66
+ "UploadedItem",
67
+ "CryopodCreature",
68
+ "DinoStats",
69
+ # Game objects
70
+ "GameObject",
71
+ "GameObjectContainer",
72
+ "LocationData",
73
+ # Map config
74
+ "MapConfig",
75
+ "get_map_config",
76
+ "get_map_config_by_name",
77
+ # Export
78
+ "export_all",
79
+ "export_tamed",
80
+ "export_wild",
81
+ "export_players",
82
+ "export_tribes",
83
+ "export_structures",
84
+ "export_tribe_logs",
85
+ "export_map_structures",
86
+ "export_cluster_uploads",
87
+ "export_cluster_items",
88
+ "export_cloud_inventory",
89
+ "export_to_files",
90
+ # Utilities
91
+ "detect_format",
92
+ "detect_file_type",
93
+ "ArkFileFormat",
94
+ "ArkFileType",
95
+ "ArkParseError",
96
+ ]
97
+
98
+ __version__ = "0.5.3"
@@ -54,4 +54,10 @@ def normalize_indexed_list(value: t.Any) -> list[t.Any]:
54
54
  return []
55
55
  if isinstance(normalized, list):
56
56
  return normalized
57
+ # A raw ByteProperty array is stored as `bytes` for memory efficiency
58
+ # (8x lighter than list[int]); expose it element-wise as a list of ints so
59
+ # consumers that iterate it (e.g. tribe MembersRankGroups -> int(rank))
60
+ # see the same shape they did before byte arrays were stored as bytes.
61
+ if isinstance(normalized, (bytes, bytearray)):
62
+ return list(normalized)
57
63
  return [normalized]
@@ -364,6 +364,14 @@ class CryopodCreature:
364
364
  props_dict[key] = p.value
365
365
  obj["properties"] = props_dict
366
366
  except Exception:
367
+ # Don't let one drifted object silently zero out: log it so
368
+ # cryopod decode regressions surface instead of returning
369
+ # empty stats with no diagnostic (was a blind swallow).
370
+ logger.debug(
371
+ "Failed to parse cryopod object properties at offset %s",
372
+ obj.get("props_offset"),
373
+ exc_info=True,
374
+ )
367
375
  obj["properties"] = {}
368
376
 
369
377
  # Find creature object (first one) and status component