Sonic Advance 3/ROM Editing

From Sonic Retro

SCHG: Sonic Advance 3
Main Article
ROM Portion
Editing the ROM
Sound Test
Sound and Music List
Dynamic Text
Hidden Level Select Menu
Special Stages
RAM Portion
RAM Editing
See Also
SCHG How-to topics
Sonic Advance PalTool
Sonic Advance 3 Tools


Huh! Don't you know what is sprite? Sprites are all visible objects that stay over a background. That includes certain texts (level titles, copyright info at title screen) and some "bars". Anything that appears at VBA's OAM Viewer should be treated as sprite. Though changing a sprite doesn't basically mean changing colors. An image is made of colors in a certain order - sprite data is the order. ||||||| That bunch of lines can have multiple or no meaning for you, but + these two line crossed you know perfectly that they are an add signal. What sprite data does is reference colors in a palette.

Main Sprite Pointer: 0x00000404

The pointer above points to 0x003314F0, where there are five pointers. In SA3, a sprite is made of five parts: three mappings, palette and its data. After these five pointers, you'll see two more, but I don't know what they handle.

Sprite data

The GBA sprite data format is very simple. It uses 1 byte for every 2 pixels. Each nybble (half-byte) points to a color on palette (from 0 to F, the palette has 16 colors). Just for the hell of it the pixels are reversed! So F0 must be treated as 0F for visualisation purposes. That is, before mounting an image, reverse the nybbles. Images are formed by a bunch of tiles, and tiles are nothing more than a block of 8x8 pixels. If you're good at math you'll soon find out that each tile occupies 32 (20h) bytes. For 1-tile (8x8) images, you won't have too much work to edit them. But for 2 tiles or more you can never tell if the next 32 bytes are from the upper or lower part of the image, and that's for what mappings exist. You can see tiles using Tile Layer Pro. And before I forget it, references to the first color (0) of the palette result in transparency.

Location: 0x0076CB1C
Pointer: 0x00331500

In TLP, put the location address in Go To dialog, so you will be able to see the tiles correctly.

Sprite mappings

Do you remember what I said above about mounting images with more than 1 tile? Well, the part that handles the tile arrangement is called mapping. Sprite mappings indicate which, how many and where the tiles appear, and which palette to use.

Location 0: 0x0032FD20
Location 1: 0x00E2C3A8
Location 2: 0x003524FC
Pointer 0: 0x003314F0
Pointer 1: 0x003314F4
Pointer 2: 0x003314F8

There are three kinds of mappings. If you look at their locations, you'll find more pointers! They refer to the sprite states, and follow the respective topic at RAM section. The first pointer is Sonic standing on foot (sprite state 0), the second is Sonic looking up (sprite state 1)... maybe I change the name of this topic to "Animation mapping". Mapping 0 is directly linked to the sprite that is shown. There, I figured how to change the palette index used, which sprite to use and the time of frames, but there are more things to figure out. You can use SA3 Pointer Reader (Downloads) to get pointers from any sprite state. Palettes

Palettes are sets of colors that are used by a certain sprite state. They are "called" through hidden flags (indexing), which change in groups of 16 colors. In mapping 0, it's possible to load more or less than 16 colors, but the first color to load will be the first color of the palette that corresponds to the index requested, due to the programming style that is used in the game. According to my calculations, there's a total of 551 palette indexes (0 to 226h).

Location: 0x00353CCC
Pointer: 0x003314FC

Each color is formed with 2 bytes, and each index has 16 colors, which means that the difference from an index to the next is 32 (20h) bytes. That is, index 0 means to load starting at 0x00353CCC; index 1, start at 0x00353CEC... and so. Click here to know how to edit GBA palette.

You may have noticed that different char states from Sonic use different palette indexes, and all of them are equal! Sonic Team seems to have never noticed it before. This "feature" is annoying while editing Sonic palette to something else... but, hey! Games weren't supposed to be hacked! ;).

Sound Test

General sprite selections

0x000D729C: selection for Play button - 8 bytes for each language.
0x000D72CC: selection for Back/Stop button - 8/8 bytes.
0x000D7334: selection for Sound Test title for each language - 8 bytes.
0x000D7364: selection for numbers - 8 bytes.
0x000D73B4: selection for double arrows - 8 bytes. (btw, the sprite is doubled here, there's no double arrow sprite state)
8 bytes format: XXXX YYYY ZZZZ ZZZZ
XXXX: sprite state (see Sprites)
YYYY: sprite sub-state - determines which part of sprite state is used. All the sprites are stored (programmed) in form of states, hence it can contain various frames.
ZZZZ ZZZZ: unknown function.

The double arrow is a perfect guinea pig for seeing which animation a sprite state does.

Music titles

0x000D73BC: beginning of music titles - ASCII.

Let's start with the first one. Before "OPENING", there's a byte which its value is 7. As you may already guessed, this byte tells the game how many letters must be read.

To edit a title, you can use ASCII chars, with some rules:

Characters that appear correctly:

  • Upper/Lower case letters;
  • Numbers from 1 to 9;
  •  !@#$%&*()?.:-/_[]{} (these are the ones I've tested);

Special notes:

  • Using 0 (30h) results in space;
  • Char ] (5Dh) appears with wrong palette, probably it's from an error at its mapping. The beta version also has this bug.

Music assignments

Location: 0x000D71A4

2 bytes per title. The value corresponds to music pointer order. See Musics section for more details.

Dynamic text

Dynamic text is nothing more than the kind of text that is shown in order to send a message to the player. The most common source of that are the Omochaos. This includes the beginning text ("The evil scientist, Dr. Eggman!..") and that one from Nonaggression ("Sonic! Use your tag action..."). Here's a sample:

Sa3 dynamictext.png

Well, "dynamic text" could be the incorrect designation, but who discovered how to edit this shit anyway? Get used to my terminology and don't argue! ;)

0x00E7E6E8: Pointer to Japanese set
0x00E7E6EC: Pointer to English set
0x00E7E6F0: Pointer to German set
0x00E7E6F4: Pointer to French set
0x00E7E6F8: Pointer to Spanish set
0x00E7E6FC: Pointer to Italian set

Theoretically, each language has 256 dynamic texts. That means in the location given by the language pointer there are 256 pointers, one after other, which belong to text data. However only 1/4 (64) of them points to "real" data. The remaining 192 pointers can be used to assemble new texts, because there is a space of 1.5 MB at the end of the file that Sonic Team kindly left for us! :)

Text format

It isn't that hard to understand. Each character uses 2 bytes. Its value minus 92h is equal to its ASCII value. Be aware when you come across the following values:

0151h: Spacer. The next 2 bytes tell the size of the space.
0152h: Timer. The next 2 bytes tell the speed which letters appear.
0153h: Line break (lik when you press ENTER in a text editor).
0154h: Button request. Player must press A button to see the rest of the text.
0155h: EOT (end of text).

The useless pointers do nothing more than point to a bunch of 0155h. Numbers follow a different pattern. If x (char value) is >= C8h and <= D1h, the formula should be: ASCII = x - 98h instead of ASCII = x- 92h.


In this page you will find information on how to change the zones of the game. Whenever I use the expression "scene" (with quotation marks), I will be referring to the value that Chao's Playground, Sonic Factory, Route 99 Map, etc, uses as scene flag. Go to RAM section to know more.

Main zone pointers

Group 1: 0x000D0624 - artwork
Group 2: 0x000D0D78 - collision

Each pointer of Group 2 corresponds to a "scene" of the game, according to its flags. On the side of Group 1, there are four pointers per "scene". First, I will explain how Group 1 works. We will use "scene" 03 (Zone 1 Act 1) as example:


The selected part corresponds to the pointers of that "scene". The first pointer (0838B590) is for BG1. The second is for BG2 (0838B5B4). Each one of them points to a sequence of 36 (24h) bytes, that contain the information about layout and palette for those BG's. The last two pointers (085DC428 and 085DEA24) point each one to a sequence of 28 (1Ch) bytes, that have to do with the image of 256 colors that is exhibited in BG's 0 and 3. If you still don't know why I call everything like BG, go into the menu Tools->Map Viewer of VBA and guess it :P.

Let's see how are the 36 bytes pointed by the first two pointers:


The byte marked with black is the beginning of the block. The pointer marked with blue tells where are the tiles. A tile is a block of 64 pixels (8x8). The pointer marked with yellow says where is the palette. There are 256 colors (512 bytes). In this case, only the second pointer (from BG2) is relevant, because, somehow, it overwrites the first. The pointer marked in red refers to the mappings of 96x96 blocks that form the layout. Last, the green pointer indicates where is the layout (1 byte per block).

Now let's see the 28 bytes from BG0 of this same "scene" to know how he game handles images of 256 colors:


Did you notice that I used the same colors of the previous picture? What is marked with black, blue and yellow has the same function that was attributed to those colors previously. The pointer in red, however, has the function of mapping and layout. The format of BG3 is the same.

Time to talk about Group 2, collision. Again from "scene" 03, it's at 0x0038B5D8. 6 pointers, plus 12 bytes.


I didn't mess a lot with the pointers, for now I cannot give more details on how they work.

Pointers for background movement and deformation : 0x000D0344. Two pointers per "scene".
Character starting position: 0x000D13C8. One pointer per "scene". Each one points to 4 bytes: 2 bytes for horizontal position and 2 for vertical.
Ring layout pointers: 0x000CEBD0. One pointer per "scene". The format wasn't figured out yet.
Object layout pointers: 0x000D0E9C. One pointer per "scene".


This section is about musics and sound effects in game, how to handle them and other things. I discovered the playlist for maps and acts. But first you need to know how musics are ordered. The Sound Test list has nothing to do with the real order. They are referenced by their order at sound pointer list. Well, let's find out what is this...

Sound pointer list

Believe it or not, I found this by chance. It starts at 0x000EB420. Each sound uses 4 bytes for the location, plus 4 more bytes for unknown function. Any pointer that points to 0x000ECE08 (080ECE08) results in no sound. Fortunately SA1 and SA2 uses the same system, so music importing/exporting is possible. However, copy & paste isn't enough. The music itself has pointers at the end of its blocks that need to be changed to fit in the new game (only "loopable" musics).

Musics and sound effects are in this list.

Music structure

The location given by the sound pointer holds a header made of 8 bytes. The first 2 bytes tells how many parts the music is formed, and consequently, how many pointers must be read after the header. The next 2 bytes have unknown function. The last 4 are the pointer for instrument definition, something like a sound driver maybe, it's essential however. Let's take music 01 (title) as example. It's at 0x00264A9C. At the header, you'll fin the following bytes, in this order: 06 00 14 80 F8 51 0E 08. The first 2 bytes indicate that there are 6 blocks used to form this music. So, after the last byte of the header, the game should read 6 groups of 4 bytes (6 pointers). Notice that those block pointers point to data behind the header. As the first block pointer is which points to the far away data (pointer with smaller value), then when you want to copy the whole music to other ROM or other place, begin to copy starting from the location of that pointer until the last byte of the last block pointer. After that, change the block pointers in accordance where the music is now. Example: the header was in CE0h, and now it is in 33450h. What you should have to do is to add 32770h (the difference between 33450h and CE0h) to the value of the block pointers.

In the case of music that loops, it has other pointers that are in the end of each block. Those pointers should also be adjusted as the block pointers.

But that is not everything that forms the music. When passing from a game to other, it is necessary to modify the pointer of the instrument definition, or the sound won't play, the game will crash, that kind of thing. Importing instrument definition from a game to another is what I have yet to discover.

Normal zones playlist

Location: 0x000CE1A4
Pointer: 0x00002638

1 byte per act. Zone 1 - Map; Zone 1 Act 1; Zone 1 Act 2... and then until Zone 7 Act 3. The byte value follows the order which musics have at sound pointer list. 00 = opening, 01 = title and so.

Hidden level select menu

First of all, you won't learn here how to access this menu, simply because neither I don't know how! Second, I decided to create this subsection because there are evidences of this being a "working" menu, so I'll put everything I discovered about it here.


0x000D6B98: Text 1: SELECT STAGE
0x000D6BA8: Text 2: ZONE %d ACT %d
0x000D6BB8: Text 3: PRESS A BTN
0x000D6BC4: Text 4: PLEASE WAIT
0x0008E8E8: Pointer to Text 1
0x0008E8F0: Pointer to Text 2
0x0008E8F4: Pointer to Text 3
0x0008E928: Pointer to Text 4

These texts are in the beta, and there are more! Those "extra" texts from beta refer to the ability of choosing between tag and single play. Why did they changed something that doesn't work? Well, that could mean that it works!

Level pointers

Location: 0x000D6B80
Pointer: 0x0008E724

Each byte corresponds to an act. Zone 1 Act 1, Zone 1 Act 2... until Zone 7 Act 3. Those are the values used by the scene flag. Read about scene flag at RAM section.

Special Stages

This section concerns about editing things in Special Stages.

Rings for checkpoints

Location: 0x000DBAF6

2 bytes Little-Endian values. SS1 - first checkpoint, SS1 - last checkpoint, SS2 - first checkpoint... and then until SS7 - last checkpoint. Change these values to make the stages easier/harder to pass.