Pokémon Palettes in Generation III
Go back to the main page This is an explanation on how palettes work in the Pokémon games of Generation III, Ruby, Sapphire, and Emerald, as well as FireRed and LeafGreen, and any ROM hacks of these.I am assuming some prior experience with a hex editor, and 24-bit color codes. Look up how to use them if you haven't before. I am also assuming basic Pokémon knowledge (e.g. what a shiny is).
This article is primarily for the sake of curiosity, not for practical use by ROM hackers. Unless you are working on some weird legacy project, there should be no reason to not use the decompilations. And even if you are, tools like UnLZ-GBA and HexManiacAdvance should let you skip most of the manual steps below.
If you find any errors or things I have missed, please contact me (see contact info on main page).
The Pokémon palettes are spread out in the games' ROM. While they are often stored after the sprites for the corresponding Pokémon, and shiny palettes after the back sprites, this is probably just a consequence of how the games were compiled from their source code as it is by no means a requirement. Instead, the palettes have a table of pointers to them. The palettes themselves are made up of 16 15-bit colors, compressed using LZ10 compression.
The palette pointer table
The pointers to all Pokémon palettes are stored in a single table.The address of the table
What the offset for the pointer table is depends on the ROM. In all ROMs but the Japanese and English versions of Ruby and Sapphire, this offset is written near the start of the ROM, as a pointer at 0x130 (other table addresses are also written nearby). The address for the shiny palette pointers is also given separately at 0x134, but it turns out we can use the "normal" address to locate those as well. For Japanese and English Ruby/Sapphire, the offset would have to be found manually.But since the number of versions is low enough (the number of [all Pokémon*the number of versions] is not), the addresses for most versions (e.g. not LeafGreen 1.1) are included in this article, whether trivial to find or not. See the tables below:
Game | (U)/English | (F)/French | (G)/German | (I)/Italian | (J)/Japanese | (S)/Spanish |
---|---|---|---|---|---|---|
Ruby | 0x1EA5B4* | 0x1F29BC | 0x1F7530 | 0x1EC250 | 0x1BEDC0* | 0x1EF2D4 |
Sapphire | 0x1EA544* | 0x1F294C | 0x1F74C4 | 0x1EC1E0 | 0x1BED50* | 0x1EF264 |
Emerald | 0x303678 | 0x30B1A8 | 0x317FE8 | 0x30303C | 0x2D6F08 | 0x3098DC |
Fire Red | 0x23730C | 0x231718 | 0x2371DC | 0x2303B0 | 0x1F68F0 | 0x232A78 |
Leaf Green | 0x2372E8 | 0x2316F4 | 0x2371B8 | 0x23048C | 0x1F68CC | 0x232A54 |
Game | (E)/English 1.2 (Europe) |
---|---|
Ruby | 0x1EA5CC* |
Sapphire | 0x1EA55C* |
Game | (U)(Independent)/English 1.1 | (J)(2CH)/Japanese 1.1 |
---|---|---|
Fire Red | 0x23737C | 0x1F2180 |
The palette pointer table itself
The palette pointers are ordered according to internal id number, which in Generation III is different from either Pokédex order, with a placeholder ?????????? at index 0, 25 identical blank entries known as ? between Celebi and Treecko, and some Gen III Pokémon out of order. This order also holds for the Pokémon egg and Unown forms, the pointers to their palettes are right after the ones of the other Pokémon.After the Unown pointers come the pointers to the shiny palettes. The break between them is easy to see, as all the Unown forms point to the same palette. The shiny palette pointers are also ordered according to the internal Pokémon order, and has the same quirks. Consequently, there is a pointer for a "shiny egg", though it points to the usual egg palette.
In conclusion, it goes:
?????????, Bulbasaur, Ivysaur, Venusaur, Charmander, ... , Ho-oh, Celebi, [? (25 times)], Treecko, Grovyle, ..., Deoxys, Chimecho, Pokémon Egg, [Unown (27 forms)],
S-?????????, S-Bulbasaur, S-Ivysaur, S-Venusaur, S-Charmander, ... , S-Ho-oh, S-Celebi, [S-? (25 times)], S-Treecko, S-Grovyle, ..., S-Deoxys, S-Chimecho, S-Pokémon Egg, [S-Unown (27 forms)]
To visualize, this here are the first few palettes as they would look in an hex editor:
B4 19 D0 08 00 00 00 00 24 00 D3 08 01 00 00 00 B8 0A D3 08 02 00 00 00
Or if we order them up and add some comments:
B4 19 D0 08 actual pointer for ??????????
00 00 00 00 "counter" for ??????????
24 00 D3 08 actual pointer for Bulbasaur
01 00 00 00 "counter" for Bulbasaur
B8 0A D3 08 actual pointer for Ivysaur
02 00 00 00 "counter" for Ivysaur
"B4 19 D0 08" => 0x0D019B4,
"24 00 D3 08" => 0x0D30024,
"24 00 D3 09" => 0x1D30024,
"24 00 D3 10" => 0x2D30024
The compressed palettes
The palettes are each 16 colors long (except for Castform's), or 15 if you discount the first one, which is always transparent. The colors are 15-bit colors, written in little endian, so they take 2 bytes each. Normally, this would mean each Pokémon palette is 16*2=32 bytes long, but they are "compressed" using LZ10 compression. This is a variant of Lempel-Ziv compression used for many GBA games, and for both palettes and sprites in the Pokémon games. LZ10 is also know as LZ77 "variant 10", or LZSS.(Strictly, LZ10 is an implementation of LZSS, short for Lempel-Ziv-Storer-Szymanski. LZSS is in turn based off LZ77. The "10" in LZ10 comes from the first identifying byte of the compressed data being "10".)
For the palettes, this "compression" actually only shortens a few palettes, making the rest of them longer, so really acts more like encryption.
How Lempel-Ziv works is mostly out-of-scope for this article, though some parts will be explained when relevant.
(The DS games use similar compression, hopefully to be covered in a later article.)
To undo the LZ10 compression, you can use this tool: LZ10 quicktool
There are many ROM hacking tools that undo LZ10, like UnLZ-GBA, but then they do it as part of a longer process and don't output the bytes directly, so for our purposes the above is preferred. In any case, here is the palette of Bulbasaur before and after decompression:
before/compressed:
10 20 00 00 00 39 57 FF
7F B0 63 4C 53 00 47 3E
23 25 BF 31 3B 21 00 B7
10 39 67 42 08 F7 3B 00
53 27 AE 1A 8A 15 1F 7C
after/uncompressed:
39 57 FF 7F B0 63 4C 53
47 3E 23 25 BF 31 3B 21
B7 10 39 67 42 08 F7 3B
53 27 AE 1A 8A 15 1F 7C
The first color of the uncompressed palette, "39 57", is transparent in-game (though it does have a color value, more on that in this article), so we will look at the second color, "FF 7F" (or FF7F), instead. FF7F stands for white, or #FFFFFF in standard 24-bit color, as you would use in a modern-day painting program. It is used for Bulbasaur's eyes, teeth and claws. To convert 24-bit colors to 15-bit, use this website: http://www.budmelvin.com/dev/15bitconverter.html.
However, if you were to input #FFFFFF, you would get 7FFF, not FF7F. This is again because of the little-endianness, lower bytes come first in the code. The only thing you have to do is swap the bytes around (so you'd get FF7F). Don't forget to!
Now, let's do a concrete example. Here is AAAC, my Nincada (on an emulator).
I want to change her (and every other Nincada's) green eyes and wings to these rad red ones I've done in a painting program:
First, I need to find the pointer to Nincada's normal palette. This is a Ruby rom, so palette pointer table starts at 0x1EA5B4. Nincada's internal id number is 301, or 12D in hexadecimal. 1EA5B4 + 8*12D = 1EAF1C, so Nincada's pointer is found at 0x1EAF1C.
We go there with out hex editor, and see the pointer.
We look at the four first bytes of the pointer, E8 E4 DE 08, and reverse them to see we need to go to 0xDEE4E8 (in bank 08) to find the palette. We go there and select a few row of bytes.
Now we actually don't know how many bytes the compressed palette is, due to the compression. In this case, I happen to know that the "10 00 08 00" we see a bit down usually signifies the start of a compressed Pokémon sprite, as "10 20 00 00" does for a palette, so I stop before it. But even if I didn't it wouldn't matter much, since the decompressor tool ignores any extra bytes.
We then paste the bytes into the decompressor tool and press "decompress".
From the compressed bytes
10 20 00 00 00 D0 3A 5C
6B 99 36 35 32 00 D3 29
10 37 8C 36 2E 1D 00 BE
73 E9 2D F9 5A B6 4A 00
33 3E CF 31 4B 29 A5 14
D0 3A 5C 6B 99 36 35 32
D3 29 10 37 8C 36 2E 1D
BE 73 E9 2D F9 5A B6 4A
33 3E CF 31 4B 29 A5 14
D0 3A 5C 6B 99 36 35 32
D3 29 10 37 8C 36 2E 1D
BE 73 E9 2D F9 5A B6 4A
33 3E CF 31 4B 29 A5 14
We then repeat those steps for the other two green colors, and the red colors that we want to replace them with, and get the below uncompressed palette (replaced colors in bold):
D0 3A 5C 6B 99 36 35 32
D3 29 F7 1C 73 2D 2E 1D
BE 73 0E 21 F9 5A B6 4A
33 3E CF 31 4B 29 A5 14
10 20 00 00 00 D0 3A 5C
6B 99 36 35 32 00 D3 29
F7 1C 73 2D 2E 1D 00 BE
73 0E 21 F9 5A B6 4A 00
33 3E CF 31 4B 29 A5 14
Success!
This change also carries over to the backsprite and the Pokédex, with no need to do anything more. We are done. Hopefully the tedium has taught you though, that this is not something you normally do manually. Use a tool!
Originally written by Voliol 12022-04-21, HE, last updated 12023-04-04.
Appendix: Explaining Castform's palette
Castform has a palette that is 64 colors, four times that of any other Pokémon. It is stored and compressed the same way as other palettes, but is just longer. The reason for this is that the palette is divided between Castform's four forms, into normal 16-color palettes.Normal Form Castform uses colors #0-15, Sunny Form uses #16-31, Rainy Form uses #32-47, and Snowy Form uses #48-63.
Originally written by Voliol 12022-07-10, HE.
LZ10 quicktool
Below are three files needed to compile into a working Java program (and a license). They use JavaFX, so getting it to run might be a little finicky.The reason they are not just an executable file, is that I am still learning Java, and sadly do not have the know-hows to port the code I borrowed for this tool. As such, when I ran into problems exporting the program into a executable .jar file, I decided to put solving it off, in favor of getting this article out there.
I eventually plan on "fixing" it and making (or referring to) a tool that can easily be used to compress/decompress LZ10. Still, the main takeaway from this article should be the ways the palette data are stored, and not this tool to manipulate only part of it. If you can't get it to run you should only have missed ~5% or so. (I hope)
Main.java
DSCmp.java
DSDecmp.java
LICENSE.txt
(the .java files are really .txt, as Neocities only allows "supporter accounts" to upload .java files)
Originally written by Voliol 12022-06-10, HE.