SNES Development
Super Adventure Island

Super Adventure Island / Takahashi Meijin no Daibōken Jima

Offsets refer to the US version of the ROM.

Level Format

Level Pointers

Offset 0x33F82 contains a table of pointers to level and cutscene headers. Each entry in the table is 4 bytes long: a 24-bit pointer followed by an additional byte which denotes the type of header (whether it is an actual level or one of the mode 7 cutscenes.)

There are (I think) a total of 60 entries:

entry 0 - opening scene (falling from sky)
entry 1 - level 1-1
entry 2 - level 1-1's midway point 
entry 3 - level 1-2
...

Level Headers

The game stores 2 separate headers for each level - one for the start, and one for the midway point. The two headers are mostly identical except for the player start point (though theoretically you could abuse this to make the player start in a totally different area if they die past the level’s midway point).

The byte following each pointer has these known bit values:

    0x02 - get flown in by the bird
    0x04 - skip level - weird. the game puts you at the level AFTER this one, with the correct sprites and numbering.
    0x08 - normal level
    0x80 - cutscene (using this for a normal level will just make the HUD and player vanish and you can't move or do anything)

Headers are 0x6A (?) bytes long. The headers are pretty large and I hadn’t bothered figuring out what each individual field does, but a bunch of them are probably pointers to graphics data etc.

Header OffsetSizeDescription
0x001Written to BG mode register and $0BFD
Should almost always be 0x79 except for cutscenes.
0x011Written to $0C97
0x022Written to $0C8D
0x042Written to $0C8F
0x062Written to $0C85
0x082Written to $0C87
0x0A2Written to $0C89
0x0C2Written to $0C8B
0x0E2Written to $009A
0x101Written to $0C83
0x112Pointer to HDMA Table?
Written to $0D80
0x131Written to $0D82
0x142Written to $0DC0
Pointer to ???
0x161Written to $0DC2
0x172Written to $0310
Pointer to ???
0x191Written to $0312
0x1A2Written to $0313
0x1C1Written to $0315
0x1D2Written to $0C29
0x1F2Written to $0C2F
0x211Width of Level (Screens)
Written to $0C18
0x221Height of Level (Screens)
Written to $0C19 and $0C2E
0x231Bank Number for Map Pointers
Written to $0C1C
0x242Pointer to Screen Indices
Written to $0C1D
0x262Pointer to Screen 0 Data
Written to $0C1F
0x282Pointer to Metatiles (Different part?)
Written to $0C21
0x2A2Pointer to Metatiles?
Written to $0C23
0x2C2Pointer to Tile Properties
Written to $0C25
0x2E2Written to $0C4A
0x302Written to $0C50
0x321Written to $0C3B and $0C49
Value * 8 is Written to $0C3D - $0C3E
0x331Written to $0C3C and $0C4F
0x341Written to $0C3F
0x352Written to $0C40
0x372Written to $0C42
0x392Written to $0C44
0x3B2Written to $0C46
0x3D2Written to $0C6B
0x3F2Written to $0C71
0x411Written to $0C5C and $0C6A
Value * 8 is written to $0C5E - $0C5F
0x421Written to $0C5D and $0C70
0x431Written to $0C60
0x442Written to $0C61
0x462Written to $0C63
0x482Written to $0C65
0x4A2Written to $0C67
0x4C3Pointer to some Compressed Data written to $0050
0x4F3Pointer to some Compressed Data written to $0050
0x523Pointer to some Compressed Data written to $0050
0x553Pointer to some Compressed Data written to $0050
0x583Pointer to some Compressed Data written to $0050
0x5B3Pointer to some Compressed Data written to $0050
0x5E3Pointer To some Compressed Data written to $0050

The format from 0x1D and up varies depending on if RAM address $0BFD == $07 aka Cutscene vs. Normal Level. The values listed are for standard levels.

Level Screen Data

Each screen is made up of 32x32 tiles. Number of horizontal + vertical screens defined in header. Screen 0 is pointed to in the header, subsequent screens are immediately after one another. Each screen is made up of 16bit tile numbers representing each 32x32 tile.

Level Screen Indices

The order in which these screens make up the level is stored in this list (pointed to in header again.) This is made up of mn bytes where the first ‘n’ make up the top row of the level, and the last ‘n’ do the same for the last half (for a total of m rows.) Level 1-1 takes place entirely on the bottom of the stage, so there are a bunch of 0s at the beginning for that stage (where screen 0 is totally blank.) You can use numbers more than once to make level geometry repeat itself if you’re lazy.

Compressed Graphics

Offset 0x356C6 has pointers to compressed data for player animation frames.
Offset 0x323F5 has pointers to compressed data for ??? (other stuff).

The graphics use some kind of RLE-like compression scheme that uses bit shifting to determine how many times to write each byte of compressed data.

How a tile is decompressed:

  1. Load the current byte of compressed data.
  2. Let x = 0.
  3. Do a bit shift left.
  4. Each time the carry bit is set, set x to the next byte of the compressed data.
  5. Write x to current memory offset, increment memory offset by two.

Do steps 2-5 a total of eight times per tile.

Examples:

Compressed:   80 FF
Decompressed: FF FF FF FF FF FF FF FF

Compressed:   0F 01 02 03 04
Decompressed: 00 00 00 00 01 02 03 04

Compressed:   CC FF 00 FF 00
Decompressed: FF 00 00 00 FF 00 00 00

Compressed:   00
Decompressed: 00 00 00 00 00 00 00 00

Compressed:   FF 01 02 03 04 05 06 07 08
Decompressed: 01 02 03 04 05 06 07 08

Decompression order at beginning of game, before showing title screen:

Even numbered bytes (00,02...) are loaded first, then odd numbered bytes are decompressed after.

07:8000 - 07:8054 -> 7F:2000
07:810D - 07:8165 -> 7F:2001

07:8055 - 07:80A6 -> 7F:2200
07:8166 - 07:81B1 -> 7F:2201

07:80A7 - 07:80DB -> 7F:2400
07:81B2 - 07:81DB -> 7F:2401

07:80DC - 07:810C -> 7F:2600
07:81DC - 07:8208 -> 7F:2601

etc...
7F:2000 - 7F:27FF are the first 4 horizontal rows of player sprite data in WRAM. 
Then the next 4 rows are loaded. Player sprites span most of the used space in bank 7

Graphics decompression routine begins at ROM address 03:AB23.

Variables:
$42 - byte of graphics data
$43 - byte being read in
$44 - bytes remaning in this item
$46 - number of items remaining to decompress for this row

--  lda    #$08
    sta    $44
    stz    $42
    lda    $0000, y    ; ex: for loading sprites, y = $8000 and bank number = 7
    sta    $43            
    iny
-   asl    $43         ; shift the byte left.
    bcs    +           ; if this results in an overflow, jump ahead
    lda    $42         ; otherwise load the byte already at $42 and store it again.
    bra    ++        
+   lda    $0000, y    ; on overflow: load the next byte, store it in $42
    sta    $42
    iny
++  sta    $7F2000,x
    inx
    inx
    dec    $44
    bne    -           ; if $44 is still positive, 
    dec    $46
    bne    --
7372

Level format, compression and graphic compression derived by Revenant. Started on 2011-03-06, last updated 2011-03-09, cleaned up on 2012-01-01.