I did some reverse-engineering on the Pokéathlon block in Pokémon HeartGold (USA) because there seems to be almost no public documentation on it, and PKHeX currently does not expose any of this data.
The main reason I’m posting this is that when I started looking into Pokéathlon save data, I found basically nothing. I searched around for documentation on:
where course records are stored
where event records are stored
where medal-per-species data is stored
where Data Cards / Athlete Points / Pokéathlon stat counters are stored
how to get into performance-locked rooms to try and read the information stored in those rooms in-game
and I came up almost completely empty, which surprised me, since the mode is fairly extensive and engaging, but also gated by obtuse unlock systems and still collects a lot of hidden data.
The only remotely useful lead I found was the RetroAchievements HGSS codenotes page. That page was genuinely helpful as a starting point because it at least suggested that the values existed and gave named offsets for some Pokéathlon-related data. But those offsets were not directly the raw save offsets, and if you try to treat them that way, you get confusing or inconsistent behavior.
That mismatch was actually a big part of the research process. At first I used those codenote offsets as though they were describing the underlying save structure directly, and the results were weird enough that it became obvious they were not 1:1 raw-save locations. What eventually became clear is that, for the Pokéathlon block at least, the codenote addresses were useful as hints, but not as a direct map of the save file In the save I tested, the practical relationship for the Pokéathlon block was::
raw save offset = codenote offset - 0x10
So I want to emphasize that this post is not “here are some RA values.” It is:
“here is how the game’s Pokéathlon data is actually laid out in the raw save, after working backward from the only breadcrumb I could find.”
I’m posting this as save-structure research that may be useful if anyone ever wants to add read-only inspection, repair tooling, or full editor support for Pokéathlon in PKHeX. A lot of useful game state in this subsystem is effectively hidden unless you already know the raw locations, and some of it visibly gates or affects other in-game behavior (record screens, room progression, Data Cards, special trophy state, friendship totals, medal tracking, etc.).
This was all tested on a HeartGold (USA) save and validated with a combination of:
direct save diffs
in-game screen checks
emulator RAM correlation
reload verification after hex edits
TL;DR:
Pokéathlon data starts around offset 0xD980, ends around offset 0xE570 and is mirrored at +0x40000
Medal table starts at 0xDAB0
Course records start at 0xD9DA
Event records start at 0xDCA0
Global counters start at 0xE4D4
Event 1st-place counters start at 0xE518
Data Cards / Athlete Points start at 0xE548
HGSS Pokéathlon save research notes (HeartGold USA)
All offsets below were tested on HeartGold (USA); I did not validate SoulSilver, non-US revisions, or flashcart-specific save wrappers.
1. General structure
Save format / copies
This is probably known to you all but I’m repeating it here for completeness. In the save I tested, the relevant Pokéathlon data exists in two mirrored copies:
main copy at raw offset X
backup copy at raw offset X + 0x40000
When editing the raw save directly, I patched both copies.
Start and end of data
In my testing, all Pokéathlon-related fields I was able to identify fell between raw save offsets 0xD980 and 0xE570. I do not claim this is the complete set of all Pokéathlon-related data in the save; only that, during my testing, I did not find any fields outside this range that appeared to affect visible Pokéathlon behavior.
2. Data types
There are three common field types in this block:
A. Single-byte bitfields
Used for:
medal table entries
Data Card ownership flags
B. 16-bit little-endian values
Used for:
course records
most event records
joins, wins, time spent, action counters, etc.
C. 32-bit little-endian values
Used for:
per-event 1st-place counters
3. Course record totals
These are the five Pokéathlon course records.
All are 16-bit little-endian.
Field
Raw offset
Backup
Speed Course record
0xD9DA
0x4D9DA
Power Course record
0xDA06
0x4DA06
Skill Course record
0xDA32
0x4DA32
Stamina Course record
0xDA5E
0x4DA5E
Jump Course record
0xDA8A
0x4DA8A
These are simple numeric totals.
4. Medal table
This is the key structure for per-species medal acquisition.
Base offsets
main: 0xDAB0
backup: 0x4DAB0
Layout
There is one byte per species.
For species Dex #N, the byte is at:
main: 0xDAB0 + (N - 1)
backup: 0x4DAB0 + (N - 1)
Bit meaning
The medal byte is a 5-bit bitfield:
0x01 = Speed medal
0x02 = Power medal
0x04 = Skill medal
0x08 = Stamina medal
0x10 = Jump medal
Examples:
00 = no medals
01 = one medal (Speed)
03 = two medals (Speed and Power)
0B = three medals (Speed, Power, and Stamina)
1B = four medals (Speed, Power, Stamina, and Jump)
1F = all five medals
Practical note
This is one of the most useful structures PKHeX could expose, even in a read-only or semi-read-only form, because it controls:
which species visibly have medals
medal acquisition totals
a lot of downstream Pokéathlon state
5. Event records
These are the ten event-specific high-score fields shown in-game.
All are 16-bit little-endian.
Event
Raw offset
Backup
Hurdle Dash record
0xDCA0
0x4DCA0
Pennant Capture record
0xDCCC
0x4DCCC
Circle Push record
0xDCF8
0x4DCF8
Block Smash record
0xDD24
0x4DD24
Disc Catch record
0xDD50
0x4DD50
Lamp Jump record
0xDD7C
0x4DD7C
Relay Run record
0xDDA8
0x4DDA8
Ring Drop record
0xDDD4
0x4DDD4
Snow Throw record
0xDE00
0x4DE00
Goal Roll record
0xDE2C
0x4DE2C
Notes
Most of these appear to be straight numeric values.
Two deserve caution:
Hurdle Dash appears to be stored in an internal timing form. In my testing it behaved like frames at 30 fps.
Relay Run is real and stable in the save, but the encoding is not as straightforward as the other event values. I could not figure out how it was actually encoded and so while I do have the location mapped I am not sure what it translates to or how to manipulate it.
If PKHeX ever exposes these, I would consider:
direct editing for the simple fields
and a conversion helper or tooltip for Hurdle/Relay
6. Event-specific ‘times joined’ values shown on the record screens
These are 16-bit little-endian fields that sit 0x28 bytes before each event record.
Event
Raw offset
Backup
Hurdle Dash times joined
0xDCC8
0x4DCC8
Pennant Capture times joined
0xDCF4
0x4DCF4
Circle Push times joined
0xDD20
0x4DD20
Block Smash times joined
0xDD4C
0x4DD4C
Disc Catch times joined
0xDD78
0x4DD78
Lamp Jump times joined
0xDDA4
0x4DDA4
Relay Run times joined
0xDDD0
0x4DDD0
Ring Drop times joined
0xDDFC
0x4DDFC
Snow Throw times joined
0xDE28
0x4DE28
Goal Roll times joined
0xDE54
0x4DE54
These are worth exposing because they appear on the in-game record screens and are otherwise opaque. In my testing these values changed independently of the event record values themselves and appear to back the “times joined” numbers shown on the in-game record screens.
7. Event-side friendship / record-point table
There is a 10-entry 16-bit table at:
0xE4C0–0xE4D2
backup 0x4E4C0–0x4E4D2
This appears to be part of the event-side contribution to Pokéathlon Friendship totals.
I would label this cautiously, something like:
“Event record friendship values”
or
“Event-side Pokéathlon point totals”
This is one of the pieces that appears to feed the total Pokéathlon friendship/point display but I couldn’t determine if this value was automatically recalculated from the event high scores or not.
8. Global Pokéathlon counters / stats
These are all 16-bit little-endian and were validated against in-game stat screens and/or save diffs.
Field
Raw offset
Backup
Notes
Time spent in Pokéathlon
0xE4D4
0x4E4D4
Stored in minutes
Pokéathlon joins
0xE4D8
0x4E4D8
Global join count
Overall 1st places
0xE4DC
0x4E4DC
Global course wins
Overall last places
0xE4E0
0x4E4E0
Global course last-place count
Bonuses earned
0xE4E4
0x4E4E4
Visible on data card screen
Instructions
0xE4E8
0x4E4E8
Visible on data card screen
Pokémon failed
0xE4EC
0x4E4EC
Visible on data card screen
Times jumped
0xE4F0
0x4E4F0
Visible on data card screen
Pokémon acquired points
0xE4F4
0x4E4F4
Visible on data card screen; exact semantics of what “acquired points” means is still somewhat fuzzy
Times tackled
0xE4F8
0x4E4F8
Visible on data card screen
Pokémon fell down
0xE4FC
0x4E4FC
Visible on data card screen
Times dashed
0xE500
0x4E500
Visible on data card screen
Times switched
0xE504
0x4E504
Visible on data card screen
Times self-impeded
0xE508
0x4E508
Visible on data card screen
Notes
Time spent definitely uses minutes.
Instructions is a real stat, not just a derived UI label. The Instructions stat appears to be directly stored rather than recomputed from underlying subtotals, at least in my testing. This is different from some of the other totals which do appear to recalculate based on underlying subtotals.
Bonuses earned is also a real stored field.
Pokémon acquired points is definitely present and editable, but I would label it conservatively until someone confirms the exact semantics of what it is counting because I couldn’t figure out what it meant.
These fields are a good example of why editor support would be useful even without “editing for advantage”:
many of these are plainly visible to the player,
but cannot currently be inspected or repaired in any clean tool.
9. Per-event 1st-place counters
These are 32-bit little-endian counters.
Event
Raw offset
Backup
Hurdle Dash 1st places
0xE518
0x4E518
Pennant Capture 1st places
0xE51C
0x4E51C
Circle Push 1st places
0xE520
0x4E520
Block Smash 1st places
0xE524
0x4E524
Disc Catch 1st places
0xE528
0x4E528
Lamp Jump 1st places
0xE52C
0x4E52C
Relay Run 1st places
0xE530
0x4E530
Ring Drop 1st places
0xE534
0x4E534
Snow Throw 1st places
0xE538
0x4E538
Goal Roll 1st places
0xE53C
0x4E53C
These were among the most useful fields to identify because they gate or explain:
Data Card stat lines
special trophy progression
and a lot of otherwise hidden Pokéathlon state
A read-only display of these in PKHeX would already be valuable simply so players could track how many times they’ve come in first in these events without spending a fortune of points on data cards.
10. Aggregate event last-place total
There appears to be an aggregate field at:
0xE540
backup 0x4E540
My best current label is:
total event last places
I would still mark that “probable” rather than “perfectly solved,” but it fits the observed save diffs and stat screens well. I say this because I could not find a stat for individual last place totals for each event and this could be a field that can update based on a subtotal to overwrite an edited value.
11. Athlete Points and Data Cards
Athlete Points
main: 0xE548
backup: 0x4E548
type: 32-bit little-endian
This is the current spendable Pokéathlon currency; at the time I did this research, it was the only obvious Pokéathlon field I saw exposed in PKHeX
Data Cards
The Data Card ownership flags are here:
Cards 1–8: 0xE54C
Cards 9–16: 0xE54D
Cards 17–24: 0xE54E
Cards 25–27: 0xE54F bits 0–2
Backups:
0x4E54C
0x4E54D
0x4E54E
0x4E54F
These are exactly the kind of hidden-but-visible state that would be extremely nice to expose in an editor.
There are also room / progression consequences tied to having the right card / trophy / record state, which makes this doubly useful for repair and inspection purposes.
12. Friendship-point composition
One of the most useful conceptual findings is that total Pokéathlon friendship appears to be built from:
course totals + event-side totals + medal bitcounts
and will recalculate itself based on those totals. As a result, there was no point in trying to find nor edit an overall friendship point field.
In other words:
the five course record totals
the event record/friendship table
and the total number of medal bits set across the species medal table
all contribute to the displayed total friendship points.
This is useful because it means a save editor could:
explain why the friendship total is what it is
detect inconsistent states
and potentially help repair Pokéathlon progression in a way that mirrors the game’s own logic
13. Special trophies / derived state
One thing I ran into while investigating the “special trophy” behavior is that the obvious runtime trophy-room readout bits did not behave like stable persistent save flags when edited directly.
What did track well were the source counters:
joins
overall wins
dashes
switches
per-event 1st-place counts
So if PKHeX ever exposes special trophy state, I would strongly recommend either:
showing the source counters and letting users infer trophy progress, or
doing more research to find the actual persistent trophy ownership flags before exposing direct trophy toggles
At least from my testing, the visible trophy-room state seemed partly derived / refreshed by gameplay rather than a clean standalone save checkbox.
14. Why this matters for editor support
The reason I think this is worth posting is not “here is how to alter Pokéathlon.”
It’s that Pokéathlon is a large, player-visible subsystem with a lot of hidden states, and right now there is no clean way to:
inspect course/event record data
inspect medal-per-species data
inspect Data Card state
inspect special-trophy progress counters
inspect or validate the many stat-screen counters
or repair / normalize weird Pokéathlon saves
Even a read-only PKHeX Pokéathlon tab would be useful.
A full tab could eventually expose:
course totals
event records
event appearances
event 1st-place counts
action counters
Data Cards
Athlete Points
and per-species medal bits
That would make Pokéathlon much less of a black box and make it easier for players to understand or repair this subsystem.
15. Suggested PKHeX UI structure
If anyone on the PKHeX side ever wanted to add this, I think the cleanest UI would be:
A. Course records
Speed / Power / Skill / Stamina / Jump
B. Event records
10 event scores/records
maybe special helper display for Hurdle and Relay encodings
C. Event participation / wins
per-event “times joined”
per-event 1st places
D. Global stats
joins
time spent
overall wins / lasts
bonuses earned
instructions
dashed / jumped / tackled / switched / self-impeded
failed / fell down
Pokémon acquired points
E. Medal table
species list with 5 medal bits each
maybe a count of total medal bits and full medalists
F. Data Cards / Athlete Points
straightforward and very useful
G. Derived summary
If someone wanted to be fancy:
course subtotal
event subtotal
medal subtotal
total friendship
That would be extremely nice from a transparency / repair standpoint.
16. Caveats / uncertain labels
These are the things I would still treat with a little caution in an implementation:
Pokémon acquired points — definitely real, still no idea what it’s measuring
event-side friendship table at 0xE4C0–0xE4D2 — definitely important, but label could be refined and it’s still unclear exactly how it operates
Relay Run encoding
persistent trophy ownership flags vs. derived trophy-room state
Everything else listed above I’m reasonably confident in.
17. Conclusion
The Pokéathlon subsystem in HGSS is much more approachable than it first looks once the offsets are mapped. The important part is that the data is:
stable
mirrored predictably
and rich enough that an editor could expose a lot of useful states
The biggest obstacle when I started was simply that there was no real documentation, and the only lead I found was the RA codenotes page, which was useful as a clue but not a direct raw-save map. Most of the real work was turning those hints into an actual save-structure map that behaved correctly in game.
If anyone working on PKHeX wants this reorganized into a cleaner CSV / table / code-oriented format, I’d be happy to do that, but the offsets and interpretations above should already be enough to start prototyping support if the developers so choose.