arkparser 0.1.3__tar.gz → 0.1.4__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.1.3 → arkparser-0.1.4}/PKG-INFO +7 -4
  2. {arkparser-0.1.3 → arkparser-0.1.4}/README.md +6 -3
  3. {arkparser-0.1.3 → arkparser-0.1.4}/arkparser/__init__.py +2 -1
  4. {arkparser-0.1.3 → arkparser-0.1.4}/arkparser/export.py +13 -3
  5. {arkparser-0.1.3 → arkparser-0.1.4}/arkparser/files/profile.py +50 -3
  6. {arkparser-0.1.3 → arkparser-0.1.4}/arkparser.egg-info/PKG-INFO +7 -4
  7. {arkparser-0.1.3 → arkparser-0.1.4}/pyproject.toml +1 -1
  8. {arkparser-0.1.3 → arkparser-0.1.4}/tests/test_export.py +3 -1
  9. {arkparser-0.1.3 → arkparser-0.1.4}/tests/test_profile.py +25 -0
  10. {arkparser-0.1.3 → arkparser-0.1.4}/LICENSE +0 -0
  11. {arkparser-0.1.3 → arkparser-0.1.4}/arkparser/common/__init__.py +0 -0
  12. {arkparser-0.1.3 → arkparser-0.1.4}/arkparser/common/binary_reader.py +0 -0
  13. {arkparser-0.1.3 → arkparser-0.1.4}/arkparser/common/exceptions.py +0 -0
  14. {arkparser-0.1.3 → arkparser-0.1.4}/arkparser/common/map_config.py +0 -0
  15. {arkparser-0.1.3 → arkparser-0.1.4}/arkparser/common/normalization.py +0 -0
  16. {arkparser-0.1.3 → arkparser-0.1.4}/arkparser/common/types.py +0 -0
  17. {arkparser-0.1.3 → arkparser-0.1.4}/arkparser/common/version_detection.py +0 -0
  18. {arkparser-0.1.3 → arkparser-0.1.4}/arkparser/data_models.py +0 -0
  19. {arkparser-0.1.3 → arkparser-0.1.4}/arkparser/files/__init__.py +0 -0
  20. {arkparser-0.1.3 → arkparser-0.1.4}/arkparser/files/base.py +0 -0
  21. {arkparser-0.1.3 → arkparser-0.1.4}/arkparser/files/cloud_inventory.py +0 -0
  22. {arkparser-0.1.3 → arkparser-0.1.4}/arkparser/files/tribe.py +0 -0
  23. {arkparser-0.1.3 → arkparser-0.1.4}/arkparser/files/world_save.py +0 -0
  24. {arkparser-0.1.3 → arkparser-0.1.4}/arkparser/game_objects/__init__.py +0 -0
  25. {arkparser-0.1.3 → arkparser-0.1.4}/arkparser/game_objects/container.py +0 -0
  26. {arkparser-0.1.3 → arkparser-0.1.4}/arkparser/game_objects/game_object.py +0 -0
  27. {arkparser-0.1.3 → arkparser-0.1.4}/arkparser/game_objects/location.py +0 -0
  28. {arkparser-0.1.3 → arkparser-0.1.4}/arkparser/models/__init__.py +0 -0
  29. {arkparser-0.1.3 → arkparser-0.1.4}/arkparser/models/character.py +0 -0
  30. {arkparser-0.1.3 → arkparser-0.1.4}/arkparser/models/creature.py +0 -0
  31. {arkparser-0.1.3 → arkparser-0.1.4}/arkparser/models/item.py +0 -0
  32. {arkparser-0.1.3 → arkparser-0.1.4}/arkparser/models/player.py +0 -0
  33. {arkparser-0.1.3 → arkparser-0.1.4}/arkparser/models/stats.py +0 -0
  34. {arkparser-0.1.3 → arkparser-0.1.4}/arkparser/models/structure.py +0 -0
  35. {arkparser-0.1.3 → arkparser-0.1.4}/arkparser/models/tribe.py +0 -0
  36. {arkparser-0.1.3 → arkparser-0.1.4}/arkparser/properties/__init__.py +0 -0
  37. {arkparser-0.1.3 → arkparser-0.1.4}/arkparser/properties/base.py +0 -0
  38. {arkparser-0.1.3 → arkparser-0.1.4}/arkparser/properties/byte_property.py +0 -0
  39. {arkparser-0.1.3 → arkparser-0.1.4}/arkparser/properties/compound.py +0 -0
  40. {arkparser-0.1.3 → arkparser-0.1.4}/arkparser/properties/primitives.py +0 -0
  41. {arkparser-0.1.3 → arkparser-0.1.4}/arkparser/properties/registry.py +0 -0
  42. {arkparser-0.1.3 → arkparser-0.1.4}/arkparser/structs/__init__.py +0 -0
  43. {arkparser-0.1.3 → arkparser-0.1.4}/arkparser/structs/base.py +0 -0
  44. {arkparser-0.1.3 → arkparser-0.1.4}/arkparser/structs/colors.py +0 -0
  45. {arkparser-0.1.3 → arkparser-0.1.4}/arkparser/structs/misc.py +0 -0
  46. {arkparser-0.1.3 → arkparser-0.1.4}/arkparser/structs/property_list.py +0 -0
  47. {arkparser-0.1.3 → arkparser-0.1.4}/arkparser/structs/registry.py +0 -0
  48. {arkparser-0.1.3 → arkparser-0.1.4}/arkparser/structs/vectors.py +0 -0
  49. {arkparser-0.1.3 → arkparser-0.1.4}/arkparser.egg-info/SOURCES.txt +0 -0
  50. {arkparser-0.1.3 → arkparser-0.1.4}/arkparser.egg-info/dependency_links.txt +0 -0
  51. {arkparser-0.1.3 → arkparser-0.1.4}/arkparser.egg-info/requires.txt +0 -0
  52. {arkparser-0.1.3 → arkparser-0.1.4}/arkparser.egg-info/top_level.txt +0 -0
  53. {arkparser-0.1.3 → arkparser-0.1.4}/setup.cfg +0 -0
  54. {arkparser-0.1.3 → arkparser-0.1.4}/tests/test_binary_reader.py +0 -0
  55. {arkparser-0.1.3 → arkparser-0.1.4}/tests/test_cloud_inventory.py +0 -0
  56. {arkparser-0.1.3 → arkparser-0.1.4}/tests/test_data_models.py +0 -0
  57. {arkparser-0.1.3 → arkparser-0.1.4}/tests/test_game_objects.py +0 -0
  58. {arkparser-0.1.3 → arkparser-0.1.4}/tests/test_models.py +0 -0
  59. {arkparser-0.1.3 → arkparser-0.1.4}/tests/test_tribe.py +0 -0
  60. {arkparser-0.1.3 → arkparser-0.1.4}/tests/test_version_detection.py +0 -0
  61. {arkparser-0.1.3 → arkparser-0.1.4}/tests/test_world_save.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: arkparser
3
- Version: 0.1.3
3
+ Version: 0.1.4
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
@@ -97,7 +97,7 @@ A pure-Python library for parsing ARK: Survival Evolved (ASE) and ARK: Survival
97
97
 
98
98
  ## Features
99
99
 
100
- - **Player Profiles** (`.arkprofile`): character name, level, stats, engrams
100
+ - **Player Profiles** (`.arkprofile`): platform gamertag, character name, level, stats, engrams
101
101
  - **Tribe Data** (`.arktribe`): members, ranks, logs, alliances
102
102
  - **Cloud Inventory / Obelisk**: uploaded creatures, items, cryopod contents
103
103
  - **World Saves** (`.ark`): full map state, including creatures, structures, items, and players
@@ -122,7 +122,8 @@ from arkparser import Profile
122
122
 
123
123
  profile = Profile.load("path/to/player.arkprofile") # auto-detects ASE/ASA
124
124
 
125
- print(profile.player_name) # "SomePlayer"
125
+ print(profile.player_name) # Platform gamertag / display name
126
+ print(profile.character_name) # In-game character name
126
127
  print(profile.level) # 105
127
128
  print(profile.tribe_id) # 1729028872
128
129
  print(profile.engram_blueprints) # ["EngramEntry_Campfire_C", ...]
@@ -258,11 +259,13 @@ All file parsers support `load(source)` which accepts `str`, `Path`, or `bytes`
258
259
 
259
260
  | Property | Type | Description |
260
261
  |---|---|---|
261
- | `player_name` | `str \| None` | Character name |
262
+ | `player_name` | `str \| None` | Platform gamertag / display name |
263
+ | `character_name` | `str \| None` | In-game character name, falling back to `player_name` when absent |
262
264
  | `player_id` | `int \| None` | Unique player ID |
263
265
  | `unique_id` | `str \| None` | Platform ID (Steam/Xbox numeric ID) |
264
266
  | `tribe_id` | `int \| None` | Tribe ID (handles ASE `TribeId` / ASA `TribeID`) |
265
267
  | `tribe_name` | `str \| None` | Always `None`; tribe name is not stored in profiles |
268
+ | `is_female` | `bool \| None` | Parsed gender flag (`True` = female, `False` = male, `None` = absent) |
266
269
  | `level` | `int` | Current level (`ExtraCharacterLevel + 1`) |
267
270
  | `experience` | `float` | Total XP |
268
271
  | `total_engram_points` | `int` | Engram points spent |
@@ -63,7 +63,7 @@ A pure-Python library for parsing ARK: Survival Evolved (ASE) and ARK: Survival
63
63
 
64
64
  ## Features
65
65
 
66
- - **Player Profiles** (`.arkprofile`): character name, level, stats, engrams
66
+ - **Player Profiles** (`.arkprofile`): platform gamertag, character name, level, stats, engrams
67
67
  - **Tribe Data** (`.arktribe`): members, ranks, logs, alliances
68
68
  - **Cloud Inventory / Obelisk**: uploaded creatures, items, cryopod contents
69
69
  - **World Saves** (`.ark`): full map state, including creatures, structures, items, and players
@@ -88,7 +88,8 @@ from arkparser import Profile
88
88
 
89
89
  profile = Profile.load("path/to/player.arkprofile") # auto-detects ASE/ASA
90
90
 
91
- print(profile.player_name) # "SomePlayer"
91
+ print(profile.player_name) # Platform gamertag / display name
92
+ print(profile.character_name) # In-game character name
92
93
  print(profile.level) # 105
93
94
  print(profile.tribe_id) # 1729028872
94
95
  print(profile.engram_blueprints) # ["EngramEntry_Campfire_C", ...]
@@ -224,11 +225,13 @@ All file parsers support `load(source)` which accepts `str`, `Path`, or `bytes`
224
225
 
225
226
  | Property | Type | Description |
226
227
  |---|---|---|
227
- | `player_name` | `str \| None` | Character name |
228
+ | `player_name` | `str \| None` | Platform gamertag / display name |
229
+ | `character_name` | `str \| None` | In-game character name, falling back to `player_name` when absent |
228
230
  | `player_id` | `int \| None` | Unique player ID |
229
231
  | `unique_id` | `str \| None` | Platform ID (Steam/Xbox numeric ID) |
230
232
  | `tribe_id` | `int \| None` | Tribe ID (handles ASE `TribeId` / ASA `TribeID`) |
231
233
  | `tribe_name` | `str \| None` | Always `None`; tribe name is not stored in profiles |
234
+ | `is_female` | `bool \| None` | Parsed gender flag (`True` = female, `False` = male, `None` = absent) |
232
235
  | `level` | `int` | Current level (`ExtraCharacterLevel + 1`) |
233
236
  | `experience` | `float` | Total XP |
234
237
  | `total_engram_points` | `int` | Engram points spent |
@@ -16,7 +16,8 @@ Example usage:
16
16
  >>>
17
17
  >>> # Load a player profile
18
18
  >>> profile = Profile.load("path/to/profile.arkprofile")
19
- >>> print(f"Player: {profile.player_name}")
19
+ >>> print(f"Gamertag: {profile.player_name}")
20
+ >>> print(f"Character: {profile.character_name}")
20
21
  >>>
21
22
  >>> # Load tribe data
22
23
  >>> tribe = Tribe.load("path/to/tribe.arktribe")
@@ -521,13 +521,23 @@ def _export_profile_parser(profile: Profile) -> dict[str, t.Any]:
521
521
  }
522
522
  steam_id = profile.unique_id or ""
523
523
 
524
+ # The C# reference treats these as two distinct names:
525
+ # Name (ContentPlayer.cs:77) -> platform gamertag (PlayerName field)
526
+ # CharacterName (ContentPlayer.cs:86) -> in-game name (PlayerCharacterName field)
527
+ # Export contract mirrors that split: ``steam`` = gamertag, ``name`` =
528
+ # in-game character name. Falls back to the gamertag if the character
529
+ # config struct is absent (matches the C# `?? Name` fallback).
530
+ gamertag = profile.player_name or ""
531
+ character_name = profile.character_name or gamertag
532
+ is_female = profile.is_female
533
+
524
534
  result: dict[str, t.Any] = {
525
535
  "playerid": profile.player_id or 0,
526
- "steam": "",
527
- "name": profile.player_name or "",
536
+ "steam": gamertag,
537
+ "name": character_name,
528
538
  "tribeid": profile.tribe_id or 0,
529
539
  "tribe": profile.tribe_name or "",
530
- "sex": "",
540
+ "sex": "Female" if is_female is True else "Male",
531
541
  "lvl": profile.level,
532
542
  "hp": stats["health"],
533
543
  "stam": stats["stamina"],
@@ -2,7 +2,7 @@
2
2
  Player profile parser for .arkprofile files.
3
3
 
4
4
  Profile files contain player character data including:
5
- - Character name and stats
5
+ - Platform gamertag and character name
6
6
  - Level and experience
7
7
  - Engrams learned
8
8
  - Inventory items (if saved)
@@ -28,7 +28,8 @@ class Profile(ArkFile):
28
28
 
29
29
  Example usage:
30
30
  >>> profile = Profile.load("examples/ase/map_save/2533274977850953.arkprofile")
31
- >>> print(f"Player: {profile.player_name}")
31
+ >>> print(f"Gamertag: {profile.player_name}")
32
+ >>> print(f"Character: {profile.character_name}")
32
33
  >>> print(f"Level: {profile.level}")
33
34
  """
34
35
 
@@ -70,9 +71,53 @@ class Profile(ArkFile):
70
71
 
71
72
  @property
72
73
  def player_name(self) -> str | None:
73
- """Get the player's character name."""
74
+ """Get the player's platform gamertag (Steam / Xbox / PSN display name).
75
+
76
+ Note: despite the historical name, this is NOT the in-game character
77
+ name. For that, use ``character_name``. The C# reference (ContentPlayer.cs
78
+ line 77) reads this same ``PlayerName`` field into its ``Name`` property.
79
+ """
74
80
  return self._player_data.get("PlayerName")
75
81
 
82
+ @property
83
+ def character_name(self) -> str | None:
84
+ """Get the player's in-game character name.
85
+
86
+ Purpose: returns the name the player chose when creating their
87
+ character (e.g. "Alex"), distinct from the platform gamertag (e.g.
88
+ "Itz0Alex") which ``player_name`` returns.
89
+ Preconditions: profile file is loaded; ``_player_data`` is accessible.
90
+ Postconditions: returns ``MyPlayerCharacterConfig.PlayerCharacterName``
91
+ when present, including empty-string values, falling back to
92
+ ``player_name`` (gamertag) only when the config field is absent -
93
+ matches the C# reference behavior
94
+ (ContentPlayer.cs line 86: ``CharacterName = playerConfig.GetPropertyValue<string>("PlayerCharacterName") ?? Name;``).
95
+ Side effects: none.
96
+ Failure modes: returns ``None`` only when both the config struct and
97
+ ``PlayerName`` are missing.
98
+ """
99
+ config = self._player_data.get("MyPlayerCharacterConfig")
100
+ if isinstance(config, dict):
101
+ character_name = config.get("PlayerCharacterName")
102
+ if character_name is not None:
103
+ return character_name
104
+ return self.player_name
105
+
106
+ @property
107
+ def is_female(self) -> bool | None:
108
+ """Get the player's character gender (True = female, False = male, None = unknown).
109
+
110
+ Same nested location as ``character_name``: read from
111
+ ``MyPlayerCharacterConfig.bIsFemale``. Returns None when the config
112
+ struct is absent (treat as gender unknown / default male in display code).
113
+ """
114
+ config = self._player_data.get("MyPlayerCharacterConfig")
115
+ if isinstance(config, dict):
116
+ value = config.get("bIsFemale")
117
+ if value is not None:
118
+ return bool(value)
119
+ return None
120
+
76
121
  @property
77
122
  def player_id(self) -> int | None:
78
123
  """Get the player's unique ID."""
@@ -202,10 +247,12 @@ class Profile(ArkFile):
202
247
  base_dict.update(
203
248
  {
204
249
  "player_name": self.player_name,
250
+ "character_name": self.character_name,
205
251
  "player_id": self.player_id,
206
252
  "unique_id": self.unique_id,
207
253
  "tribe_id": self.tribe_id,
208
254
  "tribe_name": self.tribe_name,
255
+ "is_female": self.is_female,
209
256
  "experience": self.experience,
210
257
  "total_engram_points": self.total_engram_points,
211
258
  }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: arkparser
3
- Version: 0.1.3
3
+ Version: 0.1.4
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
@@ -97,7 +97,7 @@ A pure-Python library for parsing ARK: Survival Evolved (ASE) and ARK: Survival
97
97
 
98
98
  ## Features
99
99
 
100
- - **Player Profiles** (`.arkprofile`): character name, level, stats, engrams
100
+ - **Player Profiles** (`.arkprofile`): platform gamertag, character name, level, stats, engrams
101
101
  - **Tribe Data** (`.arktribe`): members, ranks, logs, alliances
102
102
  - **Cloud Inventory / Obelisk**: uploaded creatures, items, cryopod contents
103
103
  - **World Saves** (`.ark`): full map state, including creatures, structures, items, and players
@@ -122,7 +122,8 @@ from arkparser import Profile
122
122
 
123
123
  profile = Profile.load("path/to/player.arkprofile") # auto-detects ASE/ASA
124
124
 
125
- print(profile.player_name) # "SomePlayer"
125
+ print(profile.player_name) # Platform gamertag / display name
126
+ print(profile.character_name) # In-game character name
126
127
  print(profile.level) # 105
127
128
  print(profile.tribe_id) # 1729028872
128
129
  print(profile.engram_blueprints) # ["EngramEntry_Campfire_C", ...]
@@ -258,11 +259,13 @@ All file parsers support `load(source)` which accepts `str`, `Path`, or `bytes`
258
259
 
259
260
  | Property | Type | Description |
260
261
  |---|---|---|
261
- | `player_name` | `str \| None` | Character name |
262
+ | `player_name` | `str \| None` | Platform gamertag / display name |
263
+ | `character_name` | `str \| None` | In-game character name, falling back to `player_name` when absent |
262
264
  | `player_id` | `int \| None` | Unique player ID |
263
265
  | `unique_id` | `str \| None` | Platform ID (Steam/Xbox numeric ID) |
264
266
  | `tribe_id` | `int \| None` | Tribe ID (handles ASE `TribeId` / ASA `TribeID`) |
265
267
  | `tribe_name` | `str \| None` | Always `None`; tribe name is not stored in profiles |
268
+ | `is_female` | `bool \| None` | Parsed gender flag (`True` = female, `False` = male, `None` = absent) |
266
269
  | `level` | `int` | Current level (`ExtraCharacterLevel + 1`) |
267
270
  | `experience` | `float` | Total XP |
268
271
  | `total_engram_points` | `int` | Engram points spent |
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "arkparser"
7
- version = "0.1.3"
7
+ version = "0.1.4"
8
8
  description = "Pure Python parser for ARK: Survival Evolved and ARK: Survival Ascended save files"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -46,7 +46,9 @@ def test_export_players_uses_profile_parser(ase_profile_path: Path) -> None:
46
46
  profile = Profile.load(ase_profile_path)
47
47
  exported = export_players(type("Holder", (), {"profiles": [profile]})())
48
48
  assert exported[0]["playerid"] == profile.player_id
49
- assert exported[0]["name"] == profile.player_name
49
+ assert exported[0]["steam"] == profile.player_name
50
+ assert exported[0]["name"] == profile.character_name
51
+ assert exported[0]["sex"] == ("Female" if profile.is_female else "Male")
50
52
  assert exported[0]["tribeid"] == profile.tribe_id
51
53
 
52
54
 
@@ -29,6 +29,17 @@ class TestASEProfile:
29
29
  assert isinstance(profile.player_name, str)
30
30
  assert len(profile.player_name) > 0
31
31
 
32
+ def test_ase_character_name(self, ase_profile_path: Path) -> None:
33
+ """ASE profile should expose the in-game character name separately."""
34
+ profile = Profile.load(ase_profile_path)
35
+ assert profile.character_name == "neg"
36
+ assert profile.character_name != profile.player_name
37
+
38
+ def test_ase_is_female(self, ase_profile_path: Path) -> None:
39
+ """ASE profile should expose the parsed gender flag when present."""
40
+ profile = Profile.load(ase_profile_path)
41
+ assert profile.is_female is True
42
+
32
43
  def test_ase_player_id(self, ase_profile_path: Path) -> None:
33
44
  """ASE profile should have a numeric player ID."""
34
45
  profile = Profile.load(ase_profile_path)
@@ -47,6 +58,8 @@ class TestASEProfile:
47
58
  d = profile.to_dict()
48
59
  assert isinstance(d, dict)
49
60
  assert "player_name" in d
61
+ assert d["character_name"] == profile.character_name
62
+ assert d["is_female"] is True
50
63
  assert "player_id" in d
51
64
 
52
65
 
@@ -70,6 +83,16 @@ class TestASAProfile:
70
83
  assert isinstance(profile.player_name, str)
71
84
  assert len(profile.player_name) > 0
72
85
 
86
+ def test_asa_character_name_falls_back_to_player_name(self, asa_profile_path: Path) -> None:
87
+ """ASA profile should fall back to the platform name when character config is absent."""
88
+ profile = Profile.load(asa_profile_path)
89
+ assert profile.character_name == profile.player_name
90
+
91
+ def test_asa_is_female_can_be_unknown(self, asa_profile_path: Path) -> None:
92
+ """ASA profile should return None when the gender flag is absent."""
93
+ profile = Profile.load(asa_profile_path)
94
+ assert profile.is_female is None
95
+
73
96
  def test_asa_player_id(self, asa_profile_path: Path) -> None:
74
97
  """ASA profile should have a numeric player ID."""
75
98
  profile = Profile.load(asa_profile_path)
@@ -88,6 +111,8 @@ class TestASAProfile:
88
111
  d = profile.to_dict()
89
112
  assert isinstance(d, dict)
90
113
  assert "player_name" in d
114
+ assert d["character_name"] == profile.character_name
115
+ assert d["is_female"] is None
91
116
  assert "player_id" in d
92
117
 
93
118
  def test_asa_engram_blueprints(self, asa_profile_path: Path) -> None:
File without changes
File without changes
File without changes