This is a specification for the save file format used by Sonic 3 and Sonic 3 & Knuckles, released in 1994 for the Sega Genesis, and Sonic & Knuckles Collection, released in 1997 for Windows 95.
This document uses the following terms to represent data sizes, consistent with their use in the Motorola 68000 instruction set:
Term | Length | Range |
---|---|---|
BYTE | 1 byte (8 bits) | 0–255 |
WORD | 2 bytes (16 bits) | 0–65,535 |
LONG | 4 bytes (32 bits) | 0–4,294,967,295 |
All numbers are big endian integers, meaning that the word $00FF
has the decimal value of 255 and not 65,280.
Offset (Console) | Offset (PC) | Size | Description |
---|---|---|---|
$008 –$05B |
$000 –$053 |
84 bytes | Competition, section 1 |
$05E –$0B1 |
— | 84 bytes | Competition, section 2 |
$0B4 –$0E7 |
$0C0 –$0F3 |
52 bytes | Sonic 3 single-player, section 1 |
$0FA –$12D |
— | 52 bytes | Sonic 3 single-player, section 2 |
$140 –$193 |
$180 –$1D3 |
84 bytes | Sonic 3 & Knuckles single-player, section 1 |
$196 –$1E9 |
— | 84 bytes | Sonic 3 & Knuckles single-player, section 2 |
All other bytes are 0.
The save file is 512 bytes long and contains a section for competition times and sections for Sonic 3 or Sonic 3 & Knuckles single-player data (or both, though this is uncommon for emulator saves since emulators give each ROM a separate save file).
Each section contains its own checksum and is duplicated for redundancy. If the checksum for the first section fails, the game tries the second one. If the checksum for the second section fails, the game resets the save data to the default values.
Many emulators, including Gens and Kega Fusion, pad each byte to word-length in big-endian (Motorola) order (so the byte $FF
would be written as the word $00FF
).
This is the original PC release of the game from 1997. Its save file is named "sonic3k.bin" and located in the program directory. The file is always 1024 bytes in length.
The format is very similar to the console version, but without the duplication (each section only appears once), without the section checksums, and with different starting offsets for each section.
The competition section is 84 bytes long. The first section starts at $008
and the second starts at $05E
. Both sections should contain identical data unless data corruption has occurred.
Offset 1 (Console) | Offset 2 (Console) | Offset (PC) | Size | Description |
---|---|---|---|---|
$008 –$017 |
$05E –$06D |
$000 –$00F |
16 bytes | Azure Lake |
$018 –$027 |
$06E –$07D |
$010 –$01F |
16 bytes | Balloon Park |
$028 –$037 |
$07E –$08D |
$020 –$02F |
16 bytes | Desert Palace |
$038 –$047 |
$08E –$09D |
$030 –$03F |
16 bytes | Chrome Gadget |
$048 –$057 |
$09E –$0AD |
$040 –$04F |
16 bytes | Endless Mine |
$058 –$059 |
$0AE –$0AF |
$050 –$051 |
WORD | Always $4C44 |
$05A –$05B |
$0B0 –$0B1 |
— | WORD | Checksum |
Note that the order of Chrome Gadget and Desert Palace are transposed from their order in the game.
Each stage consists of:
Offset | Size | Description |
---|---|---|
$0 –$3 |
LONG | 1st place time |
$4 –$7 |
LONG | 2nd place time |
$8 –$B |
LONG | 3rd place time |
$C –$F |
LONG | Characters |
See Appendix A for a complete list of competition position offsets.
Each time long is structured as follows:
- First byte is
$80
if the position is empty,$00
otherwise. - Second byte is minutes.
- Third byte is seconds.
- Fourth byte is ticks.
For the PC version, the bytes are in the opposite order:
- First byte is ticks.
- Second byte is seconds.
- Third byte is minutes.
- Fourth byte is
$80
if the position is empty,$00
otherwise.
A tick in Sonic 3 is 1/100 of a second.
If the position is empty, all bytes except the first are set to $00
. Empty positions are surpassed by any time when the player completes a stage, regardless of the value of the other bytes.
The characters long is structured as follows:
- First byte is the character for the 1st place position.
- Second byte is the character for the 2nd place position.
- Third byte is the character for the 3rd place position.
- Fourth byte is always 0.
The character is $00
for Sonic, $01
for Tails, or $02
for Knuckles.
The default value is $00010200
if all positions are empty.
The Sonic 3 single-player section is 52 bytes long. The first section starts at $0B4
and the second starts at $0FA
. Both sections should contain identical data unless data corruption has occurred.
Offset 1 (Console) | Offset 2 (Console) | Offset (PC) | Size | Description |
---|---|---|---|---|
$0B4 –$0BB |
$0FA –$101 |
$0C0 –$0C7 |
8 bytes | Slot 1 |
$0BC –$0C3 |
$102 –$109 |
$0C8 –$0CF |
8 bytes | Slot 2 |
$0C4 –$0CB |
$10A –$111 |
$0D0 –$0D7 |
8 bytes | Slot 3 |
$0CC –$0D3 |
$112 –$119 |
$0D8 –$0DF |
8 bytes | Slot 4 |
$0D4 –$0DB |
$11A –$121 |
$0E0 –$0E7 |
8 bytes | Slot 5 |
$0DC –$0E3 |
$122 –$129 |
$0E8 –$0EF |
8 bytes | Slot 6 |
$0E4 –$0E5 |
$12A –$12B |
$0F0 –$0F1 |
WORD | Always $4244 |
$0E6 –$0E7 |
$12C –$12D |
— | WORD | Checksum |
Each slot is structured as follows:
Offset | Size | Description |
---|---|---|
$0 |
BYTE | New game |
$1 |
BYTE | Always 0 |
$2 |
BYTE | Character |
$3 |
BYTE | Current Zone |
$4 |
BYTE | Next Special Stage |
$5 |
BYTE | Number of emeralds |
$6 |
BYTE | Chaos Emeralds |
$7 |
BYTE | Giant Rings |
$80
if new, $00
otherwise.
All other bytes for the slot are $00
if set to new.
Value | Character |
---|---|
$00 |
Sonic & Tails |
$01 |
Sonic |
$02 |
Tails |
$02 |
Knuckles |
Knuckles displays properly on the Data Select screen, but selecting him results in playing as Sonic with some minor changes.
The current Zone or $07
for clear.
Value | Zone |
---|---|
$00 |
Angel Island |
$01 |
Hydrocity |
$02 |
Marble Garden |
$03 |
Carnival Night |
$04 |
Flying Battery |
$05 |
Ice Cap |
$06 |
Launch Base |
$07 |
Clear |
Flying Battery is selectable but missing most assets. It appears as "Zone 5" on the Data Select screen, the same as Ice Cap.
The next Special Stage to be played, between $00
and $06
. The game goes through all of the Special Stages in rotation, skipping the ones that have already been successfully completed. This value is set to $00
once the player has collected all of the Chaos Emeralds.
A value of $07
loads an unused Special Stage layout.
If the value corresponds to a Chaos Emerald that has already been collected or is greater than $07
, the value is ignored and the game loads the lowest uncompleted Special Stage instead.
The number of Chaos Emeralds collected, between $00
and $07
. If set to $07
, Special Stages can no longer be entered and super forms are enabled.
Tracks the Chaos Emeralds collected by the player. Each Chaos Emeralds is associated with a particular Special Stage, so they can be collected out of order if the player fails a Special Stage.
This is stored as a bit field of seven bits, each representing one Chaos Emerald:
1 1 1 1 1 1 1 0
| | | | | | |
| | | | | | Green
| | | | | Orange
| | | | Pink
| | | Purple
| | Grey
| Red
Blue
This can also be expressed as a sum of the powers of 2:
Emerald | Value |
---|---|
Green | 2 |
Orange | 4 |
Pink | 8 |
Purple | 16 |
Grey | 32 |
Red | 64 |
Blue | 128 |
A value of $FE
means that all of the Chaos Emeralds have been collected.
Tracks the Giant Rings collected in each Zone. Resets to $00
at the start of a new Zone or after the game has been completed.
This is stored as a bit field of seven bits, each representing one Giant Ring:
1 1 1 1 1 1 1 1
| | | | | | | |
| | | | | | | 1st Giant Ring
| | | | | | 2nd Giant Ring
| | | | | 3rd Giant Ring
| | | | 4th Giant Ring
| | | 5th Giant Ring
| | 6th Giant Ring
| 7th Giant Ring
8th Giant Ring
This can also be expressed as a sum of the powers of 2:
Giant Ring | Value |
---|---|
1st | 1 |
2nd | 2 |
3rd | 4 |
4th | 8 |
5th | 16 |
6th | 32 |
7th | 64 |
8th | 128 |
The Sonic 3 & Knuckles single-player section is 84 bytes long. The first section starts at $140
and the second starts at $196
. Both sections should contain identical data unless data corruption has occurred.
Offset 1 (Console) | Offset 2 (Console) | Offset (PC) | Size | Description |
---|---|---|---|---|
$140 –$149 |
$196 –$19F |
$180 –$189 |
10 bytes | Slot 1 |
$14A –$153 |
$1A0 –$1A9 |
$18A –$193 |
10 bytes | Slot 2 |
$154 –$15D |
$1AA –$1B3 |
$194 –$19D |
10 bytes | Slot 3 |
$15E –$167 |
$1B4 –$1BD |
$19E –$1A7 |
10 bytes | Slot 4 |
$168 –$171 |
$1BE –$1C7 |
$1A8 –$1B1 |
10 bytes | Slot 5 |
$172 –$17B |
$1C8 –$1D1 |
$1B2 –$1BB |
10 bytes | Slot 6 |
$17C –$185 |
$1D2 –$1DB |
$1BC –$1C5 |
10 bytes | Slot 7 |
$186 –$18F |
$1DC –$1E5 |
$1C6 –$1CF |
10 bytes | Slot 8 |
$190 –$191 |
$1E6 –$1E7 |
$1D0 –$1D9 |
WORD | Always $4244 |
$192 –$193 |
$1E8 –$1E9 |
— | WORD | Checksum |
Each slot is structured as follows:
Offset | Size | Description |
---|---|---|
$0 |
BYTE | New or clear value |
$1 |
BYTE | Always 0 |
$2 |
BYTE | Character, number of emeralds |
$3 |
BYTE | Current Zone |
$4 |
BYTE | Giant Rings |
$5 |
BYTE | Always 0 |
$6 –$7 |
WORD | Chaos Emeralds/Super Emeralds |
$8 |
BYTE | Lives |
$9 |
BYTE | Continues |
Value | Type |
---|---|
$80 |
New |
$00 |
Not cleared |
$01 |
Cleared without all Chaos Emeralds |
$02 |
Cleared with all Chaos Emeralds |
$03 |
Cleared with all Super Emeralds |
All other bytes for the slot are $00
if set to new.
The most significant digit is the character:
Value | Character |
---|---|
$00 |
Sonic & Tails |
$10 |
Sonic |
$20 |
Tails |
$30 |
Knuckles |
A value of $40
or higher result in the glitched "Blue Knuckles" character, which uses Knuckles' sprite with Sonic's color palette.
The least significant digit is the number of emeralds collected, between $00
and $07
. An emerald is counted as collected if it is a Chaos Emerald, in the Hidden Palace, or a Super Emerald. After all emeralds have been collected, this digit rolls back around to 0.
Value | Zone |
---|---|
$00 |
Angel Island |
$01 |
Hydrocity |
$02 |
Marble Garden |
$03 |
Carnival Night |
$04 |
Ice Cap |
$05 |
Launch Base |
$06 |
Mushroom Hill |
$07 |
Flying Battery |
$08 |
Sandopolis |
$09 |
Lava Reef |
$0A |
Hidden Palace |
$0B |
Sky Sanctuary |
$0C |
Death Egg |
$0D |
The Doomsday |
Note that the numbering differs from Sonic 3 after Carnival Night since Flying Battery is moved.
If the game is cleared, this value is set to the last Zone ($0B
for Knuckles, $0C
for Tails and Sonic with fewer than seven emeralds collected, and $0D
for Sonic with at least seven emeralds collected).
Tracks the Giant Rings collected in each Zone. See the Sonic 3 section for a full description.
Tracks the Chaos Emeralds and Super Emeralds collected by the player. Each emerald corresponds to a particular Special Stage.
This is stored in two separate bytes, each consisting of a bit field.
The first byte:
1 1 1 1 1 1 1 1
| | | | | | | |
| | | | | | | Purple Chaos Emerald
| | | | | | Purple Emerald in Hidden Palace
| | | | | Pink Chaos Emerald
| | | | Pink Emerald in Hidden Palace
| | | Orange Chaos Emerald
| | Orange Emerald in Hidden Palace
| Green Chaos Emerald
Green Emerald in Hidden Palace
The second byte:
1 1 1 1 1 1 0 0
| | | | | |
| | | | | |
| | | | | |
| | | | | Blue Chaos Emerald
| | | | Blue Emerald in Hidden Palace
| | | Red Chaos Emerald
| | Red Emerald in Hidden Palace
| Grey Chaos Emerald
Grey Emerald in Hidden Palace
Each emerald has two bits associated with it, representing one of four possible states:
Bits | State |
---|---|
%00 |
Not collected |
%01 |
Chaos Emerald collected |
%10 |
In Hidden Palace |
%11 |
Super Emerald collected |
A value of $FFFC
means that all of the Super Emeralds have been collected.
For the PC version, these bytes are in the opposite order (such that $FCFF
signifies that all Super Emeralds have been collected).
The number of lives, between 0 and 99 ($63
).
The number of continues, between 0 and 99 ($63
).
The original algorithm for calculating and verifying the checksum is implemented in Motorola 68000 assembly:
Create_SRAMChecksum:
moveq #0,d7
loc_C364:
move.w (a6)+,d5
eor.w d5,d7
lsr.w #1,d7
bcc.s loc_C370
eori.w #$8810,d7
loc_C370:
dbf d6,loc_C364
rts
The address register a6 points to the save data. The data register d7 holds the checksum and d6 is the counter.
We initialize the checksum to $00
and the counter to the length of the section in words, minus the checksum ($28
for the competition and Sonic 3 & Knuckles single-player sections, $18
for the Sonic 3 single-player section).
Then we iterate through the save data one word at a time and xor each word with the checksum. We then do a logical shift right, which saves the least significant bit before the shift as the carry bit. If the carry bit is 0, we xor the checksum with the value $8810
.
The algorithm implemented in JavaScript:
function calculateChecksum(bytes) {
const BIT_MASK = 0x8810;
let checksum = 0;
for (let i = 0; i < bytes.length - 2; i += 2) {
checksum ^= (bytes[i] << 8) | bytes[i + 1];
const carry = checksum & 1; // gets least significant bit before shift
checksum >>>= 1;
if (carry != 0) {
checksum ^= BIT_MASK;
}
}
return checksum;
}
The checksum is only used by the console version.
Offset (Console) | Offset (PC) | Size | Description |
---|---|---|---|
$008 –$00B |
$000 –$003 |
LONG | Azure Lake, 1st place time |
$00C –$00F |
$004 –$007 |
LONG | Azure Lake, 2nd place time |
$010 –$013 |
$008 –$00B |
LONG | Azure Lake, 3rd place time |
$014 –$017 |
$00C –$00F |
LONG | Azure Lake, characters |
$018 –$01B |
$010 –$013 |
LONG | Balloon Park, 1st place time |
$01C –$01F |
$014 –$017 |
LONG | Balloon Park, 2nd place time |
$020 –$023 |
$018 –$01B |
LONG | Balloon Park, 3rd place time |
$024 –$027 |
$01C –$01F |
LONG | Balloon Park, characters |
$028 –$02B |
$020 –$023 |
LONG | Desert Palace, 1st place time |
$02C –$02F |
$024 –$027 |
LONG | Desert Palace, 2nd place time |
$030 –$033 |
$028 –$02B |
LONG | Desert Palace, 3rd place time |
$034 –$037 |
$02C –$02F |
LONG | Desert Palace, characters |
$038 –$03B |
$030 –$033 |
LONG | Chrome Gadget, 1st place time |
$03C –$03F |
$034 –$037 |
LONG | Chrome Gadget, 2nd place time |
$040 –$043 |
$038 –$03B |
LONG | Chrome Gadget, 3rd place time |
$044 –$047 |
$03C –$03F |
LONG | Chrome Gadget, characters |
$048 –$04B |
$040 –$043 |
LONG | Endless Mine, 1st place time |
$04C –$04F |
$044 –$047 |
LONG | Endless Mine, 2nd place time |
$050 –$053 |
$048 –$04B |
LONG | Endless Mine, 3rd place time |
$054 –$057 |
$04C –$04F |
LONG | Endless Mine, characters |
- J.C. Fields jcfields@jcfields.dev
Checksum algorithm taken from Sonic Retro's Sonic 3 & Knuckles disassembly by Stealth and contributors.