This page describes the save file format of Battle For Bikini Bottom.
Structure
TODO: Describe endianness and asset order, add movie and incredibles
If you're editing a GCI file (exported from Dolphin -> Memory Card Manager), it'll begin with the GCI header and banner + icon data. Assuming that's all still the default, the actual save file data should start at offset 0x5880.
Starting at 0x5880 are 0x200 null (0) bytes, then the string "SPONGEBOB:WHENROBOTSATTACK::RyanNeilDan", followed by 0x599 more null bytes.
The rest of the save file is structured in blocks, which have the following format:
Offset |
Size |
Type |
Name
|
0x0 |
4 |
int |
ID - (GDAT, LEDR, ROOM, etc.)
|
0x4 |
4 |
int |
BlockSize - Size of the whole block, which includes padding bytes for alignment
|
0x8 |
4 |
int |
BytesUsed Number of bytes actually used
|
Blocks are padded with the value 0xBF
.
Block Types
GDAT
Offset |
Size |
Type |
Name
|
0xC |
4 |
int |
Checksum (CRC-32 MPEG2)
|
BlockSize is always 1. Checksum is calculated using the same CRC-32 algorithm as the checksum for asset data in HIP archives on the rest of the save file, starting from the LEDR block. The checksum HAS to be correct or else you'll get a "load failed!" screen. You can also use an AR code to disable the checksum validation altogether (GameCube NTSC version only):
0403ed94 60000000
Use that and you can freely edit your save file without worrying about the checksum
LEDR
Offset |
Size |
Type |
Name
|
0xC |
64 |
char[] |
gameLabel - The text to show for the save file. Bikini Bottom, Jellyfish Fields, etc.
|
0x4C |
4 |
int |
progress - The percentage of the game completed. Can be negative :O
|
0x54 |
8 |
long |
gametime - Always null
|
0x5C |
1 |
byte |
thumbIconIdx - Which game thumbnail to show on the right
- 0 - Bikini Bottom
- 1 - Jellyfish Fields
- 2 - Downtown Bikini Bottom
- 3 - Goo Lagoon
- 4 - Poseidome
- 5 - Rock Bottom
- 6 - Mermalair
- 7 - Sand Mountain
- 8 - Industrial Park
- 9 - Kelp Forest
- 10 - Flying Dutchman's Graveyard
- 11 - SpongeBob's Dream
- 12 - Chum Bucket Lab
13 or higher - Bikini Bottom
|
0x64 |
22 |
char[] |
"--TakeMeToYourLeader--"
|
ROOM
Offset |
Size |
Type |
Name
|
0xC |
4 |
int |
SceneId - The last level you were in
|
PREF
Offset |
Size |
Type |
Name
|
0xC |
4 |
int |
SoundMode - (0 = mono, 1 = stereo)
|
0x10 |
4 |
float |
MusicVolume - (0-1)
|
0x14 |
4 |
float |
SFXVolume - (0-1)
|
0x18 |
4 |
int |
Rumble - (0 = off, 1 = on)
|
SVID
Offset |
Size |
Type |
Name
|
0xC |
4 |
int |
version
|
Other Blocks
After this are a bunch of blocks containing "serialized" data, which are the game's way of storing persistent scene states as well as player and global counter states. The format is not easily editable with a hex editor because values are sometimes written as single bits, which offsets future values by the number of bits written, which causes values to cross byte boundaries.
Scene IDs
These can be B101, DB01, JF01, etc.
Contains saved states for every object with the persistent flag set. Scene ID blocks start with following structure:
Size (Bits) |
Type |
Name
|
1 |
Bit |
Unknown - Always set?
|
32 |
float |
offsetx
|
32 |
float |
offsety
|
All xBase assets commence with an initial "Enabled" bit. xEnt assets store an additional "IsVisible" bit. After that, asset-specific bits, including the final value of a counter, the pressed or unpressed state of a button, the status of a taskbox etc. follow.
- Trigger
Type (Size)
|
Bit (1) |
Bit (1)
|
Name
|
Enabled |
Visible
|
- Pickup
Type (Size)
|
Bit (1) |
Bit (1) |
Bit (7) |
Bit (1)
|
Name
|
Enabled |
Visible |
State |
Flag
|
- Env
Type (Size)
|
Bit (1)
|
Name
|
Enabled
|
- Platform
Type (Size)
|
Bit (1) |
Bit (1)
|
Name
|
Enabled |
Visible
|
- Camera
Type (Size)
|
Bit (1)
|
Name
|
Enabled
|
- Static
Type (Size)
|
Bit (1) |
Bit (1)
|
Name
|
Enabled |
Visible
|
- Movepoint
Type (Size)
|
Bit (1)
|
Name
|
Enabled
|
- Timer
Type (Size)
|
Bit (1) |
Byte (8) |
Float (32)
|
Name
|
Enabled |
State |
SecondsLeft
|
- Portal
Type (Size)
|
Bit (1)
|
Name
|
Enabled
|
- Group
Type (Size)
|
Bit (1)
|
Name
|
Enabled
|
- Pendulum
Type (Size)
|
Bit (1) |
Bit (1)
|
Name
|
Enabled |
Visible
|
- SFX
Type (Size)
|
Bit (1)
|
Name
|
Enabled
|
- Counter
Type (Size)
|
Bit (1) |
Byte (8) |
Short (16)
|
Name
|
Enabled |
State |
Count
|
- Hangable
Type (Size)
|
Bit (1) |
Bit (1)
|
Name
|
Enabled |
Visible
|
- Button
Type (Size)
|
Bit (1) |
Bit (1) |
Bit (1) |
Bit (1)
|
Name
|
Enabled |
Visible |
Unknown |
Unknown
|
- Surface
Type (Size)
|
Bit (1)
|
Name
|
Enabled
|
- DestructObj
Type (Size)
|
Bit (1) |
Bit (1) |
Int (32)
|
Name
|
Enabled |
Visible |
State
|
- Gust
Type (Size)
|
Bit (1)
|
Name
|
Enabled
|
- Volume
Type (Size)
|
Bit (1)
|
Name
|
Enabled
|
- Dispatcher
Type (Size)
|
Bit (1)
|
Name
|
Enabled
|
- Cond
Type (Size)
|
Bit (1)
|
Name
|
Enabled
|
- UI
Type (Size)
|
Bit (1) |
Bit (1)
|
Name
|
Enabled |
Visible
|
- UIFont
Type (Size)
|
Bit (1) |
Bit (1)
|
Name
|
Enabled |
Visible
|
- Fog
Type (Size)
|
Bit (1)
|
Name
|
Enabled
|
- Light
Type (Size)
|
Bit (1)
|
Name
|
Enabled
|
- EGenerator
Type (Size)
|
Bit (1) |
Bit (1)
|
Name
|
Enabled |
Visible
|
- Script
Type (Size)
|
Bit (1)
|
Name
|
Enabled
|
- TeleportBox
Type (Size)
|
Bit (1) |
Bit (1) |
Bit (1) |
Int (32)
|
Name
|
Enabled |
Visible |
Status |
currPlrState
|
- Taskbox
Type (Size)
|
Byte (8)
|
Name
|
State
|
Description
|
- 0 = BEGIN
- 1 = DESCRIPTION
- 2 = REMINDER
- 3 = SUCCESS
- 4 = FAILURE
- 5 = END
|
- Taxi
Type (Size)
|
Bit (1)
|
Name
|
Enabled
|
- CameraFly
Type (Size)
|
Bit (1)
|
Name
|
Enabled
|
- CameraTweak
Type (Size)
|
Bit (1)
|
Name
|
Enabled
|
PLYR
Size (Bits) |
Type |
Description
|
1 |
bit |
Unknown - Always set?
|
32 |
int |
Max Health - (1 - 6, more is possible but only 6 underwear's will show up)
|
32 |
int |
Current Character - (0 = Spongebob, 1 = Patrick, 2 = Sandy)
|
32 |
int |
Total Shinies
|
32 |
int |
Total Spatulas
|
8 |
bool |
Bubble Bowl
|
8 |
bool |
Cruise Bubble
|
960 |
int[15][2] |
Sock and Pickup count for every level.
|
32 |
int |
Total Socks
|
14 |
bit[14] |
Played - Array storing a bit for every cutscene you already played (Unused/Scooby leftover?)
|
6 |
bit[6] |
Unknown - Array storing an unknown "idiot level" persistent oob state bit
|
CNTR
Size (Bits) |
Type |
Description
|
1 |
bit |
Unknown - Always set?
|
1600 |
short[15][] |
2D Array for every pause menu spatula level counter value (mnu5). Counter values:
- 0 = Locked (?)
- 1 = Unlocked (Gray Spatula)
- 2 = Collected (Golden Spatula)
Array size for each level:
- 0 (HB) = 8
- 1 (JF) = 8
- 2 (BB) = 8
- 3 (GL) = 8
- 4 (B101) = 1
- 5 (RB) = 8
- 6 (BC) = 8
- 7 (SM) = 8
- 8 (B201) = 1
- 9 (KF) = 8
- 10 (GY) = 8
- 11 (DB) = 8
- 12 (B30x) = 2
- 13 (Pat Socks) = 8
- 14 (Krabs Spatula) = 8
|
240 |
short[15] |
Robot counter values in mnu5 (HB09 ROBOT COUNTER XX)
|
16 |
short |
"REMINDER_SOCK_CNTR" counter value in mnu5
|
32 |
int |
g_flg_chEnabled
|
SFIL
Offset |
Size |
Type |
Name
|
0xC |
8 |
char[] |
"RyanNeil"
|
0x14 |
? |
byte[] |
Padding
|