synth-ai 0.2.13.dev1__py3-none-any.whl → 0.2.13.dev2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of synth-ai might be problematic. Click here for more details.

Files changed (226) hide show
  1. examples/multi_step/configs/crafter_rl_stepwise_hosted_judge.toml +12 -1
  2. examples/swe/task_app/grpo_swe_mini.py +55 -26
  3. examples/swe/task_app/hosted/rollout.py +40 -0
  4. examples/swe/task_app/hosted/test_service.py +5 -6
  5. examples/task_apps/TESTING.md +275 -0
  6. examples/task_apps/__init__.py +0 -0
  7. examples/task_apps/crafter/__init__.py +0 -0
  8. examples/task_apps/crafter/task_app/__init__.py +2 -0
  9. examples/{warming_up_to_rl → task_apps/crafter}/task_app/grpo_crafter.py +18 -13
  10. examples/{warming_up_to_rl → task_apps/crafter}/task_app/grpo_crafter_task_app.py +1 -1
  11. examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/envs/crafter/policy.py +60 -4
  12. examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/policy_routes.py +25 -3
  13. examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/rollout.py +10 -0
  14. examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/test_service.py +5 -6
  15. examples/task_apps/dev/pokemon_emerald/__init__.py +2 -0
  16. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/README.md +811 -0
  17. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/agent/__init__.py +120 -0
  18. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/agent/action.py +160 -0
  19. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/agent/memory.py +155 -0
  20. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/agent/perception.py +69 -0
  21. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/agent/planning.py +96 -0
  22. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/agent/simple.py +1502 -0
  23. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/agent/system_prompt.py +4 -0
  24. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/grab_map.py +68 -0
  25. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/manual.py +216 -0
  26. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pokemon_env/__init__.py +35 -0
  27. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pokemon_env/emerald_utils.py +631 -0
  28. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pokemon_env/emulator.py +1544 -0
  29. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pokemon_env/enums.py +1428 -0
  30. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pokemon_env/memory_reader.py +4848 -0
  31. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pokemon_env/types.py +41 -0
  32. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pokemon_env/utils.py +298 -0
  33. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pyproject.toml +95 -0
  34. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/run.py +204 -0
  35. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/server/__init__.py +0 -0
  36. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/server/app.py +2152 -0
  37. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/server/client.py +429 -0
  38. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/server/frame_server.py +155 -0
  39. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/README.md +78 -0
  40. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/__init__.py +0 -0
  41. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/run_tests.py +122 -0
  42. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_agent_direct.py +76 -0
  43. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_agent_prompts.py +413 -0
  44. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_battle_state_formatting.py +204 -0
  45. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_dialogue_detection.py +133 -0
  46. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_dialogue_detection_comprehensive.py +229 -0
  47. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_direct_agent_emulator.py +300 -0
  48. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_fps_adjustment_pytest.py +205 -0
  49. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_house_to_outside_direct.py +200 -0
  50. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_house_to_outside_transition.py +284 -0
  51. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_map_ground_truth_comparison.py +468 -0
  52. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_memory_map.py +575 -0
  53. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_server_map_validation.py +311 -0
  54. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_torchic_state.py +259 -0
  55. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/__init__.py +0 -0
  56. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/anticheat.py +372 -0
  57. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/checkpoint.py +296 -0
  58. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/error_handler.py +275 -0
  59. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/get_local_ip.py +22 -0
  60. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/helpers.py +44 -0
  61. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/llm_logger.py +514 -0
  62. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/map_formatter.py +415 -0
  63. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/map_stitcher.py +1763 -0
  64. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/map_stitcher_singleton.py +33 -0
  65. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/map_trimmer.py +106 -0
  66. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/map_visualizer.py +334 -0
  67. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/ocr_dialogue.py +1020 -0
  68. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/recording.py +188 -0
  69. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/state_formatter.py +1481 -0
  70. examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/vlm.py +862 -0
  71. examples/task_apps/dev/pokemon_emerald/modal_app.py +114 -0
  72. examples/task_apps/dev/pokemon_emerald/task_app/README.md +81 -0
  73. examples/task_apps/dev/pokemon_emerald/task_app/__init__.py +6 -0
  74. examples/task_apps/dev/pokemon_emerald/task_app/pokemon_emerald.py +685 -0
  75. examples/task_apps/enron/__init__.py +1 -0
  76. examples/task_apps/enron/eval_groq_qwen32.toml +16 -0
  77. examples/task_apps/enron/task_app/README.md +14 -0
  78. examples/task_apps/enron/task_app/__init__.py +1 -0
  79. examples/task_apps/enron/task_app/grpo_enron.py +906 -0
  80. examples/task_apps/enron/task_app/grpo_enron_task_app.py +146 -0
  81. examples/task_apps/enron/tests/__init__.py +2 -0
  82. examples/task_apps/enron/tests/conftest.py +115 -0
  83. examples/task_apps/enron/tests/integration/__init__.py +2 -0
  84. examples/task_apps/enron/tests/integration/test_enron_eval.py +177 -0
  85. examples/task_apps/enron/tests/integration/test_enron_rollout.py +135 -0
  86. examples/task_apps/enron/tests/unit/__init__.py +2 -0
  87. examples/task_apps/enron/tests/unit/test_enron_environment.py +126 -0
  88. examples/task_apps/math/__init__.py +0 -0
  89. examples/{rl/task_app → task_apps/math}/math_single_step.py +19 -10
  90. examples/task_apps/pokemon_battle/__init__.py +2 -0
  91. examples/task_apps/pokemon_battle/modal_app.py +104 -0
  92. examples/task_apps/pokemon_battle/task_app/README.md +68 -0
  93. examples/task_apps/pokemon_battle/task_app/__init__.py +6 -0
  94. examples/task_apps/pokemon_battle/task_app/pokemon_showdown.py +932 -0
  95. examples/task_apps/pokemon_red/README.md +357 -0
  96. examples/task_apps/pokemon_red/__init__.py +3 -0
  97. examples/task_apps/pokemon_red/eval_pokemon_red_policy.py +225 -0
  98. examples/task_apps/pokemon_red/pallet_town_rl_config.toml +73 -0
  99. examples/task_apps/pokemon_red/task_app.py +606 -0
  100. examples/task_apps/pokemon_red/test_pallet_town_rewards.py +191 -0
  101. examples/task_apps/sokoban/README.md +307 -0
  102. examples/task_apps/sokoban/__init__.py +3 -0
  103. examples/task_apps/sokoban/eval_groq_qwen32.toml +16 -0
  104. examples/task_apps/sokoban/eval_openai_gpt5.toml +16 -0
  105. examples/task_apps/sokoban/task_app.py +1058 -0
  106. examples/task_apps/sokoban/tests/__init__.py +2 -0
  107. examples/task_apps/sokoban/tests/conftest.py +113 -0
  108. examples/task_apps/sokoban/tests/integration/__init__.py +2 -0
  109. examples/task_apps/sokoban/tests/integration/test_sokoban_eval.py +57 -0
  110. examples/task_apps/sokoban/tests/integration/test_sokoban_rollout.py +198 -0
  111. examples/task_apps/sokoban/tests/unit/__init__.py +2 -0
  112. examples/task_apps/sokoban/tests/unit/test_sokoban_environment.py +114 -0
  113. examples/task_apps/verilog/__init__.py +1 -0
  114. examples/task_apps/verilog/eval_groq_qwen32b.toml +20 -0
  115. examples/task_apps/verilog/task_app/README.md +12 -0
  116. examples/task_apps/verilog/task_app/__init__.py +1 -0
  117. examples/task_apps/verilog/task_app/grpo_verilog.py +931 -0
  118. examples/task_apps/verilog/task_app/grpo_verilog_task_app.py +145 -0
  119. examples/task_apps/verilog/tests/__init__.py +2 -0
  120. examples/task_apps/verilog/tests/conftest.py +115 -0
  121. examples/task_apps/verilog/tests/integration/__init__.py +2 -0
  122. examples/task_apps/verilog/tests/integration/test_verilog_eval.py +179 -0
  123. examples/task_apps/verilog/tests/integration/test_verilog_rollout.py +55 -0
  124. examples/task_apps/verilog/tests/unit/__init__.py +2 -0
  125. examples/task_apps/verilog/tests/unit/test_verilog_scoring.py +118 -0
  126. examples/vlm/crafter_openai_vlm_agent.py +4 -4
  127. examples/vlm/run_crafter_vlm_benchmark.py +4 -4
  128. examples/workflows/__init__.py +0 -0
  129. examples/workflows/math_rl/__init__.py +0 -0
  130. examples/workflows/math_rl/download_dataset.py +80 -0
  131. synth_ai/__init__.py +2 -2
  132. synth_ai/api/train/builders.py +25 -11
  133. synth_ai/api/train/cli.py +12 -6
  134. synth_ai/api/train/configs/__init__.py +10 -10
  135. synth_ai/api/train/configs/rl.py +5 -4
  136. synth_ai/api/train/configs/sft.py +4 -3
  137. synth_ai/api/train/env_resolver.py +5 -2
  138. synth_ai/api/train/supported_algos.py +10 -5
  139. synth_ai/api/train/utils.py +7 -4
  140. synth_ai/cli/__init__.py +7 -51
  141. synth_ai/cli/_storage.py +4 -3
  142. synth_ai/cli/_validate_task_app.py +11 -0
  143. synth_ai/cli/balance.py +4 -3
  144. synth_ai/cli/calc.py +2 -2
  145. synth_ai/cli/demo.py +14 -7
  146. synth_ai/cli/legacy_root_backup.py +1 -1
  147. synth_ai/cli/rl_demo.py +8 -7
  148. synth_ai/cli/root.py +0 -97
  149. synth_ai/cli/task_apps.py +1707 -186
  150. synth_ai/demos/demo_task_apps/crafter/grpo_crafter_task_app.py +28 -16
  151. synth_ai/environments/examples/enron/engine.py +7 -2
  152. synth_ai/environments/examples/enron/environment.py +68 -0
  153. synth_ai/environments/examples/red/engine.py +27 -0
  154. synth_ai/environments/examples/red/engine_helpers/memory_map.py +7 -0
  155. synth_ai/environments/examples/red/engine_helpers/reward_library/pallet_town_progression.py +477 -0
  156. synth_ai/environments/examples/red/engine_helpers/state_extraction.py +32 -0
  157. synth_ai/environments/examples/red/environment.py +60 -0
  158. synth_ai/environments/examples/sokoban/taskset.py +116 -0
  159. synth_ai/environments/examples/verilog/engine.py +30 -4
  160. synth_ai/evals/client.py +58 -61
  161. synth_ai/jobs/client.py +16 -4
  162. synth_ai/judge_schemas.py +16 -16
  163. synth_ai/py.typed +0 -0
  164. synth_ai/task/__init__.py +14 -5
  165. synth_ai/task/contracts.py +124 -38
  166. synth_ai/task/proxy.py +48 -56
  167. synth_ai/task/rubrics/__init__.py +53 -0
  168. synth_ai/task/rubrics/loaders.py +133 -0
  169. synth_ai/task/rubrics/models.py +57 -0
  170. synth_ai/task/rubrics/scoring.py +113 -0
  171. synth_ai/{rubrics/validators.py → task/rubrics/strict.py} +53 -30
  172. synth_ai/task/server.py +8 -7
  173. synth_ai/task/validators.py +269 -6
  174. synth_ai/tracing_v3/decorators.py +7 -3
  175. synth_ai/tracing_v3/replica_sync.py +4 -4
  176. synth_ai/tracing_v3/serialization.py +5 -5
  177. synth_ai/tracing_v3/trace_utils.py +317 -0
  178. synth_ai/tracing_v3/turso/native_manager.py +3 -3
  179. {synth_ai-0.2.13.dev1.dist-info → synth_ai-0.2.13.dev2.dist-info}/METADATA +4 -1
  180. {synth_ai-0.2.13.dev1.dist-info → synth_ai-0.2.13.dev2.dist-info}/RECORD +214 -101
  181. examples/agora_ex/README_MoE.md +0 -224
  182. examples/agora_ex/__init__.py +0 -7
  183. examples/agora_ex/agora_ex.py +0 -65
  184. examples/agora_ex/agora_ex_task_app.py +0 -590
  185. examples/agora_ex/configs/rl_lora_qwen3_moe_2xh200.toml +0 -121
  186. examples/agora_ex/reward_fn_grpo-human.py +0 -129
  187. examples/agora_ex/system_prompt_CURRENT.md +0 -63
  188. examples/agora_ex/task_app/agora_ex_task_app.py +0 -590
  189. examples/agora_ex/task_app/reward_fn_grpo-human.py +0 -129
  190. examples/agora_ex/task_app/system_prompt_CURRENT.md +0 -63
  191. synth_ai/rubrics/__init__.py +0 -22
  192. synth_ai/task/rubrics.py +0 -219
  193. /examples/{warming_up_to_rl → task_apps/crafter}/task_app/README.md +0 -0
  194. /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/README.md +0 -0
  195. /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/__init__.py +0 -0
  196. /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/branching.py +0 -0
  197. /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/environment_routes.py +0 -0
  198. /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/envs/__init__.py +0 -0
  199. /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/envs/crafter/__init__.py +0 -0
  200. /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/envs/crafter/app.py +0 -0
  201. /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/envs/crafter/environment.py +0 -0
  202. /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/envs/crafter/react_agent.py +0 -0
  203. /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/envs/crafter/shared.py +0 -0
  204. /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/envs/crafter/tools.py +0 -0
  205. /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/hosted_app.py +0 -0
  206. /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/inference/__init__.py +0 -0
  207. /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/inference/openai_client.py +0 -0
  208. /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/main.py +0 -0
  209. /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/registry.py +0 -0
  210. /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/storage/__init__.py +0 -0
  211. /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/storage/volume.py +0 -0
  212. /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/test_agents.py +0 -0
  213. /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/utils.py +0 -0
  214. /examples/{rl/task_app → task_apps/math}/README.md +0 -0
  215. /examples/{rl/task_app → task_apps/math}/math_task_app.py +0 -0
  216. /examples/{rl → workflows/math_rl}/configs/eval_base_qwen.toml +0 -0
  217. /examples/{rl → workflows/math_rl}/configs/eval_rl_qwen.toml +0 -0
  218. /examples/{rl → workflows/math_rl}/configs/rl_from_base_qwen.toml +0 -0
  219. /examples/{rl → workflows/math_rl}/configs/rl_from_base_qwen17.toml +0 -0
  220. /examples/{rl → workflows/math_rl}/configs/rl_from_ft_qwen.toml +0 -0
  221. /examples/{rl → workflows/math_rl}/run_eval.py +0 -0
  222. /examples/{rl → workflows/math_rl}/run_rl_and_save.py +0 -0
  223. {synth_ai-0.2.13.dev1.dist-info → synth_ai-0.2.13.dev2.dist-info}/WHEEL +0 -0
  224. {synth_ai-0.2.13.dev1.dist-info → synth_ai-0.2.13.dev2.dist-info}/entry_points.txt +0 -0
  225. {synth_ai-0.2.13.dev1.dist-info → synth_ai-0.2.13.dev2.dist-info}/licenses/LICENSE +0 -0
  226. {synth_ai-0.2.13.dev1.dist-info → synth_ai-0.2.13.dev2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,631 @@
1
+ # This file is taken from https://github.com/dvruette/pygba/blob/main/pygba/utils.py
2
+ # and modified to work with the Pokemon Emerald game.
3
+
4
+ import functools
5
+ import struct
6
+ from collections import namedtuple
7
+
8
+ from pokemon_env.enums import Move, PokemonType, StatusCondition
9
+ from pokemon_env.types import PokemonData
10
+
11
+
12
+ class BaseCharmap:
13
+ charmap: list[str]
14
+ terminator: int
15
+
16
+ def decode(self, chars: bytes) -> str:
17
+ string = ""
18
+ for i in range(len(chars)):
19
+ if chars[i] == self.terminator:
20
+ break
21
+ string += self.charmap[chars[i]]
22
+ return string
23
+
24
+ class AsciiCharmap(BaseCharmap):
25
+ charmap = [
26
+ "", "", "", "", "", "", "", "", "\r", "\t", " ", " ", " ", "\n", "", "",
27
+ "", "", "", "", "", "", "", "", "", "", "SUB", "ESC", "", "", "", "",
28
+ " ", "!", "\"", "#", "$", "%", "&", "'", "(", ")", "*", "+", ",", "-", ".", "/", "0", "1",
29
+ "2", "3", "4", "5", "6", "7", "8", "9", ":", ";", "<", "=", ">", "?", "@", "A",
30
+ "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q",
31
+ "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "[", "\\", "]", "^", "_", "`", "a",
32
+ "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q",
33
+ "r", "s", "t", "u", "v", "w", "x", "y", "z", "{", "|", "}", "~", "DEL", "Ç", "ü",
34
+ "é", "â", "ä", "à", "å", "ç", "ê", "ë", "è", "ï", "î", "ì", "Ä", "Å", "É", "æ",
35
+ "Æ", "ô", "ö", "ò", "û", "ù", "ÿ", "Ö", "Ü", "ø", "£", "Ø", "×", "ƒ", "á", "í",
36
+ "ó", "ú", "ñ", "Ñ", "ª", "º", "¿", "®", "¬", "½", "¼", "¡", "«", "»", "░", "▒",
37
+ "▓", "│", "┤", "Á", "Â", "À", "©", "╣", "║", "╗", "╝", "¢", "¥", "┐", "└", "┴",
38
+ "┬", "├", "─", "┼", "ã", "Ã", "╚", "╔", "╩", "╦", "╠", "═", "╬", "¤", "ð", "Ð",
39
+ "Ê", "Ë", "È", "ı", "Í", "Î", "Ï", "┘", "┌", "█", "▄", "¦", "Ì", "▀", "Ó", "ß",
40
+ "Ô", "Ò", "õ", "Õ", "µ", "þ", "Þ", "Ú", "Û", "Ù", "ý", "Ý", "¯", "´", "¬", "±",
41
+ "‗", "¾", "¶", "§", "÷", "¸", "°", "¨", "•", "¹", "³", "²", "■", "\u00a0",
42
+ ]
43
+ terminator = 0x00
44
+
45
+
46
+
47
+ # Pokemon Emerald Sym Addresses
48
+ # https://raw.githubusercontent.com/pret/pokeemerald/symbols/pokeemerald.sym
49
+
50
+ ADDRESSES = {
51
+ "gPlayerPartyCount": 0x020244e9,
52
+ "gPlayerParty": 0x020244ec,
53
+ "gSaveBlock1Ptr": 0x03005d8c,
54
+ "gSaveBlock2Ptr": 0x03005d90,
55
+ "gPokemonStoragePtr": 0x03005d94,
56
+ "gSpeciesNames": 0x083185c8,
57
+ "sSpeciesToHoennPokedexNum": 0x0831d94c,
58
+ "sSpeciesToNationalPokedexNum": 0x0831dc82,
59
+ "sHoennToNationalOrder": 0x0831dfb8,
60
+ "gExperienceTables": 0x0831f72c,
61
+ "gSpeciesInfo": 0x083203cc,
62
+ "gItems": 0x085839a0,
63
+ }
64
+
65
+
66
+ # Struct layouts and constants taken from pret/pokeemerald:
67
+ # https://github.com/pret/pokeemerald/blob/master/include/pokemon.h
68
+ # https://github.com/pret/pokeemerald/blob/master/include/global.h
69
+
70
+
71
+ ## Constants
72
+
73
+ POKEMON_NAME_LENGTH = 10
74
+ PLAYER_NAME_LENGTH = 7
75
+ PC_ITEMS_COUNT = 50
76
+ BAG_ITEMS_COUNT = 30
77
+ BAG_KEYITEMS_COUNT = 30
78
+ BAG_POKEBALLS_COUNT = 16
79
+ BAG_TMHM_COUNT = 64
80
+ BAG_BERRIES_COUNT = 46
81
+
82
+ NUM_SPECIES = 412
83
+ NUM_DEX_FLAG_BYTES = (NUM_SPECIES + 7) // 8
84
+
85
+ TOTAL_BOXES_COUNT = 14
86
+ IN_BOX_COUNT = 30
87
+ BOX_NAME_LENGTH = 8
88
+
89
+
90
+ ## Flag IDs
91
+
92
+ SCRIPT_FLAGS_START = 0x50
93
+ TRAINER_FLAGS_START = 0x500
94
+ SYSTEM_FLAGS_START = 0x860
95
+ DAILY_FLAGS_START = 0x920
96
+
97
+ FLAG_DEFEATED_RUSTBORO_GYM = 0x4F0
98
+ FLAG_DEFEATED_DEWFORD_GYM = 0x4F1
99
+ FLAG_DEFEATED_MAUVILLE_GYM = 0x4F2
100
+ FLAG_DEFEATED_LAVARIDGE_GYM = 0x4F3
101
+ FLAG_DEFEATED_PETALBURG_GYM = 0x4F4
102
+ FLAG_DEFEATED_FORTREE_GYM = 0x4F5
103
+ FLAG_DEFEATED_MOSSDEEP_GYM = 0x4F6
104
+ FLAG_DEFEATED_SOOTOPOLIS_GYM = 0x4F7
105
+ FLAG_DEFEATED_METEOR_FALLS_STEVEN = 0x4F8
106
+
107
+ FLAG_DEFEATED_ELITE_4_SIDNEY = 0x4FB
108
+ FLAG_DEFEATED_ELITE_4_PHOEBE = 0x4FC
109
+ FLAG_DEFEATED_ELITE_4_GLACIA = 0x4FD
110
+ FLAG_DEFEATED_ELITE_4_DRAKE = 0x4FE
111
+
112
+
113
+ FLAG_SYS_POKEMON_GET = SYSTEM_FLAGS_START + 0x0
114
+ FLAG_SYS_POKEDEX_GET = SYSTEM_FLAGS_START + 0x1
115
+ FLAG_SYS_POKENAV_GET = SYSTEM_FLAGS_START + 0x2
116
+ FLAG_RECEIVED_POKEDEX_FROM_BIRCH = SYSTEM_FLAGS_START + 0x84
117
+
118
+ FLAG_BADGE01_GET = SYSTEM_FLAGS_START + 0x7
119
+ FLAG_BADGE02_GET = SYSTEM_FLAGS_START + 0x8
120
+ FLAG_BADGE03_GET = SYSTEM_FLAGS_START + 0x9
121
+ FLAG_BADGE04_GET = SYSTEM_FLAGS_START + 0xa
122
+ FLAG_BADGE05_GET = SYSTEM_FLAGS_START + 0xb
123
+ FLAG_BADGE06_GET = SYSTEM_FLAGS_START + 0xc
124
+ FLAG_BADGE07_GET = SYSTEM_FLAGS_START + 0xd
125
+ FLAG_BADGE08_GET = SYSTEM_FLAGS_START + 0xe
126
+
127
+ FLAG_VISITED_LITTLEROOT_TOWN = SYSTEM_FLAGS_START + 0xF
128
+ FLAG_VISITED_OLDALE_TOWN = SYSTEM_FLAGS_START + 0x10
129
+ FLAG_VISITED_DEWFORD_TOWN = SYSTEM_FLAGS_START + 0x11
130
+ FLAG_VISITED_LAVARIDGE_TOWN = SYSTEM_FLAGS_START + 0x12
131
+ FLAG_VISITED_FALLARBOR_TOWN = SYSTEM_FLAGS_START + 0x13
132
+ FLAG_VISITED_VERDANTURF_TOWN = SYSTEM_FLAGS_START + 0x14
133
+ FLAG_VISITED_PACIFIDLOG_TOWN = SYSTEM_FLAGS_START + 0x15
134
+ FLAG_VISITED_PETALBURG_CITY = SYSTEM_FLAGS_START + 0x16
135
+ FLAG_VISITED_SLATEPORT_CITY = SYSTEM_FLAGS_START + 0x17
136
+ FLAG_VISITED_MAUVILLE_CITY = SYSTEM_FLAGS_START + 0x18
137
+ FLAG_VISITED_RUSTBORO_CITY = SYSTEM_FLAGS_START + 0x19
138
+ FLAG_VISITED_FORTREE_CITY = SYSTEM_FLAGS_START + 0x1A
139
+ FLAG_VISITED_LILYCOVE_CITY = SYSTEM_FLAGS_START + 0x1B
140
+ FLAG_VISITED_MOSSDEEP_CITY = SYSTEM_FLAGS_START + 0x1C
141
+ FLAG_VISITED_SOOTOPOLIS_CITY = SYSTEM_FLAGS_START + 0x1D
142
+ FLAG_VISITED_EVER_GRANDE_CITY = SYSTEM_FLAGS_START + 0x1E
143
+
144
+ FLAG_IS_CHAMPION = SYSTEM_FLAGS_START + 0x1F
145
+
146
+
147
+
148
+
149
+ class EmeraldCharmap(BaseCharmap):
150
+ charmap = [
151
+ " ", "À", "Á", "Â", "Ç", "È", "É", "Ê", "Ë", "Ì", "こ", "Î", "Ï", "Ò", "Ó", "Ô",
152
+ "Œ", "Ù", "Ú", "Û", "Ñ", "ß", "à", "á", "ね", "ç", "è", "é", "ê", "ë", "ì", "ま",
153
+ "î", "ï", "ò", "ó", "ô", "œ", "ù", "ú", "û", "ñ", "º", "ª", "�", "&", "+", "あ",
154
+ "ぃ", "ぅ", "ぇ", "ぉ", "Lv", "=", ";", "が", "ぎ", "ぐ", "げ", "ご", "ざ", "じ", "ず", "ぜ",
155
+ "ぞ", "だ", "ぢ", "づ", "で", "ど", "ば", "び", "ぶ", "べ", "ぼ", "ぱ", "ぴ", "ぷ", "ぺ", "ぽ",
156
+ "っ", "¿", "¡", "P\u200dk", "M\u200dn", "P\u200do", "K\u200dé", "B\u200dL", "O\u200dC", "\u200dK", "Í", "%", "(", ")", "セ", "ソ",
157
+ "タ", "チ", "ツ", "テ", "ト", "ナ", "ニ", "ヌ", "â", "ノ", "ハ", "ヒ", "フ", "ヘ", "ホ", "í",
158
+ "ミ", "ム", "メ", "モ", "ヤ", "ユ", "ヨ", "ラ", "リ", "⬆", "⬇", "⬅", "➡", "ヲ", "ン", "ァ",
159
+ "ィ", "ゥ", "ェ", "ォ", "ャ", "ュ", "ョ", "ガ", "ギ", "グ", "ゲ", "ゴ", "ザ", "ジ", "ズ", "ゼ",
160
+ "ゾ", "ダ", "ヂ", "ヅ", "デ", "ド", "バ", "ビ", "ブ", "ベ", "ボ", "パ", "ピ", "プ", "ペ", "ポ",
161
+ "ッ", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "!", "?", ".", "-", "・",
162
+ "…", "“", "”", "‘", "’", "♂", "♀", "$", ",", "×", "/", "A", "B", "C", "D", "E",
163
+ "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U",
164
+ "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k",
165
+ "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "▶",
166
+ ":", "Ä", "Ö", "Ü", "ä", "ö", "ü", "⬆", "⬇", "⬅", "�", "�", "�", "�", "�", "",
167
+ ]
168
+ terminator = 0xFF
169
+
170
+
171
+ PokemonSubstruct0_spec = (
172
+ ("species", "H"),
173
+ ("heldItem", "H"),
174
+ ("experience", "I"),
175
+ ("ppBonuses", "B"),
176
+ ("friendship", "B"),
177
+ ("unknown", "H"),
178
+ )
179
+ PokemonSubstruct0 = namedtuple("PokemonSubstruct0", [x[0] for x in PokemonSubstruct0_spec])
180
+ PokemonSubstruct0_format = "".join([x[1] for x in PokemonSubstruct0_spec])
181
+
182
+ PokemonSubstruct1_spec = None
183
+ PokemonSubstruct1 = namedtuple("PokemonSubstruct1", ("moves", "pp"))
184
+ PokemonSubstruct1_format = "4H4B"
185
+
186
+ PokemonSubstruct2_spec = (
187
+ ("hpEV", "B"),
188
+ ("attackEV", "B"),
189
+ ("defenseEV", "B"),
190
+ ("speedEV", "B"),
191
+ ("spAttackEV", "B"),
192
+ ("spDefenseEV", "B"),
193
+ ("cool", "B"),
194
+ ("beauty", "B"),
195
+ ("cute", "B"),
196
+ ("smart", "B"),
197
+ ("tough", "B"),
198
+ ("sheen", "B"),
199
+ )
200
+ PokemonSubstruct2 = namedtuple("PokemonSubstruct2", [x[0] for x in PokemonSubstruct2_spec])
201
+ PokemonSubstruct2_format = "".join([x[1] for x in PokemonSubstruct2_spec])
202
+
203
+ PokemonSubstruct3_spec = None
204
+ PokemonSubstruct3_format = "III"
205
+ PokemonSubstruct3 = namedtuple("PokemonSubstruct3", (
206
+ "pokerus",
207
+ "metLocation",
208
+ "metLevel",
209
+ "metGame",
210
+ "pokeball",
211
+ "otGender",
212
+ "hpIV",
213
+ "attackIV",
214
+ "defenseIV",
215
+ "speedIV",
216
+ "spAttackIV",
217
+ "spDefenseIV",
218
+ "isEgg",
219
+ "abilityNum",
220
+ "ribbons",
221
+ ))
222
+
223
+
224
+ BoxPokemon_spec = (
225
+ ("personality", "I"),
226
+ ("otId", "I"),
227
+ ("nickname", f"{POKEMON_NAME_LENGTH}s"),
228
+ ("language", "B"),
229
+ ("flags", "B"),
230
+ ("otName", f"{PLAYER_NAME_LENGTH}s"),
231
+ ("markings", "B"),
232
+ ("checksum", "H"),
233
+ ("unknown", "H"),
234
+ ("substructs", f"{48}s"),
235
+ )
236
+ BoxPokemon = namedtuple("BoxPokemon", [x[0] for x in BoxPokemon_spec])
237
+ BoxPokemon_format = "".join([x[1] for x in BoxPokemon_spec])
238
+
239
+
240
+ Pokemon_spec = (
241
+ ("box", f"{struct.calcsize(BoxPokemon_format)}s"),
242
+ ("status", "I"),
243
+ ("level", "B"),
244
+ ("mail", "B"),
245
+ ("hp", "H"),
246
+ ("maxHp", "H"),
247
+ ("attack", "H"),
248
+ ("defense", "H"),
249
+ ("speed", "H"),
250
+ ("spAttack", "H"),
251
+ ("spDefense", "H"),
252
+ )
253
+ Pokemon = namedtuple("Pokemon", [x[0] for x in Pokemon_spec])
254
+ Pokemon_format = "".join([x[1] for x in Pokemon_spec])
255
+
256
+
257
+ Pokedex_spec = (
258
+ ("order", "B"),
259
+ ("mode", "B"),
260
+ ("nationalMagic", "B"),
261
+ ("padding1", "B"),
262
+ ("unownPersonality", "I"),
263
+ ("spindaPersonality", "I"),
264
+ ("padding2", "4s"),
265
+ ("owned", f"{NUM_DEX_FLAG_BYTES}s"),
266
+ ("seen", f"{NUM_DEX_FLAG_BYTES}s"),
267
+ )
268
+ Pokedex = namedtuple("Pokedex", [x[0] for x in Pokedex_spec])
269
+ Pokedex_format = "".join([x[1] for x in Pokedex_spec])
270
+
271
+
272
+ SpeciesInfo_spec = (
273
+ ("baseHP", "B"),
274
+ ("baseAttack", "B"),
275
+ ("baseDefense", "B"),
276
+ ("baseSpeed", "B"),
277
+ ("baseSpAttack", "B"),
278
+ ("baseSpDefense", "B"),
279
+ ("type1", "B"),
280
+ ("type2", "B"),
281
+ ("catchRate", "B"),
282
+ ("expYield", "B"),
283
+ ("evYield", "H"),
284
+ ("itemCommon", "H"),
285
+ ("itemRare", "H"),
286
+ ("genderRatio", "B"),
287
+ ("eggCycles", "B"),
288
+ ("friendship", "B"),
289
+ ("growthRate", "B"),
290
+ ("eggGroup1", "B"),
291
+ ("eggGroup2", "B"),
292
+ ("ability1", "B"),
293
+ ("ability2", "B"),
294
+ ("safariZoneFleeRate", "B"),
295
+ ("bodyColor", "Bxx"),
296
+ )
297
+ SpeciesInfo = namedtuple("SpeciesInfo", [x[0] for x in SpeciesInfo_spec])
298
+ SpeciesInfo_format = "".join([x[1] for x in SpeciesInfo_spec])
299
+
300
+ Coords16_spec = (
301
+ ("x", "H"),
302
+ ("y", "H"),
303
+ )
304
+ Coords16 = namedtuple("Coords16", [x[0] for x in Coords16_spec])
305
+ Coords16_format = "".join([x[1] for x in Coords16_spec])
306
+
307
+ WarpData_spec = (
308
+ ("mapGroup", "b"),
309
+ ("mapNum", "b"),
310
+ ("warpId", "bx"),
311
+ ("x", "H"),
312
+ ("y", "H"),
313
+ )
314
+ WarpData = namedtuple("WarpData", [x[0] for x in WarpData_spec])
315
+ WarpData_format = "".join([x[1] for x in WarpData_spec])
316
+
317
+ ItemSlot_spec = (
318
+ ("itemId", "H"),
319
+ ("quantity", "H"),
320
+ )
321
+ ItemSlot = namedtuple("ItemSlot", [x[0] for x in ItemSlot_spec])
322
+ ItemSlot_format = "".join([x[1] for x in ItemSlot_spec])
323
+
324
+ SaveBlock2_spec = (
325
+ ("playerName", f"{PLAYER_NAME_LENGTH + 1}s"),
326
+ ("playerGender", "B"),
327
+ ("specialSaveWarpFlags", "B"),
328
+ ("playerTrainerId", "4s"),
329
+ ("playTimeHours", "H"),
330
+ ("playTimeMinutes", "B"),
331
+ ("playTimeSeconds", "B"),
332
+ ("playTimeVBlanks", "B"),
333
+ ("optionsButtonMode", "B"),
334
+ ("options", "H"),
335
+ ("padding1", "2s"),
336
+ ("pokedex", f"{struct.calcsize(Pokedex_format)}s"),
337
+ ("filler_90", "8s"),
338
+ ("localTimeOffset", "8s"),
339
+ ("lastBerryTreeUpdate", "8s"),
340
+ ("gcnLinkFlags", "I"),
341
+ ("encryptionKey", "I"),
342
+ ("rest", f"{0xe7c}s"),
343
+ )
344
+ SaveBlock2 = namedtuple("SaveBlock2", [x[0] for x in SaveBlock2_spec])
345
+ SaveBlock2_format = "".join([x[1] for x in SaveBlock2_spec])
346
+
347
+ SaveBlock1_spec = (
348
+ ("pos", f"{struct.calcsize(Coords16_format)}s"),
349
+ ("location", f"{struct.calcsize(WarpData_format)}s"),
350
+ ("continueGameWarp", f"{struct.calcsize(WarpData_format)}s"),
351
+ ("dynamicWarp", f"{struct.calcsize(WarpData_format)}s"),
352
+ ("lastHealLocation", f"{struct.calcsize(WarpData_format)}s"),
353
+ ("escapeWarp", f"{struct.calcsize(WarpData_format)}s"),
354
+ ("savedMusic", "H"),
355
+ ("weather", "B"),
356
+ ("weatherCycleStage", "B"),
357
+ ("flashLevel", "B"),
358
+ ("padding1", "B"),
359
+ ("mapLayoutId", "H"),
360
+ ("mapView", f"{0x200}s"),
361
+ ("playerPartyCount", "B"),
362
+ ("padding2", "3s"),
363
+ ("playerParty", f"{600}s"),
364
+ ("money", "I"),
365
+ ("coins", "H"),
366
+ ("registeredItem", "H"),
367
+ ("pcItems", f"{struct.calcsize(ItemSlot_format) * PC_ITEMS_COUNT}s"),
368
+ ("bagPocket_Items", f"{struct.calcsize(ItemSlot_format) * BAG_ITEMS_COUNT}s"),
369
+ ("bagPocket_KeyItems", f"{struct.calcsize(ItemSlot_format) * BAG_KEYITEMS_COUNT}s"),
370
+ ("bagPocket_PokeBalls", f"{struct.calcsize(ItemSlot_format) * BAG_POKEBALLS_COUNT}s"),
371
+ ("bagPocket_TMHM", f"{struct.calcsize(ItemSlot_format) * BAG_TMHM_COUNT}s"),
372
+ ("bagPocket_Berries", f"{struct.calcsize(ItemSlot_format) * BAG_BERRIES_COUNT}s"),
373
+ ("pokeblocks", f"{320}s"),
374
+ ("seen1", f"{NUM_DEX_FLAG_BYTES}s"),
375
+ ("berryBlenderRecords", "6s"),
376
+ ("unused", "6s"),
377
+ ("trainerRematchStepCounter", "H"),
378
+ ("trainedRematches", "100s"),
379
+ ("padding3", "2s"),
380
+ ("objectEvents", f"{576}s"),
381
+ ("objectEventTemplates", f"{1536}s"),
382
+ ("flags", f"{300}s"),
383
+ ("rest", f"{0x29ec}s"),
384
+ )
385
+ SaveBlock1 = namedtuple("SaveBlock1", [x[0] for x in SaveBlock1_spec])
386
+ SaveBlock1_format = "".join([x[1] for x in SaveBlock1_spec])
387
+
388
+
389
+ PokemonStorage_spec = (
390
+ ("currentBox", "B"),
391
+ ("padding", "3s"), # 3 bytes padding
392
+ ("boxes", f"{struct.calcsize(BoxPokemon_format) * TOTAL_BOXES_COUNT * IN_BOX_COUNT}s"),
393
+ ("boxNames", f"{TOTAL_BOXES_COUNT * (BOX_NAME_LENGTH + 1)}s"),
394
+ ("boxWallpapers", f"{TOTAL_BOXES_COUNT}s"),
395
+ )
396
+ PokemonStorage = namedtuple("PokemonStorage", [x[0] for x in PokemonStorage_spec])
397
+ PokemonStorage_format = "".join([x[1] for x in PokemonStorage_spec])
398
+
399
+
400
+ def parse_box_pokemon(data):
401
+ if int.from_bytes(data[:4], "little") == 0:
402
+ return None
403
+
404
+ box = BoxPokemon._make(struct.unpack("<" + BoxPokemon_format, data))
405
+
406
+ key = box.otId ^ box.personality
407
+ substructs_raw = struct.unpack("<" + "I" * 12, box.substructs)
408
+ substructs = [x ^ key for x in substructs_raw]
409
+
410
+ substructSelector = [
411
+ [0, 1, 2, 3], [0, 1, 3, 2], [0, 2, 1, 3], [0, 3, 1, 2],
412
+ [0, 2, 3, 1], [0, 3, 2, 1], [1, 0, 2, 3], [1, 0, 3, 2],
413
+ [2, 0, 1, 3], [3, 0, 1, 2], [2, 0, 3, 1], [3, 0, 2, 1],
414
+ [1, 2, 0, 3], [1, 3, 0, 2], [2, 1, 0, 3], [3, 1, 0, 2],
415
+ [2, 3, 0, 1], [3, 2, 0, 1], [1, 2, 3, 0], [1, 3, 2, 0],
416
+ [2, 1, 3, 0], [3, 1, 2, 0], [2, 3, 1, 0], [3, 2, 1, 0],
417
+ ]
418
+ # get substruct permutation by personality mod 24
419
+ perm = substructSelector[box.personality % 24]
420
+ substruct0 = substructs[3 * perm[0] : 3 * (perm[0] + 1)]
421
+ substruct1 = substructs[3 * perm[1] : 3 * (perm[1] + 1)]
422
+ substruct2 = substructs[3 * perm[2] : 3 * (perm[2] + 1)]
423
+ substruct3 = substructs[3 * perm[3] : 3 * (perm[3] + 1)]
424
+
425
+ substruct0 = PokemonSubstruct0._make(struct.unpack("<" + PokemonSubstruct0_format, struct.pack("<" + "I" * 3, *substruct0)))
426
+ substruct2 = PokemonSubstruct2._make(struct.unpack("<" + PokemonSubstruct2_format, struct.pack("<" + "I" * 3, *substruct2)))
427
+
428
+ x1, x2, x3 = substruct1
429
+ substruct1 = PokemonSubstruct1(
430
+ [
431
+ (x1 >> 0) & 0xFFFF,
432
+ (x1 >> 16) & 0xFFFF,
433
+ (x2 >> 0) & 0xFFFF,
434
+ (x2 >> 16) & 0xFFFF,
435
+ ],
436
+ [
437
+ (x3 >> 0) & 0xFF,
438
+ (x3 >> 8) & 0xFF,
439
+ (x3 >> 16) & 0xFF,
440
+ (x3 >> 24) & 0xFF,
441
+ ]
442
+ )
443
+
444
+ x1, x2, x3 = substruct3
445
+ substruct3 = PokemonSubstruct3(
446
+ (x1 >> 0) & 0xFF,
447
+ (x1 >> 8) & 0xFFFF,
448
+ (x1 >> 16) & 0b01111111,
449
+ (x1 >> 23) & 0xF,
450
+ (x1 >> 27) & 0xF,
451
+ (x1 >> 31) & 0b1,
452
+ (x2 >> 0) & 0b00011111,
453
+ (x2 >> 5) & 0b00011111,
454
+ (x2 >> 10) & 0b00011111,
455
+ (x2 >> 15) & 0b00011111,
456
+ (x2 >> 20) & 0b00011111,
457
+ (x2 >> 25) & 0b00011111,
458
+ (x2 >> 30) & 0b1,
459
+ (x2 >> 31) & 0b1,
460
+ x3,
461
+ )
462
+
463
+ box = box._replace(
464
+ nickname=EmeraldCharmap().decode(box.nickname),
465
+ otName=EmeraldCharmap().decode(box.otName),
466
+ substructs=(
467
+ substruct0._asdict(),
468
+ substruct1._asdict(),
469
+ substruct2._asdict(),
470
+ substruct3._asdict(),
471
+ ),
472
+ )
473
+
474
+ box = box._asdict()
475
+ del box["unknown"]
476
+ del box["substructs"][0]["unknown"]
477
+ return box
478
+
479
+ def parse_pokemon(data):
480
+ pokemon = Pokemon._make(struct.unpack("<" + Pokemon_format, data))
481
+ box = parse_box_pokemon(pokemon.box)
482
+ pokemon = pokemon._replace(box=box)
483
+ # Construct a PokemonData object
484
+ return PokemonData(
485
+ species_id=box['substructs'][0]['species'],
486
+ species_name=box['nickname'],
487
+ current_hp=pokemon.hp,
488
+ max_hp=pokemon.maxHp,
489
+ level=pokemon.level,
490
+ status=StatusCondition(pokemon.status),
491
+ type1=PokemonType(box['type1']) if 'type1' in box.keys() else None,
492
+ type2=PokemonType(box['type2']) if 'type2' in box.keys() else None,
493
+ moves=[Move(move).name for move in box['substructs'][1]['moves']],
494
+ move_pp=box['substructs'][1]['pp'],
495
+ trainer_id=box['otId'],
496
+ nickname=box['nickname'],
497
+ experience=box['substructs'][0]['experience']
498
+ )
499
+
500
+
501
+ def read_save_block_2(gba):
502
+ save_block_2_ptr = gba.read_u32(ADDRESSES["gSaveBlock2Ptr"])
503
+ if save_block_2_ptr == 0:
504
+ return None
505
+
506
+ save_block_2_data = gba.read_memory(save_block_2_ptr, struct.calcsize(SaveBlock2_format))
507
+ save_block_2 = SaveBlock2._make(struct.unpack("<" + SaveBlock2_format, save_block_2_data))
508
+ save_block_2 = save_block_2._replace(pokedex=Pokedex._make(struct.unpack("<" + Pokedex_format, save_block_2.pokedex))._asdict())
509
+ return save_block_2._asdict()
510
+
511
+ def read_save_block_1(gba, parse_items: bool = False):
512
+ save_block_1_ptr = gba.read_u32(ADDRESSES["gSaveBlock1Ptr"])
513
+ if save_block_1_ptr == 0:
514
+ return None
515
+
516
+ save_block_1_data = gba.read_memory(save_block_1_ptr, struct.calcsize(SaveBlock1_format))
517
+ save_block_1 = SaveBlock1._make(struct.unpack("<" + SaveBlock1_format, save_block_1_data))
518
+
519
+ player_party_count = gba.read_u8(ADDRESSES["gPlayerPartyCount"])
520
+ player_party_data = gba.read_memory(ADDRESSES["gPlayerParty"], player_party_count * struct.calcsize(Pokemon_format))
521
+
522
+ # parse nested structs
523
+ save_block_1 = save_block_1._replace(
524
+ pos=Coords16._make(struct.unpack("<" + Coords16_format, save_block_1.pos))._asdict(),
525
+ location=WarpData._make(struct.unpack("<" + WarpData_format, save_block_1.location))._asdict(),
526
+ continueGameWarp=WarpData._make(struct.unpack("<" + WarpData_format, save_block_1.continueGameWarp))._asdict(),
527
+ dynamicWarp=WarpData._make(struct.unpack("<" + WarpData_format, save_block_1.dynamicWarp))._asdict(),
528
+ lastHealLocation=WarpData._make(struct.unpack("<" + WarpData_format, save_block_1.lastHealLocation))._asdict(),
529
+ escapeWarp=WarpData._make(struct.unpack("<" + WarpData_format, save_block_1.escapeWarp))._asdict(),
530
+ playerParty=[
531
+ parse_pokemon(player_party_data[i:i+struct.calcsize(Pokemon_format)])
532
+ for i in range(0, player_party_count * struct.calcsize(Pokemon_format), struct.calcsize(Pokemon_format))
533
+ ],
534
+ )
535
+ if parse_items:
536
+ save_block_1 = save_block_1._replace(
537
+ pcItems=[
538
+ ItemSlot._make(struct.unpack("<" + ItemSlot_format, save_block_1.pcItems[i:i+struct.calcsize(ItemSlot_format)]))._asdict()
539
+ for i in range(0, len(save_block_1.pcItems), struct.calcsize(ItemSlot_format))
540
+ ],
541
+ bagPocket_Items=[
542
+ ItemSlot._make(struct.unpack("<" + ItemSlot_format, save_block_1.bagPocket_Items[i:i+struct.calcsize(ItemSlot_format)]))._asdict()
543
+ for i in range(0, len(save_block_1.bagPocket_Items), struct.calcsize(ItemSlot_format))
544
+ ],
545
+ bagPocket_KeyItems=[
546
+ ItemSlot._make(struct.unpack("<" + ItemSlot_format, save_block_1.bagPocket_KeyItems[i:i+struct.calcsize(ItemSlot_format)]))._asdict()
547
+ for i in range(0, len(save_block_1.bagPocket_KeyItems), struct.calcsize(ItemSlot_format))
548
+ ],
549
+ bagPocket_PokeBalls=[
550
+ ItemSlot._make(struct.unpack("<" + ItemSlot_format, save_block_1.bagPocket_PokeBalls[i:i+struct.calcsize(ItemSlot_format)]))._asdict()
551
+ for i in range(0, len(save_block_1.bagPocket_PokeBalls), struct.calcsize(ItemSlot_format))
552
+ ],
553
+ bagPocket_TMHM=[
554
+ ItemSlot._make(struct.unpack("<" + ItemSlot_format, save_block_1.bagPocket_TMHM[i:i+struct.calcsize(ItemSlot_format)]))._asdict()
555
+ for i in range(0, len(save_block_1.bagPocket_TMHM), struct.calcsize(ItemSlot_format))
556
+ ],
557
+ bagPocket_Berries=[
558
+ ItemSlot._make(struct.unpack("<" + ItemSlot_format, save_block_1.bagPocket_Berries[i:i+struct.calcsize(ItemSlot_format)]))._asdict()
559
+ for i in range(0, len(save_block_1.bagPocket_Berries), struct.calcsize(ItemSlot_format))
560
+ ]
561
+ )
562
+
563
+ return save_block_1._asdict()
564
+
565
+
566
+ def read_pokemon_storage(gba):
567
+ pokemon_storage_ptr = gba.read_u32(ADDRESSES["gPokemonStoragePtr"])
568
+ if pokemon_storage_ptr == 0:
569
+ return None
570
+
571
+ pokemon_storage_data = gba.read_memory(pokemon_storage_ptr, struct.calcsize(PokemonStorage_format))
572
+ pokemon_storage = PokemonStorage._make(struct.unpack("<" + PokemonStorage_format, pokemon_storage_data))
573
+
574
+ box_mon_size = struct.calcsize(BoxPokemon_format)
575
+ box_size = box_mon_size * IN_BOX_COUNT
576
+ parsed_boxes = []
577
+ for j in range(TOTAL_BOXES_COUNT):
578
+ parsed_boxes.append([
579
+ parse_box_pokemon(pokemon_storage.boxes[i:i+box_mon_size])
580
+ for i in range(j * box_size, (j + 1) * box_size, box_mon_size)
581
+ ])
582
+ pokemon_storage = pokemon_storage._replace(
583
+ boxes=parsed_boxes,
584
+ boxNames=[
585
+ pokemon_storage.boxNames[i:i+BOX_NAME_LENGTH]
586
+ for i in range(0, len(pokemon_storage.boxNames), BOX_NAME_LENGTH + 1)
587
+ ]
588
+ )
589
+ return pokemon_storage._asdict()
590
+
591
+ @functools.lru_cache(maxsize=1)
592
+ def read_species_names(gba):
593
+ species_names_ptr = ADDRESSES["gSpeciesNames"]
594
+ if species_names_ptr == 0:
595
+ return None
596
+
597
+ species_names_data = gba.read_memory(species_names_ptr, NUM_SPECIES * (POKEMON_NAME_LENGTH +1))
598
+ species_names = [
599
+ EmeraldCharmap().decode(species_names_data[i:i+POKEMON_NAME_LENGTH+1])
600
+ for i in range(0, len(species_names_data), POKEMON_NAME_LENGTH+1)
601
+ ]
602
+ return species_names
603
+
604
+ @functools.lru_cache(maxsize=1)
605
+ def read_species_info(gba):
606
+ species_info_ptr = ADDRESSES["gSpeciesInfo"]
607
+ if species_info_ptr == 0:
608
+ return None
609
+
610
+ species_info_data = gba.read_memory(species_info_ptr, NUM_SPECIES * struct.calcsize(SpeciesInfo_format))
611
+ species_info = [
612
+ SpeciesInfo._make(struct.unpack("<" + SpeciesInfo_format, species_info_data[i:i+struct.calcsize(SpeciesInfo_format)]))
613
+ for i in range(0, len(species_info_data), struct.calcsize(SpeciesInfo_format))
614
+ ]
615
+ return species_info
616
+
617
+ @functools.lru_cache(maxsize=1)
618
+ def read_experience_tables(gba):
619
+ exp_table_ptr = ADDRESSES["gExperienceTables"]
620
+ if exp_table_ptr == 0:
621
+ return None
622
+
623
+ # there's 6 different growth rates and 101 different levels, each being a 4-byte int
624
+ num_ints = 6 * 101
625
+ exp_table_data = gba.read_memory(exp_table_ptr, num_ints * 4)
626
+ exp_table_format = "<" + "I" * num_ints
627
+ exp_table_flat = struct.unpack(exp_table_format, exp_table_data)
628
+ exp_tables = []
629
+ for i in range(0, len(exp_table_flat), 101):
630
+ exp_tables.append(exp_table_flat[i:i+101])
631
+ return exp_tables