arkparser 0.5.4__tar.gz → 0.7.0__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.
- {arkparser-0.5.4 → arkparser-0.7.0}/PKG-INFO +51 -11
- {arkparser-0.5.4 → arkparser-0.7.0}/README.md +50 -10
- {arkparser-0.5.4 → arkparser-0.7.0}/arkparser/__init__.py +98 -98
- {arkparser-0.5.4 → arkparser-0.7.0}/arkparser/common/binary_reader.py +98 -5
- {arkparser-0.5.4 → arkparser-0.7.0}/arkparser/common/normalization.py +15 -2
- {arkparser-0.5.4 → arkparser-0.7.0}/arkparser/data_models.py +2 -2
- {arkparser-0.5.4 → arkparser-0.7.0}/arkparser/export.py +425 -130
- {arkparser-0.5.4 → arkparser-0.7.0}/arkparser/files/world_save.py +378 -43
- {arkparser-0.5.4 → arkparser-0.7.0}/arkparser/game_objects/container.py +149 -20
- {arkparser-0.5.4 → arkparser-0.7.0}/arkparser/game_objects/game_object.py +77 -0
- {arkparser-0.5.4 → arkparser-0.7.0}/arkparser/properties/base.py +32 -10
- {arkparser-0.5.4 → arkparser-0.7.0}/arkparser/properties/compound.py +1 -1
- {arkparser-0.5.4 → arkparser-0.7.0}/arkparser/properties/primitives.py +5 -5
- arkparser-0.7.0/arkparser/properties/registry.py +434 -0
- {arkparser-0.5.4 → arkparser-0.7.0}/arkparser.egg-info/PKG-INFO +51 -11
- {arkparser-0.5.4 → arkparser-0.7.0}/arkparser.egg-info/SOURCES.txt +2 -0
- {arkparser-0.5.4 → arkparser-0.7.0}/pyproject.toml +86 -86
- {arkparser-0.5.4 → arkparser-0.7.0}/tests/test_binary_reader.py +19 -1
- {arkparser-0.5.4 → arkparser-0.7.0}/tests/test_cryopod_export.py +1 -1
- arkparser-0.7.0/tests/test_export.py +177 -0
- arkparser-0.7.0/tests/test_golden_exports.py +78 -0
- arkparser-0.7.0/tests/test_lazy_properties.py +164 -0
- {arkparser-0.5.4 → arkparser-0.7.0}/tests/test_review_fixes.py +1 -1
- arkparser-0.5.4/arkparser/properties/registry.py +0 -256
- arkparser-0.5.4/tests/test_export.py +0 -92
- {arkparser-0.5.4 → arkparser-0.7.0}/LICENSE +0 -0
- {arkparser-0.5.4 → arkparser-0.7.0}/arkparser/common/__init__.py +0 -0
- {arkparser-0.5.4 → arkparser-0.7.0}/arkparser/common/exceptions.py +0 -0
- {arkparser-0.5.4 → arkparser-0.7.0}/arkparser/common/map_config.py +0 -0
- {arkparser-0.5.4 → arkparser-0.7.0}/arkparser/common/types.py +0 -0
- {arkparser-0.5.4 → arkparser-0.7.0}/arkparser/common/version_detection.py +0 -0
- {arkparser-0.5.4 → arkparser-0.7.0}/arkparser/files/__init__.py +0 -0
- {arkparser-0.5.4 → arkparser-0.7.0}/arkparser/files/base.py +0 -0
- {arkparser-0.5.4 → arkparser-0.7.0}/arkparser/files/cloud_inventory.py +0 -0
- {arkparser-0.5.4 → arkparser-0.7.0}/arkparser/files/profile.py +0 -0
- {arkparser-0.5.4 → arkparser-0.7.0}/arkparser/files/tribe.py +0 -0
- {arkparser-0.5.4 → arkparser-0.7.0}/arkparser/game_objects/__init__.py +0 -0
- {arkparser-0.5.4 → arkparser-0.7.0}/arkparser/game_objects/location.py +0 -0
- {arkparser-0.5.4 → arkparser-0.7.0}/arkparser/properties/__init__.py +0 -0
- {arkparser-0.5.4 → arkparser-0.7.0}/arkparser/properties/byte_property.py +0 -0
- {arkparser-0.5.4 → arkparser-0.7.0}/arkparser/structs/__init__.py +0 -0
- {arkparser-0.5.4 → arkparser-0.7.0}/arkparser/structs/base.py +0 -0
- {arkparser-0.5.4 → arkparser-0.7.0}/arkparser/structs/colors.py +0 -0
- {arkparser-0.5.4 → arkparser-0.7.0}/arkparser/structs/misc.py +0 -0
- {arkparser-0.5.4 → arkparser-0.7.0}/arkparser/structs/property_list.py +0 -0
- {arkparser-0.5.4 → arkparser-0.7.0}/arkparser/structs/registry.py +0 -0
- {arkparser-0.5.4 → arkparser-0.7.0}/arkparser/structs/vectors.py +0 -0
- {arkparser-0.5.4 → arkparser-0.7.0}/arkparser.egg-info/dependency_links.txt +0 -0
- {arkparser-0.5.4 → arkparser-0.7.0}/arkparser.egg-info/requires.txt +0 -0
- {arkparser-0.5.4 → arkparser-0.7.0}/arkparser.egg-info/top_level.txt +0 -0
- {arkparser-0.5.4 → arkparser-0.7.0}/setup.cfg +0 -0
- {arkparser-0.5.4 → arkparser-0.7.0}/tests/test_asa_header_position.py +0 -0
- {arkparser-0.5.4 → arkparser-0.7.0}/tests/test_asa_name_table.py +0 -0
- {arkparser-0.5.4 → arkparser-0.7.0}/tests/test_ase_cluster_drift.py +0 -0
- {arkparser-0.5.4 → arkparser-0.7.0}/tests/test_binary_reader_layouts.py +0 -0
- {arkparser-0.5.4 → arkparser-0.7.0}/tests/test_cloud_export.py +0 -0
- {arkparser-0.5.4 → arkparser-0.7.0}/tests/test_cloud_inventory.py +0 -0
- {arkparser-0.5.4 → arkparser-0.7.0}/tests/test_current_stats.py +0 -0
- {arkparser-0.5.4 → arkparser-0.7.0}/tests/test_data_models.py +0 -0
- {arkparser-0.5.4 → arkparser-0.7.0}/tests/test_game_objects.py +0 -0
- {arkparser-0.5.4 → arkparser-0.7.0}/tests/test_profile.py +0 -0
- {arkparser-0.5.4 → arkparser-0.7.0}/tests/test_tribe.py +0 -0
- {arkparser-0.5.4 → arkparser-0.7.0}/tests/test_v13_property_layouts.py +0 -0
- {arkparser-0.5.4 → arkparser-0.7.0}/tests/test_version_detection.py +0 -0
- {arkparser-0.5.4 → arkparser-0.7.0}/tests/test_world_save.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: arkparser
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.0
|
|
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
|
|
@@ -58,6 +58,7 @@ A pure-Python library for parsing ARK: Survival Evolved (ASE) and ARK: Survival
|
|
|
58
58
|
- **Dual format**: automatic ASE (v5-12) / ASA (v13-14+, SQLite) detection
|
|
59
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
|
+
- **Memory-bounded**: streaming JSON export plus an opt-in lazy parse mode (`lazy_properties=True`, ASE and ASA) that materializes property blocks on demand and evicts them as records stream to disk; a 1.8 GB / 1.79M-object busy-PvE ASE save exports in 2.5 GB peak RSS instead of 7.1 GB, and a 217k-object ASA save in 307 MB instead of 853 MB (and slightly faster)
|
|
61
62
|
|
|
62
63
|
## Installation
|
|
63
64
|
|
|
@@ -132,6 +133,38 @@ print(f"Parse errors: {save.parse_error_count}")
|
|
|
132
133
|
print(f"Is ASA: {save.is_asa}")
|
|
133
134
|
```
|
|
134
135
|
|
|
136
|
+
### Low-memory parsing (large saves)
|
|
137
|
+
|
|
138
|
+
By default every object's property block is parsed up front, which on a busy
|
|
139
|
+
multi-GB PvE save means a multi-GB resident object graph. `lazy_properties=True`
|
|
140
|
+
parses object headers eagerly but defers each property block until something
|
|
141
|
+
reads it, then the export drivers evict it again once the record has been
|
|
142
|
+
written:
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
from arkparser import WorldSave, export_to_files
|
|
146
|
+
from arkparser.common import get_map_config
|
|
147
|
+
|
|
148
|
+
save = WorldSave.load("path/to/Fjordur.ark", lazy_properties=True) # ASE
|
|
149
|
+
save = WorldSave.load("path/to/TheIsland_WP.ark", lazy_properties=True) # ASA
|
|
150
|
+
export_to_files(save, "output/", get_map_config("fjordur.ark"))
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Output is identical to eager mode (validated record-for-record). ASE retains
|
|
154
|
+
the file reader and re-seeks each object's block on demand; ASA retains the
|
|
155
|
+
SQLite connection (one held read transaction) and re-fetches row blobs by
|
|
156
|
+
GUID. On ASA v14+ saves the export pipeline additionally uses partial decodes:
|
|
157
|
+
a verified byte-exact skip walk that parses only the property names a record
|
|
158
|
+
needs, with any out-of-whitelist read transparently upgrading to the full
|
|
159
|
+
block.
|
|
160
|
+
|
|
161
|
+
Measured load + export, same machine: a 1.8 GB Fjordur PvE ASE save (1.79M
|
|
162
|
+
objects) drops from ~7.1 GB peak RSS / 348 s eager to ~2.5 GB / 287 s lazy; a
|
|
163
|
+
233 MB ASA TheIsland save (217k objects) drops from 853 MB / 38 s eager to
|
|
164
|
+
307 MB / ~36 s lazy. Property access on a lazy save is transparent: any
|
|
165
|
+
`get_property_value` call materializes the block on demand, so all getters and
|
|
166
|
+
exports work unchanged.
|
|
167
|
+
|
|
135
168
|
### Cryopod-stored Creatures
|
|
136
169
|
|
|
137
170
|
Tamed creatures stored inside in-world cryopods aren't returned by `get_tamed_creatures()`. Iterate them via `WorldSave.iter_cryopod_creatures()`:
|
|
@@ -218,7 +251,7 @@ The legacy ASVExport.exe emitted only the visible 8 stats (`hp`, `stam`, `melee`
|
|
|
218
251
|
| `isClone` | legacy | `bIsClone` or `bIsCloneDino` |
|
|
219
252
|
| `tamedServer` | legacy | `TamedOnServerName` |
|
|
220
253
|
| `uploadedServer` | legacy | `UploadedFromServerName` |
|
|
221
|
-
| `maturation` | legacy | `str(int(BabyAge * 100))
|
|
254
|
+
| `maturation` | legacy | `str(int(BabyAge * 100))`, integer maturation percent (a baby with no `BabyAge` is `0`, a newborn). Note: legacy emits the full float string (e.g. `"7.5131035"`); arkparser truncates to the integer percent. Semantically equal and the downstream consumer coerces to `int` either way. |
|
|
222
255
|
| `traits` | legacy | `CreatureTraits` as a list of `{"trait": <class>}` objects (matches `ASVExport`'s shape, not a flat string list) |
|
|
223
256
|
| `inventory` | legacy | items from `MyInventoryComponent.InventoryItems`. Each entry carries `itemId`, `qty`, `blueprint`, plus a full snake_case property dump flattened in at the top level (`id`, `rating`, `durability`, `quality`, `damage`, `armor`, `durability_max`, `hypo`, `hyper`, `clip_size`, `weight`, `crafter`, `crafter_tribe`, `skill_bonus`, `loaded_ammo`, `spoils_at`, `spoiled_at`, `c0`..`c5`, `egg_*`, etc). `item_stat_values` is unpacked into the universal 8-slot ARK map (slot 0 `gen_quality`, 1 `armor`, 2 `durability_max`, 3 `damage`, 4 `clip_size`, 5 `hypo`, 6 `weight`, 7 `hyperthermal_insulation`); raw uint16s scaled by the per-blueprint multiplier (which lives in the UE blueprint, not the save). Default / unset values are filtered (no `craft_queue=0`, `skin=-1`, `color_pre_skin=[0]*6`, NaN spoil timers, etc). When the item is a **cryopod / soultrap / vivarium / dinoball** with an embedded creature, the entry is enriched with `dino_id` (combined 64-bit id matching the corresponding `ASV_Tamed` record), `dino_creature` (species / class name), and `dino_name` (`TamedName` if set). Cryopods stored in containers (cryofridges, vaults, dedicated storage) get the same enrichment. |
|
|
224
257
|
| `father_id`, `mother_id` | added | combined dino id from the first `DinoAncestors` entry (`null` when missing) |
|
|
@@ -281,7 +314,7 @@ ARK keeps player data in **two distinct places**, and the parser builds `ASV_Pla
|
|
|
281
314
|
|
|
282
315
|
The export pipeline (`export_players`) loops over `save.profiles` (a list the caller assembles). For each entry:
|
|
283
316
|
|
|
284
|
-
- If it's a `Profile` instance → `_player_from_profile` runs: fills core identity (name, gender, level, stats, tribe id, engram count, experience, active datetime from `LastLoginTime`).
|
|
317
|
+
- If it's a `Profile` instance → `_player_from_profile` runs: fills core identity (name, gender, level, stats, tribe id, engram count, experience, active datetime from `LastLoginTime`). It joins `PlayerDataID == LinkedPlayerDataID` against the world save's player pawns, so when a live pawn exists `lat`/`lon`/`ccc` come from that pawn's `.location`; players with no live pawn (dead / logged out) keep `lat`/`lon`/`ccc` at zero. `inventory` is empty and pawn-state flags (`is_sleeping`, `is_dead`, `chibi_levels`, …) absent (the profile carries neither).
|
|
285
318
|
- Otherwise it's treated as a wrapped `(profile, objects)` pair pointing at an in-world `PlayerPawn` → `_player_from_object` runs: fills location, inventory, pawn-state flags, body/hair cosmetics, death timestamps, and `active` from `SavedLastTimeHadController`.
|
|
286
319
|
|
|
287
320
|
For the richest output, hand `export_players` **both**, assemble a wrapper for each player that carries the `Profile` (for offline identity) *and* the in-world pawn (when present) so the merged record gets identity + live state. The current validation script only passes `Profile` instances, which is why fields like `is_sleeping` / `body_colors` / `current_weapon` show up empty in the validation output even though the parser supports them.
|
|
@@ -303,7 +336,7 @@ For the richest output, hand `export_players` **both**, assemble a wrapper for e
|
|
|
303
336
|
| `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. |
|
|
304
337
|
| `steamid`, `dataFile` | legacy | platform net id and `{steamid}.arkprofile` filename |
|
|
305
338
|
| `active` | legacy (now populated) | ISO 8601 datetime of last login, converted from profile `LastLoginTime` or in-world pawn `SavedLastTimeHadController`. Legacy schema reserved the field but the old C# exporter only filled it for in-world pawns; the parser fills it for profiles too. `null` when neither source is present. |
|
|
306
|
-
| `lat`, `lon`, `ccc` | legacy (now populated) | In-world position
|
|
339
|
+
| `lat`, `lon`, `ccc` | legacy (now populated) | In-world position. Built directly from a `PlayerPawn` GameObject when one is passed, and for profile-sourced records the parser joins `PlayerDataID == LinkedPlayerDataID` to the world save's player pawns and pulls the matched pawn's `.location`. Records whose player has no live pawn (dead / logged out / cluster-only) keep the legacy `0` / `"0 0 0"` placeholders since there is no world position to report. |
|
|
307
340
|
| `inventory` | legacy (now populated) | Items from the pawn's `MyInventoryComponent` when built from an in-world pawn. Empty list otherwise. |
|
|
308
341
|
| `engram_points` | added | `TotalEngramPoints` |
|
|
309
342
|
| `experience` | added | `ExperiencePoints` (status), integer |
|
|
@@ -322,17 +355,17 @@ For the richest output, hand `export_players` **both**, assemble a wrapper for e
|
|
|
322
355
|
|
|
323
356
|
#### `ASV_Tribes` schema
|
|
324
357
|
|
|
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
|
|
358
|
+
`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
359
|
|
|
327
360
|
| Field | Origin | Source / formula |
|
|
328
361
|
|---|---|---|
|
|
329
362
|
| `tribeid` | legacy | `TribeID` / parser `Tribe.tribe_id`, or a synthesized stub/sentinel id (see above) |
|
|
330
363
|
| `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
|
|
364
|
+
| `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
365
|
| `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=""`. |
|
|
333
366
|
| `tames`, `structures` | legacy | counts derived from `WorldSave` (creatures + structures whose `TargetingTeam` matches) |
|
|
334
367
|
| `uploadedTames` | legacy | reserved (currently `0`) |
|
|
335
|
-
| `active` | legacy | ISO 8601 datetime of the
|
|
368
|
+
| `active` | legacy | ISO 8601 datetime, legacy `ContentTribe.LastActive`: max of the tribe file's write time (`.arktribe` mtime; world-save mtime for in-save tribes) and the allocated members' last-active times (`LastLoginTime` via the save anchor), discarding future values. `null` when no past candidate exists. Tribe-log "Day N" stamps are game-calendar time and are deliberately not used. |
|
|
336
369
|
| `dataFile` | legacy | `"{tribeid}.arktribe"` filename pattern. |
|
|
337
370
|
| `owner_id` | added | `OwnerPlayerDataID` / parser `Tribe.owner_player_id` |
|
|
338
371
|
| `owner_name` | added | `OwnerPlayerName` (object form only; parser-tribe form has no equivalent) |
|
|
@@ -353,7 +386,7 @@ For the richest output, hand `export_players` **both**, assemble a wrapper for e
|
|
|
353
386
|
| `id` | added | `GameObject.id`, internal numeric identifier. Cross-references the values in other records' `linked_structures` / `saddle_structures` / `attached_dino_id` lists. |
|
|
354
387
|
| `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
388
|
| `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
|
|
389
|
+
| `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. |
|
|
357
390
|
| `name` | legacy | `BoxName` (empty when it matches the class name, mirroring legacy's no-rename strip) |
|
|
358
391
|
| `locked` | legacy | `bIsPinLocked` or `bIsLocked` |
|
|
359
392
|
| `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. |
|
|
@@ -412,7 +445,7 @@ data = export_all(save, map_config, cluster="path/to/cluster")
|
|
|
412
445
|
# .arkprofile filename stem.
|
|
413
446
|
```
|
|
414
447
|
|
|
415
|
-
Cluster items are spliced into the owning player's `inventory`; no separate `ASV_ClusterItems` file is emitted. The match works because every cluster file and the player's `.arkprofile` share a stem
|
|
448
|
+
Cluster items are spliced into the owning player's `inventory`; no separate `ASV_ClusterItems` file is emitted. The match works because every cluster file and the player's `.arkprofile` share a stem (the Steam id on ASE, the hex platform UUID on ASA) so the join is keyed on the profile's source filename stem. (`Profile.unique_id` equals that stem only on ASE; on ASA it is the numeric net id, not the UUID filename, so loading profiles from real file paths (which sets `Profile.source_path`) is required for the ASA splice.)
|
|
416
449
|
|
|
417
450
|
Pre-loaded `CloudInventory` instances also work (`cluster=[inv1, inv2, ...]`).
|
|
418
451
|
|
|
@@ -530,7 +563,9 @@ Key methods:
|
|
|
530
563
|
|
|
531
564
|
| Method | Returns | Description |
|
|
532
565
|
|---|---|---|
|
|
533
|
-
| `load(source, load_properties=True, max_objects=None)` | `WorldSave` | Load and parse a world save |
|
|
566
|
+
| `load(source, load_properties=True, max_objects=None, lazy_properties=False)` | `WorldSave` | Load and parse a world save (`lazy_properties`: on-demand property parsing, ASE + ASA, see Quick Start) |
|
|
567
|
+
| `materialize_object(obj, names=None)` | `None` | Parse one lazy object's deferred property block (called automatically on property access; `names` is an ASA v14+ partial-decode hint) |
|
|
568
|
+
| `evict_materialized()` | `int` | Release every property block materialized since the last call (lazy saves; no-op eager) |
|
|
534
569
|
| `get_creatures()` | `list[GameObject]` | All creatures |
|
|
535
570
|
| `get_tamed_creatures()` / `get_wild_creatures()` | `list[GameObject]` | Filtered creature sets |
|
|
536
571
|
| `get_structures()` | `list[GameObject]` | Tribe-owned placed structures |
|
|
@@ -558,7 +593,7 @@ Key methods:
|
|
|
558
593
|
| `properties` | `list[Property]` | Parsed property list |
|
|
559
594
|
| `parent` / `components` | `GameObject \| None` / `dict[str, GameObject]` | Relationships |
|
|
560
595
|
|
|
561
|
-
Lookup helpers: `get_property(name, index=None)`, `get_property_value(name, default=None, index=None)`, `get_properties_by_name(name)`, `has_property(name)`, `to_dict()`.
|
|
596
|
+
Lookup helpers: `get_property(name, index=None)`, `get_property_value(name, default=None, index=None)`, `get_properties_by_name(name)`, `has_property(name)`, `to_dict()`. On lazy saves every helper materializes the deferred property block transparently; `evict_properties()` releases it again (re-access re-parses, so eviction is always safe).
|
|
562
597
|
|
|
563
598
|
#### GameObjectContainer (`arkparser.GameObjectContainer`)
|
|
564
599
|
|
|
@@ -674,6 +709,11 @@ python -m pytest tests/ -v
|
|
|
674
709
|
|
|
675
710
|
Tests live in `tests/`. Byte-level layout tests (`test_v13_property_layouts.py`, `test_binary_reader_layouts.py`) pin the canonical v13/v14 property body byte sequences with no file-fixture dependency. Integration tests (`test_world_save.py`, `test_export.py`, etc.) skip cleanly when their referenced save files are not present under `references/examples/`.
|
|
676
711
|
|
|
712
|
+
Two suites guard export output against drift:
|
|
713
|
+
|
|
714
|
+
- **Golden manifests** (`tests/test_golden_exports.py` + `tests/golden/*.json`): a per-map fingerprint of `export_all` (record count, field-key union, order-independent sha256) for 17 real saves (10 ASE map dumps + 7 ASA). Any change to parsing or export that alters output fails the matching map. Regenerate intentionally with `ARKPARSER_UPDATE_GOLDEN=1`.
|
|
715
|
+
- **Lazy parity** (`tests/test_lazy_properties.py`): proves `lazy_properties=True` yields property-for-property identical objects (ASE and ASA), that evict/re-materialize round-trips, and that full lazy exports match the committed eager goldens. The ASA partial-decode skip walk is additionally verified byte-exact against the full parser over every object of every local ASA fixture by `references/scripts/verify_partial_walk.py`.
|
|
716
|
+
|
|
677
717
|
## Credits
|
|
678
718
|
|
|
679
719
|
Built by reverse-engineering ARK save formats with heavy reference to [ASV (Ark Save Visualizer)](https://github.com/miragedmuk/ASV) by **miragedmuk**. The C# implementation in ASV is the primary reference for porting binary parsing logic to Python.
|
|
@@ -24,6 +24,7 @@ A pure-Python library for parsing ARK: Survival Evolved (ASE) and ARK: Survival
|
|
|
24
24
|
- **Dual format**: automatic ASE (v5-12) / ASA (v13-14+, SQLite) detection
|
|
25
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
|
+
- **Memory-bounded**: streaming JSON export plus an opt-in lazy parse mode (`lazy_properties=True`, ASE and ASA) that materializes property blocks on demand and evicts them as records stream to disk; a 1.8 GB / 1.79M-object busy-PvE ASE save exports in 2.5 GB peak RSS instead of 7.1 GB, and a 217k-object ASA save in 307 MB instead of 853 MB (and slightly faster)
|
|
27
28
|
|
|
28
29
|
## Installation
|
|
29
30
|
|
|
@@ -98,6 +99,38 @@ print(f"Parse errors: {save.parse_error_count}")
|
|
|
98
99
|
print(f"Is ASA: {save.is_asa}")
|
|
99
100
|
```
|
|
100
101
|
|
|
102
|
+
### Low-memory parsing (large saves)
|
|
103
|
+
|
|
104
|
+
By default every object's property block is parsed up front, which on a busy
|
|
105
|
+
multi-GB PvE save means a multi-GB resident object graph. `lazy_properties=True`
|
|
106
|
+
parses object headers eagerly but defers each property block until something
|
|
107
|
+
reads it, then the export drivers evict it again once the record has been
|
|
108
|
+
written:
|
|
109
|
+
|
|
110
|
+
```python
|
|
111
|
+
from arkparser import WorldSave, export_to_files
|
|
112
|
+
from arkparser.common import get_map_config
|
|
113
|
+
|
|
114
|
+
save = WorldSave.load("path/to/Fjordur.ark", lazy_properties=True) # ASE
|
|
115
|
+
save = WorldSave.load("path/to/TheIsland_WP.ark", lazy_properties=True) # ASA
|
|
116
|
+
export_to_files(save, "output/", get_map_config("fjordur.ark"))
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Output is identical to eager mode (validated record-for-record). ASE retains
|
|
120
|
+
the file reader and re-seeks each object's block on demand; ASA retains the
|
|
121
|
+
SQLite connection (one held read transaction) and re-fetches row blobs by
|
|
122
|
+
GUID. On ASA v14+ saves the export pipeline additionally uses partial decodes:
|
|
123
|
+
a verified byte-exact skip walk that parses only the property names a record
|
|
124
|
+
needs, with any out-of-whitelist read transparently upgrading to the full
|
|
125
|
+
block.
|
|
126
|
+
|
|
127
|
+
Measured load + export, same machine: a 1.8 GB Fjordur PvE ASE save (1.79M
|
|
128
|
+
objects) drops from ~7.1 GB peak RSS / 348 s eager to ~2.5 GB / 287 s lazy; a
|
|
129
|
+
233 MB ASA TheIsland save (217k objects) drops from 853 MB / 38 s eager to
|
|
130
|
+
307 MB / ~36 s lazy. Property access on a lazy save is transparent: any
|
|
131
|
+
`get_property_value` call materializes the block on demand, so all getters and
|
|
132
|
+
exports work unchanged.
|
|
133
|
+
|
|
101
134
|
### Cryopod-stored Creatures
|
|
102
135
|
|
|
103
136
|
Tamed creatures stored inside in-world cryopods aren't returned by `get_tamed_creatures()`. Iterate them via `WorldSave.iter_cryopod_creatures()`:
|
|
@@ -184,7 +217,7 @@ The legacy ASVExport.exe emitted only the visible 8 stats (`hp`, `stam`, `melee`
|
|
|
184
217
|
| `isClone` | legacy | `bIsClone` or `bIsCloneDino` |
|
|
185
218
|
| `tamedServer` | legacy | `TamedOnServerName` |
|
|
186
219
|
| `uploadedServer` | legacy | `UploadedFromServerName` |
|
|
187
|
-
| `maturation` | legacy | `str(int(BabyAge * 100))
|
|
220
|
+
| `maturation` | legacy | `str(int(BabyAge * 100))`, integer maturation percent (a baby with no `BabyAge` is `0`, a newborn). Note: legacy emits the full float string (e.g. `"7.5131035"`); arkparser truncates to the integer percent. Semantically equal and the downstream consumer coerces to `int` either way. |
|
|
188
221
|
| `traits` | legacy | `CreatureTraits` as a list of `{"trait": <class>}` objects (matches `ASVExport`'s shape, not a flat string list) |
|
|
189
222
|
| `inventory` | legacy | items from `MyInventoryComponent.InventoryItems`. Each entry carries `itemId`, `qty`, `blueprint`, plus a full snake_case property dump flattened in at the top level (`id`, `rating`, `durability`, `quality`, `damage`, `armor`, `durability_max`, `hypo`, `hyper`, `clip_size`, `weight`, `crafter`, `crafter_tribe`, `skill_bonus`, `loaded_ammo`, `spoils_at`, `spoiled_at`, `c0`..`c5`, `egg_*`, etc). `item_stat_values` is unpacked into the universal 8-slot ARK map (slot 0 `gen_quality`, 1 `armor`, 2 `durability_max`, 3 `damage`, 4 `clip_size`, 5 `hypo`, 6 `weight`, 7 `hyperthermal_insulation`); raw uint16s scaled by the per-blueprint multiplier (which lives in the UE blueprint, not the save). Default / unset values are filtered (no `craft_queue=0`, `skin=-1`, `color_pre_skin=[0]*6`, NaN spoil timers, etc). When the item is a **cryopod / soultrap / vivarium / dinoball** with an embedded creature, the entry is enriched with `dino_id` (combined 64-bit id matching the corresponding `ASV_Tamed` record), `dino_creature` (species / class name), and `dino_name` (`TamedName` if set). Cryopods stored in containers (cryofridges, vaults, dedicated storage) get the same enrichment. |
|
|
190
223
|
| `father_id`, `mother_id` | added | combined dino id from the first `DinoAncestors` entry (`null` when missing) |
|
|
@@ -247,7 +280,7 @@ ARK keeps player data in **two distinct places**, and the parser builds `ASV_Pla
|
|
|
247
280
|
|
|
248
281
|
The export pipeline (`export_players`) loops over `save.profiles` (a list the caller assembles). For each entry:
|
|
249
282
|
|
|
250
|
-
- If it's a `Profile` instance → `_player_from_profile` runs: fills core identity (name, gender, level, stats, tribe id, engram count, experience, active datetime from `LastLoginTime`).
|
|
283
|
+
- If it's a `Profile` instance → `_player_from_profile` runs: fills core identity (name, gender, level, stats, tribe id, engram count, experience, active datetime from `LastLoginTime`). It joins `PlayerDataID == LinkedPlayerDataID` against the world save's player pawns, so when a live pawn exists `lat`/`lon`/`ccc` come from that pawn's `.location`; players with no live pawn (dead / logged out) keep `lat`/`lon`/`ccc` at zero. `inventory` is empty and pawn-state flags (`is_sleeping`, `is_dead`, `chibi_levels`, …) absent (the profile carries neither).
|
|
251
284
|
- Otherwise it's treated as a wrapped `(profile, objects)` pair pointing at an in-world `PlayerPawn` → `_player_from_object` runs: fills location, inventory, pawn-state flags, body/hair cosmetics, death timestamps, and `active` from `SavedLastTimeHadController`.
|
|
252
285
|
|
|
253
286
|
For the richest output, hand `export_players` **both**, assemble a wrapper for each player that carries the `Profile` (for offline identity) *and* the in-world pawn (when present) so the merged record gets identity + live state. The current validation script only passes `Profile` instances, which is why fields like `is_sleeping` / `body_colors` / `current_weapon` show up empty in the validation output even though the parser supports them.
|
|
@@ -269,7 +302,7 @@ For the richest output, hand `export_players` **both**, assemble a wrapper for e
|
|
|
269
302
|
| `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. |
|
|
270
303
|
| `steamid`, `dataFile` | legacy | platform net id and `{steamid}.arkprofile` filename |
|
|
271
304
|
| `active` | legacy (now populated) | ISO 8601 datetime of last login, converted from profile `LastLoginTime` or in-world pawn `SavedLastTimeHadController`. Legacy schema reserved the field but the old C# exporter only filled it for in-world pawns; the parser fills it for profiles too. `null` when neither source is present. |
|
|
272
|
-
| `lat`, `lon`, `ccc` | legacy (now populated) | In-world position
|
|
305
|
+
| `lat`, `lon`, `ccc` | legacy (now populated) | In-world position. Built directly from a `PlayerPawn` GameObject when one is passed, and for profile-sourced records the parser joins `PlayerDataID == LinkedPlayerDataID` to the world save's player pawns and pulls the matched pawn's `.location`. Records whose player has no live pawn (dead / logged out / cluster-only) keep the legacy `0` / `"0 0 0"` placeholders since there is no world position to report. |
|
|
273
306
|
| `inventory` | legacy (now populated) | Items from the pawn's `MyInventoryComponent` when built from an in-world pawn. Empty list otherwise. |
|
|
274
307
|
| `engram_points` | added | `TotalEngramPoints` |
|
|
275
308
|
| `experience` | added | `ExperiencePoints` (status), integer |
|
|
@@ -288,17 +321,17 @@ For the richest output, hand `export_players` **both**, assemble a wrapper for e
|
|
|
288
321
|
|
|
289
322
|
#### `ASV_Tribes` schema
|
|
290
323
|
|
|
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
|
|
324
|
+
`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
325
|
|
|
293
326
|
| Field | Origin | Source / formula |
|
|
294
327
|
|---|---|---|
|
|
295
328
|
| `tribeid` | legacy | `TribeID` / parser `Tribe.tribe_id`, or a synthesized stub/sentinel id (see above) |
|
|
296
329
|
| `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
|
|
330
|
+
| `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
331
|
| `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=""`. |
|
|
299
332
|
| `tames`, `structures` | legacy | counts derived from `WorldSave` (creatures + structures whose `TargetingTeam` matches) |
|
|
300
333
|
| `uploadedTames` | legacy | reserved (currently `0`) |
|
|
301
|
-
| `active` | legacy | ISO 8601 datetime of the
|
|
334
|
+
| `active` | legacy | ISO 8601 datetime, legacy `ContentTribe.LastActive`: max of the tribe file's write time (`.arktribe` mtime; world-save mtime for in-save tribes) and the allocated members' last-active times (`LastLoginTime` via the save anchor), discarding future values. `null` when no past candidate exists. Tribe-log "Day N" stamps are game-calendar time and are deliberately not used. |
|
|
302
335
|
| `dataFile` | legacy | `"{tribeid}.arktribe"` filename pattern. |
|
|
303
336
|
| `owner_id` | added | `OwnerPlayerDataID` / parser `Tribe.owner_player_id` |
|
|
304
337
|
| `owner_name` | added | `OwnerPlayerName` (object form only; parser-tribe form has no equivalent) |
|
|
@@ -319,7 +352,7 @@ For the richest output, hand `export_players` **both**, assemble a wrapper for e
|
|
|
319
352
|
| `id` | added | `GameObject.id`, internal numeric identifier. Cross-references the values in other records' `linked_structures` / `saddle_structures` / `attached_dino_id` lists. |
|
|
320
353
|
| `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
354
|
| `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
|
|
355
|
+
| `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. |
|
|
323
356
|
| `name` | legacy | `BoxName` (empty when it matches the class name, mirroring legacy's no-rename strip) |
|
|
324
357
|
| `locked` | legacy | `bIsPinLocked` or `bIsLocked` |
|
|
325
358
|
| `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. |
|
|
@@ -378,7 +411,7 @@ data = export_all(save, map_config, cluster="path/to/cluster")
|
|
|
378
411
|
# .arkprofile filename stem.
|
|
379
412
|
```
|
|
380
413
|
|
|
381
|
-
Cluster items are spliced into the owning player's `inventory`; no separate `ASV_ClusterItems` file is emitted. The match works because every cluster file and the player's `.arkprofile` share a stem
|
|
414
|
+
Cluster items are spliced into the owning player's `inventory`; no separate `ASV_ClusterItems` file is emitted. The match works because every cluster file and the player's `.arkprofile` share a stem (the Steam id on ASE, the hex platform UUID on ASA) so the join is keyed on the profile's source filename stem. (`Profile.unique_id` equals that stem only on ASE; on ASA it is the numeric net id, not the UUID filename, so loading profiles from real file paths (which sets `Profile.source_path`) is required for the ASA splice.)
|
|
382
415
|
|
|
383
416
|
Pre-loaded `CloudInventory` instances also work (`cluster=[inv1, inv2, ...]`).
|
|
384
417
|
|
|
@@ -496,7 +529,9 @@ Key methods:
|
|
|
496
529
|
|
|
497
530
|
| Method | Returns | Description |
|
|
498
531
|
|---|---|---|
|
|
499
|
-
| `load(source, load_properties=True, max_objects=None)` | `WorldSave` | Load and parse a world save |
|
|
532
|
+
| `load(source, load_properties=True, max_objects=None, lazy_properties=False)` | `WorldSave` | Load and parse a world save (`lazy_properties`: on-demand property parsing, ASE + ASA, see Quick Start) |
|
|
533
|
+
| `materialize_object(obj, names=None)` | `None` | Parse one lazy object's deferred property block (called automatically on property access; `names` is an ASA v14+ partial-decode hint) |
|
|
534
|
+
| `evict_materialized()` | `int` | Release every property block materialized since the last call (lazy saves; no-op eager) |
|
|
500
535
|
| `get_creatures()` | `list[GameObject]` | All creatures |
|
|
501
536
|
| `get_tamed_creatures()` / `get_wild_creatures()` | `list[GameObject]` | Filtered creature sets |
|
|
502
537
|
| `get_structures()` | `list[GameObject]` | Tribe-owned placed structures |
|
|
@@ -524,7 +559,7 @@ Key methods:
|
|
|
524
559
|
| `properties` | `list[Property]` | Parsed property list |
|
|
525
560
|
| `parent` / `components` | `GameObject \| None` / `dict[str, GameObject]` | Relationships |
|
|
526
561
|
|
|
527
|
-
Lookup helpers: `get_property(name, index=None)`, `get_property_value(name, default=None, index=None)`, `get_properties_by_name(name)`, `has_property(name)`, `to_dict()`.
|
|
562
|
+
Lookup helpers: `get_property(name, index=None)`, `get_property_value(name, default=None, index=None)`, `get_properties_by_name(name)`, `has_property(name)`, `to_dict()`. On lazy saves every helper materializes the deferred property block transparently; `evict_properties()` releases it again (re-access re-parses, so eviction is always safe).
|
|
528
563
|
|
|
529
564
|
#### GameObjectContainer (`arkparser.GameObjectContainer`)
|
|
530
565
|
|
|
@@ -640,6 +675,11 @@ python -m pytest tests/ -v
|
|
|
640
675
|
|
|
641
676
|
Tests live in `tests/`. Byte-level layout tests (`test_v13_property_layouts.py`, `test_binary_reader_layouts.py`) pin the canonical v13/v14 property body byte sequences with no file-fixture dependency. Integration tests (`test_world_save.py`, `test_export.py`, etc.) skip cleanly when their referenced save files are not present under `references/examples/`.
|
|
642
677
|
|
|
678
|
+
Two suites guard export output against drift:
|
|
679
|
+
|
|
680
|
+
- **Golden manifests** (`tests/test_golden_exports.py` + `tests/golden/*.json`): a per-map fingerprint of `export_all` (record count, field-key union, order-independent sha256) for 17 real saves (10 ASE map dumps + 7 ASA). Any change to parsing or export that alters output fails the matching map. Regenerate intentionally with `ARKPARSER_UPDATE_GOLDEN=1`.
|
|
681
|
+
- **Lazy parity** (`tests/test_lazy_properties.py`): proves `lazy_properties=True` yields property-for-property identical objects (ASE and ASA), that evict/re-materialize round-trips, and that full lazy exports match the committed eager goldens. The ASA partial-decode skip walk is additionally verified byte-exact against the full parser over every object of every local ASA fixture by `references/scripts/verify_partial_walk.py`.
|
|
682
|
+
|
|
643
683
|
## Credits
|
|
644
684
|
|
|
645
685
|
Built by reverse-engineering ARK save formats with heavy reference to [ASV (Ark Save Visualizer)](https://github.com/miragedmuk/ASV) by **miragedmuk**. The C# implementation in ASV is the primary reference for porting binary parsing logic to Python.
|
|
@@ -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.
|
|
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.7.0"
|