Ripping Sprites from Dragon Quest VI - A Report
Go back to the main page This is a report on ripping (overworld player character and monster) sprites from Dragon Quest VI, for the Super Famicom. I'm writing it in the hopes that doing so will aid my learning/memory, but I figured I might as well make it presentable enough to put on this website. There may be some points of general interest, though that assessment is of course up to the reader.Note that spoilers are not marked below, so read at your own discretion if you care about that. I'm also using language a bit loosely here, "sprite" is used to mean "image" in several places. Though not strictly correct it is a common use, so I don't think it should be distracting. "$" is used to mark numbers in hexadecimal. If you want to see every sprite I ripped in order, see the background image.
The raison d'être of this project is that I wanted the sprites for Terry . I looked for the sheets at Spriter's Resource and there weren't any! And so I decided I might as well rip them myself. Doing so I decided I should rip not only those of the swordsman, but the rest of the playable characters as well. Including Drago/Lizzie, which is why monster sprites were ripped as well.
Preparation
Knowing I wanted to rip sprites of a Super Famicom game, the first thing I needed was the ROM. My usual source Emuparadise didn't What I found was this: https://archive.org/details/rr-nintendo-snesApparently all Super Famicom/SNES games? I downloaded "nintendo-snes-japan.7z". With it being a folder which is 1,1GB when compressed, I'm assuming most Super Famicom games are in there. In any case there were three versions of Dragon Quest VI listed there, marked with "[f1]", "[f2]" and "" respectively. I used the last one for this ripping project.
I also downloaded emulators to play/look at the ROM, BizHawk and no$sns, though at a later point I made a switch to one with better debugging capacities (Mesen-S).
Retro Game Mechanics Explained
A channel that really aided me was Retro Game Mechanics Explained (RGME), without their phenomenal series on the SNES (and thus also Super Famicom) I don't think this project would have been possible. I had watched these videos earlier, but in a sort of casual noncommittal way. I knew they existed, and about what I could expect from them, but the ~3 videos that ended up being of particular use I still had to re-watch, some of them multiple times.Understanding where the data is loaded to
If I were to rip sprites from a more documented game like Pokémon Ruby (disregarding this has already been done a thousand times) I would look up sites like Data Crystal or more localized fansites or forums, ask around on said forums, or even use a tool like HexManiacAdvance. These sites, people, or tools would tell me where either the ROM offsets for the sprites themselves, or offsets for tables pointing to a whole bunch of images, as well as the offsets for the palettes they use. Then you can just rip them automatically using some home-written code, given you know the data format. Easy said, easy done*.But Dragon Quest VI is not a well documented game. Or rather it is, but not relative to the community-forming games I am used to, with live modding scenes. I'd reckon this game, which did not make an English release until the DS remake in 2011 (that remake being why I care about the game in the first place, it's a personally formative experience) (DS games are a pain to hack afaik, so I homed in on the SNES when I wanted to rip), does not have a modding community at all.
*Unless they are compressed, haha ha ha...
The first step here is to open up the game in the emulators, and "see what you can see".
The emulators I use both have VRAM visualizers. They allow me to see what is loaded into tile data, basically what "tiles" the game needs to show what's currently on the screen. The sprites have palettes that are 16 colors long (including transparent), and so their corresponding tiles have a bit-depth of 4 bits per pixel (bpp).
The tile data format is covered by RGME, but I rely equally on this article: https://sneslab.net/wiki/Graphics_Format
Note that the Super Famicom/SNES uses this format for 4bpp images, but other 16-bit systems like the GameBoy Advance (GBA) does not. I actually found the site previously when I was trying to figure out how images/4bpp worked in the GBA, much to my confusion back then. (The nifty part about how this works is that 4bpp tiles can be divided into two 2bpp tiles after another, just be reading the data as such.)
Six (8x8) tiles are loaded into VRAM at once for the player character, just enough for one 2x3 tile image of them, the one currently shown on screen. These tiles change when the PC takes a step or changes direction.
NPCs are also loaded into VRAM here, but all of their data is loaded in at once. I note that while the player character (as the hero, Rex), and Hassan/Carver and Muriel/Milly when loaded as NPCs in the intro scene, have full sprites for all directions and animation frames, while the villagers in Lifecod/Weaver's Peak don't. (A little) more on that later.
Initially, I had thought it would be enough to rip them manually this way, by copy-pasting them tile by tile into an image file. Also rearranging the tiles by hand. But this turned out to be slooow, and if I wanted Terry's sprite I would have to go a fair way into the game, even if I could rip it when he first appears as an NPC.
Debugging/changing what data is loaded
So what do we do now? Knowing where data is loaded to, we want to find how and where this happens, what the assembly code looks like. If we know this, we can change what data is loaded, or even find the ROM offsets for the sprite data. I try to do this with BizHawk and no$sns, but don't get anywhere until I ask for a better emulator on RGME's discord server, and get Mesen-S recommended. I still don't know if it is the best debugging emulator (I couldn't find how to "freeze" bytes in memory), but it's workable. The most important aspect of Mesen-S is that it has a "debugger" mode, where the SNES code can be seen as human-readable ASM/assembly code. This code can be changed on the fly, and you can also set breakpoints and see what values were recently used for the various commands (e.g. what values were stored and loaded).I've got some prior knowledge on assembly programming, but in this stage I also used Assembly for the SNES: An ASM tutorial, version 2.3, by Ersanio.
Again, the RGME videos are relevant. Part 1 and part 1b are an introduction redundant to the "Graphics Format" article above, but are still worth watching. Part 7, part 9, and part 11 touch on VRAM, and explain DMA and the general memory structure of the SNES(/Super Famicom), and are as such the most useful for the contents of this report/the ripping process. Part 2 might be relevant if you seek to do a deeper dive, but here I ultimately never did anything with OAM.
If you already watched the videos I just recommended, you will know that data is not written to VRAM the "usual way", via STA (STore Adress) commands, or even "pop sliding" or "block moving" (I do not know how these work, but it's out of scope either way). Instead, data is written using something called DMA, Direct Memory Access. If you've got a good understanding of the DMA commands, you could simply search for where a DMA writing is initiated, and try to find the routine for writing PC overworld sprites by flipping through the hits you got, perhaps using breakpoints to speed the search up. I did not do this. Instead, I used the "event viewer" of Mesen-S and filtered out only writes to VRAM. This way I find a routine which starts at $C00427. This routine runs every frame, and is the routine for loading the PC sprites into VRAM, i.e. what we are looking for. Perhaps it does more too, I do not know as I've not investigated all lines in detail. How do I know this is the PC-into-VRAM one, then, if I haven't investigated it all? In this routine, some commands (LDA) read memory addresses. I go to these memory addresses in the "memory viewer", and mess around with them to see what happens. Mesen-S facilitates this by highlighting addresses that have been recently read or written to.
An example:
$C0043B: LDA $953B
$C0043E: BEQ $C00464
The code above loads the value at $7E953B (Why the $7E in front, you ask? It ought to be the current bank but I haven't put much thought into why it's bank $7E here).
Then it checks if the value is equal to 0, and if it is breaks/jumps to $C00427.
Looking at our memory, the value at $7E953B seems to always be $0100, which is not 0. I change this value to be $0000 == 0, and what happens? The player character doesn't animate anymore! I can still move around, but the sprite is frozen, neither stepping nor changing the facing direction. This means $7E953B contains the info on whether the player character should be animated or not! We can confirm this by looking at the commands skipped, and recognize them as DMA-related writing.
LDA $9547
STA A1T0L
LDA #$00C0
STA DAS0L
LDA $9553
STA VMADDL
SEP #$20
LDA $956B
STA A1B0
LDA #$01
STA MDMAEN
Mesen-S has default labels for the DMA-related offsets.
A1T0L = $4302, A-bus Address (low)
A1B0 = $4304, A-bus Address (high/bank)
DAS0L = $4305, the size (#bytes) of the upcoming DMA
VMADDL = $2116, VRAM Address Registers (low)
MDMAEN = $420B, DMA Enable Register
Here the example led directly to the code we're interesting in, but we could have played around more. Changing bytes $7E953D-46 appears to allow us to turn off/on the animation for the other player characters, mirroring the above. Since I'm testing this at the very start of the game before the acquisition of more than the Hero as a party member, this causes the background tiles to be animated instead. Just one of many fun, glitchy, effects.
In any case, we know that data is being written to VRAM via VMA. Our next lines of interest are among those above:
LDA $9547
STA A1T0L
These reads a value at $7E9547, and sets it as the lower word for the A-bus. The A-bus, again, is where data is moved from in this DMA, so this is very promising. Looking at $7E9547 in memory, we see that the value is changing once every time the PC takes a step, or changes direction. When does this happen? Data is usually written using STA, so we search for "STA $9547"... Actually, we don't, because Mesen-S is being weird and doesn't give anything when we do that - we search for "sta $9547".
Searching for "sta $9547", we find multiple routines that are seemingly very similar. This is because they are just mirrors of each other. Part 9b of RGME's series explains this, but here it only matters that they are redundant, and any of them can be used... At least for the following steps, my gut tells me the routine in the $C0 bank might be preferable if any are, since it's in that bank we found the other routine.
In any case, we are met with "STA $9547,X", as the hit of our search. Ersanio's tutorial calls this pattern "Direct Indexed with X". It is a pain, and I'm not bothering learning it (now). Instead, I know that this is a promising routine, and go look at the memory values again. Just above "STA $9547,X" is "ADC $955F,X". It is also "Direct Indexed with X", but thanks to the debugger we can see that (when we have only one player character) it reads from the address $7E955F.
The memory value at $7E955F-60 does not change when I walk around or change directions, but it is written to when I move to another map. If I change its value, the player character sprite changes! $7E955F seems to Some kind of "base offset" for the sprite, and all its alternate frames.
It is here in writing this to remember how I did, that I realize that stopping here might have sufficed. You can increase $7E9560 by 6 to get the next sprite, going from Hero -> Hassan/Carver -> Chamoro/Nevan -> etc...
But we take one step further. By seeing when $7E955F is written to. Searching for "sta $955F" doesn't turn anything up (oh, this was because it should've been "sta $955f"...), good thing we can put a breakpoint on the memory address itself! We could of course have done the same for "sta $9547", but it was at this point I discovered the functionality. We set the breakpoint and move to a new map, and the game pauses at a "STA $955F,Y" at $C0C8DC. This routine has a bunch going on, but I hone it to the line "LDA $F39515,X" a bit up, since I see its latest loaded value is $0001. This turns out to be partially incidental, but it looks very promising, since I am hoping to find a place where I can just give a number for the sprite, and get all of the data loaded in the right place. $0001 seems like it could be for the hero, conceivably the sprite at a hypothetical index #1.
What I find instead is the memory values at $F39515-16. By increasing these by 1, I can move 1 tile over, in regards to where the sprite data is started to be read from (this is practical/conjectural, I don't know where the sprite data is ultimately stored so I can't confirm this). By increasing by $30=6*8, you can move past a whole sprite and all its animation frames (4 directions * 2 frames per direction), hopefully directly to the next sprite. The reason this is better than changing $7E9560 is that incrementing it always moves over the tile by exactly 8, and some sprite data (e.g. that of the wagon) is not offset by a multiple of 8 tiles, having "empty tiles" between them and the data before. Thanks to being able to move over only a single tile when needed, we can load those too.
Another detail is that the value at $F39516 also "loops" at values > $03, preventing us from loading some sprites. This is due to the line "AND #$03FF" at $C0C8BD. By changing that to "NOP" we may go to higher values (this does causes a palette bug, but we don't care about that).
This way we can (and I did) rip the sprites of all playable characters - and more.
Unscrambling, from VRAM to .png
The above steps describe how the right data is loaded into VRAM, but how do I go from that to .png?I change "LDA #$00C0" (at $C00447) to "LDA #$0600". This is because being immediately followed by "STA DAS0L", it is what defines the size of the DMA write. $C0 corresponds to 6 tiles * (8 * 8) pixels per tile * 4 bits per pixel / 8 (bits in a byte). $600 = $C0 * 8, or $C0 * 4 directions * 2 frames per direction. In other words, this makes it so that the whole sprite data is loaded into VRAM, and not just the one which should currently be shown on screen. Loading more bytes than appropriate into VRAM leads to funny graphical glitches, but we don't care about that since we just want to screen-shot the VRAM visualizer and crop out everything but the sprite we're interesting. Note there are two palettes for normal overworld sprites, so we have to choose the right one before screen-shotting. The final result should look something like this:
.
I then run it through an "unscrambler", which is just a quick Java program I wrote (download here, as a .txt file). It takes a 128x24 image as an input, and unscrambles it and puts it at the bottom of the 128-pixel wide output image. For example, given and , you get . And that's it.
What can be said about the sprites we found/ripped?
About the sprites, their order, etc.
As I debugged and tried to figure this all out, I also played some of the game to try to get a grasp on it. One of the goals I had was to get to the point of the game where you recruit Hassan/Carver, so I could see what switching the order of the party members did for the memory addresses. Though ultimately I found the solution elsewhere - having a party member to switch out didn't help much - I still feel I should accredit those who helped me on that endeavor. Because I don't know Japanese (and it's been a while since I played the DS version) I needed to use walkthroughs and translation guides to get as far, both for story progression and for navigating the GUIs. These guides also helped in part to identify/explain some aspects I talk about below.The walkthrough I used was this one, from the Strategywiki. The translation guide I used is this one by Alexander Langella. If I don't say otherwise, all transliterations into roman characters (for the Japanese names) should be from there. Finally there is this one, which I'll call Guide B, and some info was also taken from the Dragon Quest Wiki. All of the guides were found simply by searching "Dragon [Warrior/Quest] VI Guide".
Player Character Sprites
The order of the character sprites is different from the order you recruit them in-game. The sprite's order: The Hero/Rex, Hassan/Carver, Chamoro/Nevan, Barbara/Ashlynn, Muriel/Milly, Terry, [a blank sprite], AmosThe recruit order: The Hero/Rex, Hassan/Carver, Muriel/Milly, Barbara/Ashlynn, Chamoro/Nevan, Amos, Terry
Note that Drago/Lizzie does not appear here. There is also a mysterious sprite of a red guard grouped with the player character's, right after Amos'. I wonder if this is used as a disguise somewhere through the game, or if it is unused data?
After the sprites of the main playable characters come some special ones. They are: the coffin, Falshion*/Peggy Sue, the wagon, and Falshion/Peggy Sue again. The second Falshion sprite is identical to the first one, which is a little strange, since Falshion turns out to be the Pegasus who should then have a different winged sprite.
It is at this point I open up my DS with a save of the DS version to confirm that, and sure, Falshion does have a second sprite in that game - but all the sprites are also different. I've managed to rip the Super Famicom sprites and did so assuming they were the same, and now that turns out to be false... Oh well.
*Transcription by Guide B.
Monster Sprites
Both the Dragon Quest wiki and Guide B name 18 monsters that can be recruited as "monster recruits". At first I had thought the monster sprites listed after those of the main playable characters were only those, but as there are sprites for e.g. she-slimes and non-liquid metal slimes*, which don't appear as monster recruits, this can't be it. Throughout the game, monsters occasionally appear in the overworld, so I'm assuming these are used in those places.Note that Drago/Lizzie is treated much like a normal party member, but the BattleRex/Hacksaurus sprite is still among those of the other monsters. Guide B also mentions a slime known as "Rookie" who is be also recruited outside of the monster recruit system, by winning in the Slime Arena. I'm assuming he's graphically identical to slimes recruited "normally".
I think the sprite for the Lamp Demon/High Djinks looks a little off. Is the sprite/palette incorrect, or does it simply look like that? Its $F39515-16 value is $D4 05.
There are also some (off-topic) weirdness in the info I can find on monster recruits. The wiki lists multiple names on the articles for most monster recruits, but not for the slimes. Those are only mentioned by the names they have in the remakes. Guide B only mentions one name each.
*These aren't technically palette swaps, even if they are identical except for the colors. There are seemingly only two overworld palettes; the normal slime uses palette #1 and both she-slime and metal slime uses palette #2. They manage being different colors by simply using different parts of the palette given to them. The same applies to the normal/metal liquid slime.
NPC Sprites
After that comes sprites that look like they are used by "common" NPCs, whose graphics don't overlap with the above two categories. Human villagers and city folk and such. They probably are too, I'm just not making sure at this point, and most games have at least some unused data left in them. Unlike the player and monster sprites, these lack data for some tiles. These are presumably filled in with copying/mirroring from other frames/directions.This makes them harder to rip semi-automatically, so I'm not even trying it now.
What lies beyond the NPC Sprite data I do not know. Perhaps overworld background tiles? Graphics for the attack animations? The in-battle monsters? That is up to someone else (you? or a future me?) to figure out.
Originally written by Voliol 12022-10-04, HE.