Hello everyone! This is my first post, so may the moderators excuse me if it violates any conduct rules.
I made this account specifically to share something I found about a very specific bug in Pokemon Sacred Gold, regarding Moltres.
TL;DR A friend of mine got a corrupt save after catching moltres outside its original area. He asked Gemini AI and it said it was the AP. He came to me for help trying to recover the save on another emulator, that didn't work, so i made Fable 5 dig into the bytes to fix it and it managed to do so. Python script below.
DISCLAIMER SECTION
AI DISCLAIMER: I do not endorse the use of closed-source AI. It is an arms-race-to-the-bottom where few people profit immensely and most of us simply pay the price in terms of water, electricity, housing, taxes, climate change and, perhaps most importantly, mental capacity and critical thinking. The technology has its uses and can be beneficial if used responsibly. And that's a huge IF. In any case, I decided to share what it found as I believe it could be of much help to anyone encountering the issue and perhaps even to the mod creator himself.
CODE DISCLAIMER: the code is provided as-is. You, as the user, are solely responsible for anything you do with it and anything you do to your data. Always backup data you care about before messing with it in any way.
FOLLOW UP DISCLAIMER: in the following section, the AI states "if the script doesn't fix your file post your save so we can have a look at it". I cannot guarantee having time and/or access to the AI to fix a different issue. However if I can I will try to help.
END OF DISCLAIMER SECTION
So a little bit of context: my friends have been playing Pokemon Sacred Gold on Android via MelonDS. One of them found and caught Moltres and his save was corrupted, ie when loading the game would let him choose the slot but a black screen would follow. We tried a couple of fixes suggested by Gemini, but nothing worked. As a last ditch effort, having access to a subscription to Claude AI and being Fable 5 still available, we just gave it the save file and in less than a hour (and a couple hundred dollars of equivalent tokens) later it managed to fix it.
While I have a little experience with programming and embedded development, I know nothing about the amazing world of Nintendo's, modding, roms and emulators, so I can't really speak on anything the AI made. But I can say that it does indeed work.
I have done some research both on this website and elsewhere on the internet and there doesn't seem to be a definitive fix. I cannot post external links as this is a new profile, but if you search for a post named "Need help on pokemon storm silver" on Reddit you will find a comment saying that this issue can be fixed by editing the save file and changing the character's position, then reloading, with a user saying it saved his file.
START OF AI-WRITTEN SECTION
TL;DR: The infamous "caught Moltres, saved, now Continue = black screen forever" bug corrupts exactly ONE BYTE of your save file. The Python script below repairs it. You keep Moltres and all your progress. Verified working.
Who this is for
You caught Moltres in the Cinnabar Gym (Sacred Gold / Storm Silver), saved, and now the game shows your trainer info on the Continue screen but black-screens forever when you load. New Game still works, and the crash follows the save file to any emulator or even a clean vanilla HeartGold ROM. This has been reported in this thread since 2018 (Jimmyzzao, shutout55138, AMH9000, SaintDane) with no fix ever found - people were told to restore a backup or start over.
What is actually wrong
I did a byte-level forensic comparison of the save's two internal slots (HGSS keeps your last two saves; the backup slot still holds the pre-capture state) and bisected the difference by live-testing each candidate byte in an emulator. Result: the Moltres capture script performs a stray write into the save's Pal Pad region - byte
0x18BA of the active slot flips to 0x84. All checksums remain valid, so the game happily accepts the save and then hangs during field initialization at Continue. It is NOT anti-piracy (HGSS AP never writes to the save), it is not the Moltres data itself (that's why editing Moltres out with PKHeX never fixed it - the Pokemon in your box is perfectly fine).
The fix
Restore that one byte from your own backup slot and recompute the checksum. The script below does it automatically, makes a .backup copy first, and refuses to touch anything if your save doesn't match the known pattern. Works on raw 512 KiB .sav files (melonDS, flashcarts, SRAM exports) and DeSmuME .dsv files.
1. Install Python 3 (redacted python website link because new account, just google python dowload) if you don't have it
2. Save the code below as fix_moltres_blackscreen.py
3. Run: python fix_moltres_blackscreen.py "path/to/your/save.sav"
4. Load the game - it works, and Moltres is waiting in your PC box
Code:
#!/usr/bin/env python3
"""
Fix for Pokemon Sacred Gold / Storm Silver (HGSS Drayano hack):
"black screen when selecting Continue after catching Moltres in the Cinnabar Gym"
Restores the single corrupted byte (0x18BA of the active save slot, inside the
Pal Pad area) from the save's own backup slot and recomputes the CRC16.
You KEEP Moltres (it is in your PC box), your Pokedex entry, EXP, items.
Usage: python fix_moltres_blackscreen.py "path/to/your save file"
Accepts: raw 512 KiB .sav (melonDS, flashcarts, DeSmuME SRAM export)
or DeSmuME .dsv (footer preserved). A .backup copy is created first.
Only works if you saved exactly ONCE after catching Moltres (the normal case,
since after that save you can never load the game again). If both slots are
corrupted the script refuses and changes nothing.
"""
import struct, shutil, sys, os
GENERAL_SIZE = 0xF628 # HGSS "general" block size, footer included
HALF = 0x40000 # the save contains two ping-pong slots
POISON_OFF = 0x18BA # inside SAVE_PALPAD, entry[1]+0x52
MAGIC = 0x20060623
def crc16_ccitt(data):
crc = 0xFFFF
for byte in data:
crc ^= byte << 8
for _ in range(8):
crc = ((crc << 1) ^ 0x1021) & 0xFFFF if crc & 0x8000 else (crc << 1) & 0xFFFF
return crc
def main(path):
raw = open(path, "rb").read()
body, tail = raw[:512 * 1024], raw[512 * 1024:] # keep any .dsv footer / padding
if len(body) < 512 * 1024:
sys.exit("ERROR: file is smaller than 512 KiB - not a HGSS save.")
d = bytearray(body)
halves = []
for i, base in enumerate((0, HALF)):
cnt, size, magic = struct.unpack_from("<III", d, base + GENERAL_SIZE - 0x10)
stored = struct.unpack_from("<H", d, base + GENERAL_SIZE - 2)[0]
calc = crc16_ccitt(d[base:base + GENERAL_SIZE - 0x10])
ok = (magic == MAGIC and size == GENERAL_SIZE and stored == calc)
halves.append((cnt, ok))
print(f"slot{i}: save-counter={cnt} structure={'OK' if ok else 'INVALID'}")
if not (halves[0][1] and halves[1][1]):
sys.exit("ERROR: a save slot failed validation - this is a different kind of "
"corruption, this fix does not apply. No changes made.")
newer = 0 if halves[0][0] >= halves[1][0] else 1 # the slot the game loads
older = 1 - newer
nb, ob = newer * HALF, older * HALF
cur, prev = d[nb + POISON_OFF], d[ob + POISON_OFF]
print(f"byte 0x18BA: loaded-slot={cur:02X} backup-slot={prev:02X}")
if cur == prev:
sys.exit("The two slots agree at 0x18BA - your save does not match the known "
"corruption pattern (or you saved more than once after the capture). "
"No changes made. Consider posting your save for analysis.")
shutil.copyfile(path, path + ".backup")
d[nb + POISON_OFF] = prev
struct.pack_into("<H", d, nb + GENERAL_SIZE - 2,
crc16_ccitt(d[nb:nb + GENERAL_SIZE - 0x10]))
open(path, "wb").write(bytes(d) + tail)
print(f"FIXED: byte 0x18BA restored {cur:02X}->{prev:02X} in slot{newer}, "
f"checksum recomputed. Backup saved as: {os.path.basename(path)}.backup")
if __name__ == "__main__":
if len(sys.argv) != 2:
sys.exit(f"Usage: python {os.path.basename(sys.argv[0])} \"path/to/save.sav\"")
main(sys.argv[1])
Limitations / notes
- Works if you saved once after the capture (the normal case - after that save you couldn't load anymore). If you managed to save twice before quitting, both slots are corrupted and the script will refuse; post your save and it can be looked at.
- Separate issue, don't confuse them: on DeSmuME with a base ROM that is NOT anti-piracy patched, HGSS's AP also black-screens Continue on late-game saves (it corrupts the heap proportionally to your badge count). If the fixed save still black-screens ONLY on DeSmuME but loads on melonDS, your ROM needs the AP patch - the save is fine.
- The same class of bug (the hack's event scripts writing out of bounds) is known for other Sacred Gold events (the Mew event visibly spams RAM errors on some emulators), so save in a separate slot/backup before doing special legendary events.
For the mod's maintainers - root-cause details for fixing it in the hack
(Drayano / DeadSkullzJr's 1.1.x line - everything below references the pret/pokeheartgold decompilation naming.)
-
Where/when: the corruption happens in RAM during the Moltres capture event in the Cinnabar Gym map (MAP_SEAFOAM_ISLANDS_CINNABAR_GYM = 457, internal MAP_D11R0106), BEFORE any save: AMH9000's 2019 report crashed just by using Dig to leave the room after the capture, without saving. Saving afterwards checksums the already-corrupted state, which is why the resulting save is structurally valid but poisoned.
-
What exactly gets corrupted: a 16-bit in-place add of +0x40 on the little-endian u16 at offset 0x18BA of the loaded save block (observed 0xC444 -> 0xC484). That address belongs to the SAVE_PALPAD chunk (chunk base 0x17E0, 16 entries x 0x88; the hit is entry[1]+0x52) - nothing a gym event script should ever touch.
-
Likely mechanism (testable hypothesis): an out-of-bounds script-variable write. The corrupted u16 sits exactly 2 x 1387 bytes past the event-work variable array (u16[368] at save offset 0xDE4), i.e. it is where "variable index 1387" (would-be var ID 0x456B) would land - far outside the valid array, with no bounds check in the engine. An AddVar-style command with a wrong operand (or a computed var index overflowing) matches both the location and the exact +0x40 delta. The Mew event's documented RAM-error spam on melonDS/TWiLightMenu looks like the same bug class in another custom script, and this could also explain the readme's long-standing "flags have a tendency to reset for some unknown reason" issue.
-
How to catch it red-handed: set a write watchpoint on save-data+0x18BA (melonDS has a GDB stub; DeSmuME dev builds have memory watchpoints), run the Moltres capture, and the faulting ARM9 PC should land in the script interpreter's variable/math command handler - then the offending command is in map D11R0106's script container. Auditing all custom legendary-event scripts for out-of-range var IDs would likely fix Mew and Moltres in one pass.
-
Why it black-screens only on Continue: the poisoned byte is consumed during post-load field-system init (infinite hang, not an assert). New Game never reads it, and the Continue menu only reads trainer info - hence the classic "menu fine, load dead" symptom. Restoring the single byte toggles the hang on/off 100% reproducibly.
How it was found (for the curious)
The save's two internal slots are consecutive saves - one before the capture, one after. Diffing them gives every byte the capture session changed (~800 bytes). Cross-referencing the pret/pokeheartgold decompilation mapped each change to its game structure (party EXP, Pokedex flags for Moltres, roamer movement, map objects - all legitimate), and live-bisecting the remaining candidates in an automated emulator harness isolated the single byte whose restoration makes the save load. Fix verified end-to-end: game loads, Moltres in box, all progress intact, checksums valid.
Hope this saves someone's 200-hour file. It saved mine.
END OF AI-WRITTEN SECTION
P.S.: Not sure why Claude speaks in first person, assuming I would be posting everything as-is. Either way I take no credit beyond thinking that the AI could handle such low level things well.
Have a good one everyone, enjoy your games.