Jump to content

Recommended Posts

Posted

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:

  1. showing the source counters and letting users infer trophy progress, or
  2. 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.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...