-
Notifications
You must be signed in to change notification settings - Fork 37
Graphical Animations
Note: All addresses used here are for an unheadered ROM.
Each fat tileset has a list of up to 8 graphical animations. These animations involve "DMAing" 8x8 tiles into the SNES's VRAM at different points in time, which makes minitiles appear to change in every metatile they're used in. The set of tiles used for graphical animations for a given fat tileset are all stored together in one blob of 4bpp graphical data. ("Tile," in this context, refers to one specific layer of a minitile.) For simplicity, this will be referred to as "Tile Cluster". The clusters can contain up to 256 tiles for all animations.
Each graphical animation in the list contains the following information:
- Number of animation frames (stills) in the animation. "Frame count"
- Number of frames (measure of time) that each animation frame (still) displays on-screen. "Delay"
- Size of the graphical data (number of tiles * 32, since the map graphics are 4bpp; it is possible to replace many contiguous tiles in VRAM with one animation)
- Copy src (an offset into the fat tileset's animation graphics blob where all the animation frames for all tiles are stored)
- Copy dest (the VRAM address to copy tiles to)
CoilSnake and EB Project Editor currently do not have any built-in support for viewing/editing graphical animations.
In the ROM, there is a table of 32-bit pointers to the clusters of compressed animation graphics data at $EF11CB
. There is also a table of 32-bit pointers to lists of graphical animations at $EF121B
. A list's first byte is the number of entries; after that comes an array of graphical animations, each consisting of frame count (1 byte), delay (1 byte), size (2 bytes), src (2 bytes), and dest (2 bytes), in that order.
Each event palette has the option of choosing one of 32 different palette animations to display after the map has faded in, numbered 1 to 32. (Only 8 of these contain anything interesting.) A table of 32-bit pointers to every palette animation can be found at $DFE4E1
in the ROM.
A palette animation consists of a 32-bit pointer to compressed palette data, made of (96 * number of animation frames) colors, followed by a byte specifying the number of animation frames, followed by one byte for each animation frame, used as a duration. A palette animation may contain up to 10 animation frames using the RAM layout of the vanilla game.
CoilSnake and EB Project Editor currently do not have any built-in support for editing palette animations, but as mentioned before, it is easy to choose which animation a given event palette uses in map_palette_settings.yml
.
The two main parts of animated tiles are the graphics (clusters) and a configuration controlling how they animate. There are also two tables that contain pointers to these two pieces of data.
The Tile Cluster Pointer Table is located at $2F11CB
in the ROM, and is 80 bytes long. There are 20 entries (one for each tileset) and each entry is simply a 4 byte pointer to a tile cluster. See the table at the end of this document with a list of all the entries. The tile cluster graphics are compressed in the rom, just like regular map tiles. You can easily decompress them for editing with Exhal, and reinsert them with Inhal, using the pointers provided in the tile cluster pointer table, which I’ve included at the end of this page. You can edit the decompressed graphics in a tile editor such as YY-CHR. Keep in mind that if your modified graphic cluster is larger than the original, you’ll have to move it to some free space
elsewhere, and repoint everything to match.
The Tile Config Pointer Table is located at $2F121B
, and is also 80 bytes long with 20 entries, 4 bytes per entry. Once again, each entry is for each tileset in order, and points to a Tile Animation Configuration Editing the cluster of tiles is easy enough. Most of the hard work, and also what’s most likely to go wrong, are the animation configs. Once you’ve figured it out though it’s easy enough to change or add new entries to it— but again, adding new entries requires moving it and repointing it so that you don’t overwrite stuff after it (although, if your hack isn’t using some
of the stuff and you don’t really care about getting rid of it, feel free to overwrite other entries as long as those tilesets won’t ever be used.)
Most of the info here was originally researched by the_Kirby1.
Animated Tile Configuration format:
Let's use the second config entry as an example, which is used for Onett’s tileset.
Onett's config, located at $2F1294
, goes like this:
02 04 13 40 01 20 00 10 00 02 14 40 00 20 05 B0 00
The very first byte is a sort of header that says how many groups there are. Each group is 8 bytes long. Thus, if we break it down it looks like this:
02
04 13 40 01 20 00 10 00
02 14 40 00 20 05 B0 00
In Onett, the first group is used for the water, and the second group for the stars at night. Each group follows this pattern:
AA BB CC CC DD DD EE EE
-
AA
= How many frames this animation has. The first group (water) has 4 frames, while the second group (stars) has 2. -
BB
= The speed of the animation, in frames. the first group has 0x13, which in decimal is 19. Each frame of animation holds for 19 game frames (60fps) before switching to the next one. -
CC CC
= Defines how long of a string each frame is. This “String Length” setting is a bit complicated to explain. If we open the tile cluster in an editor, the graphics look like this:
Understanding this String Length setting will make more sense if we lay it out like this to understand the order the tiles are in:
See how, for the first group, there are four frames (top to bottom), and each frame is 10 tiles long? The “10 tiles long” part is the String Length.
Remember though how CC CC
was 40 01
? Switch those two bytes around (if you’re wondering why, see the bottom of this doc, labeled “Endianness”). Now we have 0x140. Each tile in the string is worth 0x20, so multiply the number of tiles in the string by 0x20
. 0xA
times 0x20
equals 0x140
. (you can also multiply the decimal numbers and convert the result to hex at the end. 10 times 32 is 320, which then
converts to 0x140.) The purpose of this string will be further explained later with EE EE
, which sets the starting
minitile in the tileset that gets replaced with the animation.
-
DD DD
= Sets the offset for the first tile in the cluster to start the animation with. For this example I’m going to use the second group instead, the stars. Look at the tile cluster again:
Let’s number them:
If we look at DD DD
in group 2, you’ll see 20 05
. Once again, flip the order, and it’s 0x520
. Just like CC CC
, this number is only multiples of 0x20
. 0x520
divided by 0x20
equals 0x29
, aka 41. And see how in the tile cluster, number 41 is the first star?
If we look back at the first group, you’ll see the cluster offset is 20 00, which is simply 1.
-
EE EE
= Which minitile in the main tileset to replace. This will replace not only the chosen minitile, but also several minitiles after as defined by *CC CC
, the string length. Unlike the two previous parameters, this one is in multiples of0x10
. Looking at EBprojedit, in the tile editor, you can see most the first 12 tiles are animated, 10 for water and 2 for stars. ForEE EE
, group 1 has10 00
, which is0x10
, the first minitile, then remember its string length,CC CC
, is 10. So the group starts with this minitile number, but then keeps replacing tiles for as long as the string. This way a single config group can handle many animations at once.
Inserting this custom animation, it features dancing flowers that will be added to Onett’s tileset:
Since I want to do this without getting rid of the water or star animations, you’d need to repoint all the stuff. The two tables I made, animated_tile_configuration_table
and animated_tile_graphic_table
take care of all this.
The first step is to decompress the graphics. Onett’s tile cluster is at $1FC93B
, so decompressing it with Exhal goes something like this:
“exhal.exe” “EarthBound.sfc” 0x1FC93B “OnettTiles.bin”
Now if we open the resulting bin file in YY-CHR, we can see and edit the tiles:
Make sure you set the “Format” to 4BPP SNES. The colors don’t matter, but if you want to preview what they should look like you can right click on the colors in the palette and change them to the ones used in the map palettes. Now I’m simply going to create new tiles at the end, after the stars. I put the first frame for both animations, then the second frame for both animations, and so on. This makes it simpler to configure, because remember with the String Length, we can animate both flowers with the same configuration. The first two tiles will be used, one for the white flower and one for the red, and then the second two tiles, again one for each, etc.
Here's the final result:
Simply save the file, and now it’s time to compress it. This time we use Inhal, and the usage goes something like this:
“inhal.exe” -n “onett flowers.bin” “flowers Compressed.bin”
The first file, “onett flowers.bin”, is the file with the tiles you made. The second file, “flowers Compressed.bin”
, is the name of the new compressed file to be created. Don’t get these mixed up! Now, to actually insert these tiles into the rom, we use the animated_tile_graphic_table.ccs
.
Open this table and get rid of the address and uncomment the long pointer for Onett near the top (basically, get rid of the "[3B C9 DF 00]"
and two slashes on line 11. It’s on line 7 in the image below but I have since modified the file a bit). Then, down under that table, get rid of
the placeholder comment under Onett: (the one that says //paste bytes here...
) Now open up the new Compressed bin file in a hex editor, copy the entire thing, and paste under the Onett: entry. Make sure to surround it with “[
and ]”
!
Now the next step is over in animated_tile_configuration_table.ccs
, to set up how
these flowers work. Using what we covered above, this should be easy to figure out.
AA BB CC CC DD DD EE EE
-
AA
= This animation has four frames, so 04. -
BB
= I chose to make the flowers animate every 20 frames, so in hex this is 14. -
CC CC
= The string length is 2 (one tile for each flower, so two total). Remember you add0x20
for each tile, so this one would be0x40
. Reverse it for endianness, and we get40 00
. -
DD DD
= The first tile for these flowers starts at tile number 45. Multiply this by0x20
, and we get0x5A0
. Again, reverse for endianness, and we getA0 05
. -
EE EE
= Most of the original animated tiles in EarthBound replace the very first minitiles in a tileset. This doesn’t have to be the case though, as you can see the flowers I want to animate are right smack in the middle:
After spending a long time squinting at my screen and counting, I was able to find that that first white flower is tile number 268. Remember that for the configuration the tile to replace is a multiple of 0x10
, so that gives us 0x10C0
. Once again, reverse the bytes for endianness, and we get C010
. Now put it all together, and we get a nice final result of 04 14 40 00 A0 05 C0 10
Unlike with the graphics table, we don’t have to change or remove anything, so we can simply paste that right under the existing configurations in the entry for Onett in the Configuration Table:
Now, these two ccs tables are all you have to put into the CCScript folder of your Coilsnake project (well, you do need to have asm65816 in there as well). You should be good to compile and check it out! Once you’ sufficiently understood everything in this document you can simply refer to “Animated tiles summary.txt” which is a summarized version of the important information here. It’s quicker to look at.
Tile Graphic Cluster Pointer Table (located at $2F11CB in the ROM):
The Entry column shows the original bytes as they exist in the rom. The number on the left naturally represents which tileset it’s for. The Pointer column simply rearranges that into a single pointer. These pointers are used within SNES mapping space, which has $C00000
added to everything. The numbers under the “ROM Address” column are the actual offsets from the start of the rom, and are what you put into Exhal when decompressing. This is likely the column you’d get the most use out of, I only included the others for reference in case you wind up wanting
them for something.
Entry Resulting Pointer ROM Address
0 43 C2 DF 00 $DFC243 FC243
1 3B C9 DF 00 $DFC93B FC93B
2 7F CB DF 00 $DFCB7F FCB7F
3 98 CB DF 00 $DFCB98 FCB98
4 B1 CB DF 00 $DFCBB1 FCBB1
5 CA CB DF 00 $DFCBCA FCBCA
6 00 D0 DF 00 $DFD000 FD000
7 EE D6 DF 00 $DFD6EE FD6EE
8 57 DD DF 00 $DFDD57 FDD57
9 EB E1 DF 00 $DFE1EB FE1EB
10 04 E2 DF 00 $DFE204 FE204
11 1D E2 DF 00 $DFE21D FE21D
12 36 E2 DF 00 $DFE236 FE236
13 02 E4 DF 00 $DFE402 FE402
14 C8 E4 DF 00 $DFE4C8 FE4C8
15 E7 F0 DE 00 $DEF0E7 EF0E7
16 00 F1 DE 00 $DEF100 EF100
17 CF F2 DE 00 $DEF2CF EF2CF
18 EB F5 DE 00 $DEF5EB EF5EB
19 69 F8 DE 00 $DEF869 EF869
The Super Nintendo is a Little Endian machine, meaning that bytes are processed with the least significant digit first. The “normal” way that would be most natural for humans is called Big Endian, where the largest digits are first. For example, the decimal number 41,394 would be 0xA1B2
in hexadecimal. But when writing it in the little endian format, you’d break it into the digits A1
and B2
, then switch them around, giving us B2 A1
. Not all forms of data on the SNES are written in little endian, as that depends on how the programmers chose to make their code work. However, pointers almost always are, as you can see in the graphic pointer table above (the Resulting Pointer column is just the Entry column but in reverse order) and as you’ve also seen most of the data used in the config tables is little endian as well.
- Overworld Sprites
- Battle Backgrounds
- Battle Sprites
- Title Screen
- Window Graphics
- Logos
- Fonts
- Animations
- Swirls
- EB Project Editor
- Tile Data
- Tile Editor
- Collision Data
- Adding Map Palettes
- Map Editor
- Doors
- Warp Styles
- Enemy Placement
- Hotspots