Jump to content

[Gen 2] Pokémon Stadium 2 GS Savefile Structure (also, a dumper for registered teams)


Recommended Posts

I've looked into the Stadium 2 savefile to locate registered teams and also coded a dumper that exports the registered pokémon as pk2 files ready to use with pkhex. This may help some people get their original pokémon back, if they ever registered them on Stadium 2. As a bonus, this can be used to dump the rental pokémon if you register them.

Note about the dumps: registered pokémon lose the extra 0x50 terminators in the nickname (when a pokémon is never nicknamed, all extra bytes are set as 0x50, when nicknamed they are set to 0x00). This has no real impact, but it's a pitty they aren't 1:1 copies. (The status condition is also lost, but that isn't important either so...).

UPDATE 14/03/2020: It seems Stadium 2 never actually stores the "full" name of the pokémon. The name is stored only until a 0x50 terminator is found. This makes depositing the pokémon in stadium 2 become "nicknamed". This doesn't make any actual different, but on the GBC games this wouldn't happen as the game seems to keep copying the bytes 0x50 bytes. Because pokémon data in boxes in the GB games isn't actually erased but rather the box slot is marked as "empty" when you withdraw/release/move pokémon, there is a chance that when you return the pokémon to GB the pokémon will become unnicknamed again, depending on the pokémon that was stored in that slot before. If one would like to return the pokémon to the "unnicknamed status", the easiest would be to have an unnicknamed evolved or wild muk stored in the box (because it's the shortest pokémon name and all chars besides the first 3 will be 0x50). Then put a pokémon from stadium 2 into that box in the same slot muk was. This is easiest done in a box with 19 pokemon, deposit muk in GB, withdraw, then deposit pokémon from stadium 2 into that box.
This doesn't happen with OT name, it is fully copied. I suspect this is done so only full OT name + ID can nicnkame a pokémon (the pokémon being marked as nicknamed or not is irrelevant).
Thus, this finding makes any registered pokémon recovered with the dumper be actually the same one you had back in your day.

Registered teams start at 0x4000 in the savefile, here's the structure:

Registered parties are 0x180 each: 0x10 header + six 0x3C pokemon structures + 2 bytes padding, 4 byte footer and 2 byte checksum

0x00 Header:
    0x0 1 True if team is present (0x00 if no team)
    0x1 1 Padding 0x00
    0x2 2 trainer ID (default 0x0000)
    0x4 12 Trainer name (GB encoding, 0x50 terminated + 1 byte padding 0x00) (default 0x5000)
 
0x10 Start of party pkmn, 0x3C each; uses a similar GB storage format:
    0x00 1 species
    0x01 1 held item
    0x02 1 move 1
    0x03 1 move 2
    0x04 1 move 3
    0x05 1 move 4
    0x06 2 OT ID
   *0x08 4 EXP (1 byte extra padding)
    0x0C 2 HP exp
    0x0E 2 Atk exp
    0x10 2 Def exp
    0x12 2 Spd exp
    0x14 2 Spc exp
    0x16 2 DVs
    0x18 1 PP 1
    0x19 1 PP 2
    0x1A 1 PP 3
    0x1B 1 PP 4
    0x1C 1 friendship
    0x1D 1 level
    0x1E 1 status condition? (status is set to 0 when the pokémon is registered) (might be padding)
    0x20 2 Caught data
    0x22 1 Padding? (might be status condition)
   *0x24 12 nickname (GB encoding, 0x50 terminated) (1 extra padding byte)
   *0x30 12 OT name? (GB encoding, 0x50 terminated; 0 if rental) (1 extra padding byte)
	0x178 2 Padding? always 0x0000

0x17A 4 Footer 'P3v0' (0x50337630)
0x17E 2 Checksum (UByte 8 bit) Computes Header + party data + footer
	

 

Download (source code included):
stadium2dumper.zip

Edited by suloku
  • Like 2
  • Proud 1
Link to post
Share on other sites
  • 2 weeks later...

So, I thought I should tell everyone that at first the program worked for me after using a Neo N64 Myth Cart to backup my cartridge save and then using ED64-SaveSwap on the save, however that was once and now any new backups I make from the cartridge don't work with the program, even after using SaveSwap.  Personally, I don't care since I have a fully-compatible backup of the save already, but I just thought I'd mention this oddity.

Link to post
Share on other sites
11 hours ago, Aquaaquaaqua said:

So, I thought I should tell everyone that at first the program worked for me after using a Neo N64 Myth Cart to backup my cartridge save and then using ED64-SaveSwap on the save, however that was once and now any new backups I make from the cartridge don't work with the program, even after using SaveSwap.  Personally, I don't care since I have a fully-compatible backup of the save already, but I just thought I'd mention this oddity.

Send me the saves via PM and I'll check them and see what's wrong.

Link to post
Share on other sites
  • 1 month later...
  • 4 months later...

Just for the record, as I told suloku via pm, the reason I felt the need to redump my save after I had a save dump that was converted to successful compatibility with the Pokemon dumper, was mainly because of OCD.

 

And oh yeah I did in the end send suloku a fresh save dump, (that wasn't saveswapped,) but, whatever.

Link to post
Share on other sites
  • 1 year later...

Apologies to the moderators if bumping a two year thread violates any rules, but I’m interested in any work being done in this area and may be able to contribute.

Thanks suloku for this excellent research. Could anyone who has edited N64 save files provide some additional info on:

  • Endianness of bytes (big/little)
  • Other checksums across the data sections that would need to be updated when the regions detailed above are modified.
  • Any other relevant research or implementations of dumping or editing the registered Pokemon (or other Pokemon stadium save data) including different formats and lengths of save files.

It would cool to see a fully fledged Pokemon Stadium 2 editor, and this provides a great starting point for any developers. Thanks.

  • Like 2
Link to post
Share on other sites

I've been running some tests and I did find out stadium 2 does not store the full pokémon name, either when boxing or when registering. I've updated the first post with this information.

It seems Stadium 2 never actually stores the "full" name of the pokémon. The name is stored only until a 0x50 terminator is found. This makes depositing the pokémon in stadium 2 become "nicknamed". This doesn't make any actual different, but on the GBC games this wouldn't happen as the game seems to keep copying the bytes 0x50 bytes. Because pokémon data in boxes in the GB games isn't actually erased but rather the box slot is marked as "empty" when you withdraw/release/move pokémon, there is a chance that when you return the pokémon to GB the pokémon will become unnicknamed again, depending on the pokémon that was stored in that slot before. If one would like to return the pokémon to the "unnicknamed status", the easiest would be to have an unnicknamed evolved or wild muk stored in the box (because it's the shortest pokémon name and all chars besides the first 3 will be 0x50). Then put a pokémon from stadium 2 into that box in the same slot muk was. This is easiest done in a box with 19 pokemon, deposit muk in GB, withdraw, then deposit pokémon from stadium 2 into that box.
This doesn't happen with OT name, it is fully copied. I suspect this is done so only full OT name + ID can nicnkame a pokémon (the pokémon being marked as nicknamed or not is irrelevant).
Thus, this finding makes any registered pokémon recovered with the dumper be actually the same one you had back in your day.

Also, friendship is reset, as if the pokémon was traded.

Edited by suloku
Link to post
Share on other sites
  • 6 months later...

Posting my finding on the offset locations for the Registered Teams. It seem that the Japanese version (G&S) only has 10 registered teams per mode, which puts it in line with the English version. Also will be clarifying the Pokemon data structure. First the is a registered team is made using OP's post as a base:

Registered parties are 0x180 each: 0x10 header + six 0x3C pokemon structures + 2 bytes padding, 4 byte footer and 2 byte checksum

0x00 Header:
    0x0 1 True if team is present (0x00 if no team)
    0x1 1 Padding 0x00
    0x2 2 trainer ID (default 0x0000)
    *0x4 12 Trainer name (GB encoding, 0x50 terminated + remaining byte padding 0x00) (default 0x5000)
 
0x10 Start of party pkmn, 0x3C each; uses a similar GB storage format:

    0x00 Index number of the Species 				1 byte
    0x01 Held Item 						1 byte
    0x02 Move 1							1 byte
    0x03 Move 2							1 byte
    0x04 Move 3							1 byte
    0x05 Move 4							1 byte
    0x06 OT ID							2 bytes
   *0x08 Experience points (1 byte 				4 bytes
   	 extra padding at beginning)		
    0x0C HP EV Data						2 bytes
    0x0E Atk EV Data						2 bytes
    0x10 Def EV Data						2 bytes
    0x12 Spd EV Data						2 bytes
    0x14 Spc EV Data						2 bytes
    0x16 DV Data						2 bytes
    0x18 Move 1's PP values  					1 byte
    0x19 Move 2's PP values  					1 byte
    0x1A Move 3's PP values  					1 byte
    0x1B Move 4's PP values  					1 byte
    0x1C Friendship or Hatch Counter				1 byte
    0x1D Level							1 byte
    0x1E Status**    						2 bytes
    0x20 Pokérus Status						1 byte
    0x21 Caught data						2 bytes
    0x23 1 Padding?
   *0x24 Nickname (GB encoding, 0x50				12 bytes 
   		 terminated) (1 extra padding byte) 
   *0x30 OT name (GB encoding, 0x50 				12 bytes
   		 terminated; 0 if rental) 
         	 (4 extra padding bytes)
	

0x17A Footer 'P3v0' (0x50337630) 				4 bytes
0x17E Checksum (UByte 8 bit) 					2 bytes
	  Computes Header + party data + footer

*If OT Name is short, 0x50 is used to fill in rest (7 characters total in English, 5 in Japanese).

*Japanese version is mostly the same except for one difference in Nickname and OT Name. 
Nicknames can only be 5 bytes, has 0x50 terminator, and 6 bytes of padding. Same with OT names. Both will have 0x00 padding to fill 12 bytes each.

**Status is set to 0 when the pokémon is registered or in box. Set to 1 if Egg in box. Set to 4 if it is a rental Pokemon in a registered team. There is one byte padding afterwards.

Thanks to @Kaphotics for figuring out which byte Pokérus is. For example, D1 = strain 13 and 1 day left.

Use this chart for English Nickname and OT: https://bulbapedia.bulbagarden.net/wiki/Character_encoding_in_Generation_II#English

For Japanese Nickname and OT: https://bulbapedia.bulbagarden.net/wiki/Character_encoding_in_Generation_II#Japanese

Here are the locations for the Registered Teams (same in English and Japanese versions):

	Anything Goes	Little Cup	Poké Cup	Prime Cup	Gym Leader Castle	Vs. Rival
01	0x0		0xF00		0x1E00		0x2D00		0x4000			0x4F00
02	0x180		0x1080		0x1F80		0x2E80		0x4180			0x5080
03	0x300		0x1200		0x2100		0x3000		0x4300			0x5200
04	0x480		0x1380		0x2280		0x3180		0x4480			0x5380
05	0x600		0x1500		0x2400		0x3300		0x4600			0x5500
06	0x780		0x1680		0x2580		0x3480		0x4780			0x5680
07	0x900		0x1800		0x2700		0x3600		0x4900			0x5800
08	0xA80		0x1980		0x2880		0x3780		0x4A80			0x5980
09	0xC00		0x1B00		0x2A00		0x3900		0x4C00			0x5B00
10	0xD80		0x1C80		0x2B80		0x3A80		0x4D80			0x5C80

Offsets for Boxes in Professor Oak's Lab:

English boxes are 0x4D8 each:   Japanese boxes are 0x750 each:

Box 1	0x5E00			 Box 1	 0x5E00
Box 2	0x8000			 Box 2	 0x8000
Box 3	0x84D8			 Box 3	 0x8750
Box 4	0x89B0			 Box 4	 0x8E60
Box 5	0x8E88			 Box 5	 0x9590
Box 6	0x9360			 Box 6	 0x9CC0
Box 7	0x9838			 Box 7	 0xA3F0
Box 8	0x9D10			 Box 8	 0xAB20
Box 9	0xA1E8			 Box 9	 0xB250
Box 10	0xA6C0				
Box 11	0xAB98				
Box 12	0xB070				
Box 13	0xB548				
Box 14	0xBA20				

 

Edited by ShadowMario3
More info about status.
Link to post
Share on other sites

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...