Fire Emblem: Shadow Dragon (FE11) documentation

I have recently been diving into researching Fire Emblem: Shadow Dragon (FE11). I wanted to document my findings in hopes that it can be (a) useful to folks exploring the DS games, and (b) built upon over time.

Since I have some real-life commitments coming up over the next little while, I wanted to get the information I’ve found down on paper before it goes stale in my brain.

If you have any information from your own DSFE hacking explorations, please feel free to add any findings of your own to this thread!

There are a number of terms that I’ll use below, which I’ll try to define ahead of time.

First, the term “ARM9 binary”. When you unpack the DS ROM using a tool like DSLazy, it will have a file called “arm9.bin”. This file contains the main code for the game. The DS has two processors, and “ARM9” refers to the one responsible for running the game code.

When I reference code or ROM data, I’ll use the notation FE11:0x<...>. This refers to a memory location of the game when the game is running. Since the DS loads the ROM to a base of 0x02000000, you can find the equivalent location in the “arm9.bin” file by subtracting 0x02000000 from the in-game address. All offsets refer to the US version of the game.

If/when I eventually start referencing the code or data inside of an overlay, I’ll use the notation FE11:Ovy##_0x<...>.

For completeness, the DS also has an ARM7 processor (and an ARM7 binary), which was mainly used for sound processing and other auxiliary tasks. Nintendo allegedly discouraged using ARM7 for game logic, so I likely won’t reference it much in this thread.

In general, I highly recommend the post Starcube Labs - “Reverse Engineering a DS Game”. It gives what I think is a clear introduction to loading DS ROMs into reverse-engineering tools like Ghidra, and offers helpful tips on identifying code and setting breakpoints in emulators.

Other relevant DSFE resources:

Overlays

The Nintendo DS employs a system called “overlays” to manage memory efficiently. Overlays allow the console to dynamically load code or data into memory only when needed and unload it when it’s no longer required. This helps optimize the limited memory resources of the DS.

Details

When you unpack a DSFE ROM using a tool like DSLazy, you will find a folder called “overlay”, which contains the binary files for each overlay. Additionally, the file “y9.bin” contains metadata about the overlays:

Overlay (Size: 0x20):
00 | word | Overlay ID
04 | word | Load address (where the overlay is placed in memory at runtime)
08 | word | Overlay size
0C | word | BSS (uninitialized data) size
10 | word | Static initializer start address
14 | word | Static initializer end address
18 | word | File ID
1C | word | 0 (delimiter between overlays)

For a detailed explanation of overlays, I recommend checking out the overlays section in the Starcube Laboratories post on Reverse Engineering a DS game.

FE11 features 12 overlays, which is relatively low compared to other DS games I’ve seen such as Pokemon HeartGold or Pokemon Mystery Dungeon: Explorers of Sky.

While the game is running, the table tracking the currently-loaded overlays is found at FE11:0x020E4DF4. It is represented by a series of bytes in memory. If an overlay is active, the byte value at that overlay’s index will be 1. If it’s inactive, the value will be 0.

The functions for managing overlays are as follows:

  • Loading an overlay: FE11:0x0200F20C
  • Unloading an overlay: FE11:0x0200F24C

There are additionally proc commands that involve loading/unloading overlays, which we will see later when I talk about the DSFE proc system.

From my initial research:

  • Overlay 0 and 2 are typically always active.
  • Overlay 4 seems related to core gameplay (e.g. battle maps).
  • Overlay 6 might handle the title and save screen.

There is still lots more to be uncovered about the overlays/how they are used.

Procs

FE11, like many other Fire Emblem games, features a “proc” system which is used to enable the game to do pseudo-parallel processing on an otherwise single-threaded CPU. This is something that has been very well detailed by others in the past:

For the sake of brevity, I won’t cover the entire scope of what procs are since I think Nat’s guide is already extremely thorough and comprehensive, and I would just be repeating a lot of that. Please read their research on it, and consider this to be the DSFE extension of that research.

Details

The DSFE proc system is, as far as I can tell, quite similar to the GBAFE proc system. It features the same general interfaces for starting/finding/ending procs, and the code itself appears to be relatively similar too.

There are a few differences. For example, each proc can have its own “function table”; a pointer to a list of callable functions that is stored at +0x00 of the proc. The purpose of this isn’t clear to me yet but I hope to understand how this complements the existing script-based format.

So far, I haven’t been able to identify things like what order the proc trees are executed in, or whether that is terribly important in DSFE.

The Proc Tree is located in RAM at FE11:0x02190F28.

The function at FE11:0x02018C58 seems to be equivalent to FE8’s Proc_Init. It iterates over 0x80 procs and initializes their fields to zero values. Based on that, it seems that the max proc count is 0x80 in FE11.

There appears to be 14 proc trees in FE11, based on the same Proc_Init function.

Many of the core functions for interacting with the procs are located in the “ITCM” (or “Instruction Tightly-Coupled Memory”) region. One of the features of the Nintendo DS is to give fast access to a limited amount of code.

What this means is that, at the time when the game is loaded, there is some code gets loaded the end of the ROM and put into a fast-access area. Most of the game’s code refers to the functions in that particular area. It makes it a touch more confusing for us as hackers to find references to that code (or at some point in the future, to modify it).

The Proc Function table is located at FE11:0x020E7898 in the ROM. It gets copied into the ITCM region, to FE11:0x01FFBBF8, when the game is running. In Nat’s doc, this aligns with the “Code Interpretation Table”.

Below, you can the locations of the proc functions along with some names that indicate their purposes (in most cases, I’ve tried to reuse the FE8 proc names where they seem to line up).

[00] 0x02019358 - ProcCmd_DELETE
[01] 0x02019358 - ProcCmd_DELETE
[02] 0x02019368 // returns 0
[03] 0x02019370 // returns 1
[04] 0x02019378 // advance and return 1
[05] 0x0201938c - ProcCmd_SET_DESTRUCTOR (?)
[06] 0x020193b4 - ? maybe set name?
[07] 0x020193dc - ProcCmd_CALL_ROUTINE
[08] 0x020193fc - ProcCmd_CALL_ARG_ADV
[09] 0x02019420 - ProcCmd_WHILE_ROUTINE
[0A] 0x0201945c - ProcCmd_CALL_ARG_ADVCOND (advance conditionally)
[0B] 0x0201951c - ?
[0C] 0x02019620 - ProcCmd_LOOP_ROUTINE
[0D] 0x02019640 - ProcCmd_WHILE_EXISTS
[0E] 0x02019674 - ProcCmd_NEW_CHILD
[0F] 0x020196a0 - ProcCmd_NEW_CHILD_BLOCKING
[10] 0x020196cc - ProcCmd_NEW_PROC_IN_TREE (?)
[11] 0x020196f8 - ProcCmd_END_ALL (sarg skips an additional pointer @ 027E1268)
[12] 0x02019734 - ProcCmd_BREAK_ALL (sarg skips an additional pointer @ 027E1268)
[13] 0x02019954 - ProcCmd_NOP_1D ? label?
[14] 0x02019770 - ProcCmd_GOTO
[15] 0x0201979c - ProcCmd_GOTO_IF_YES (if larg = 0 or larg func returns non-zero, goto)
[16] 0x020197e8 - ProcCmd_GOTO_IF_NO (same but if larg func returns zero)
[17] 0x02019834 - ProcCmd_JUMP
[18] 0x02019874 - ProcCmd_SLEEP
[19] 0x020198a4 - ProcCmd_SET_MARK (?)
[1A] 0x020198c4 - (?) end children?
[1B] 0x020198f8 - (?) end children?
[1C] 0x0201992c - (?) end children?
[1D] 0x02019954 - ProcCmd_NOP_1D
[1E] 0x02019968 - start a specific child (blocking if larg not &1)
[1F] 0x020199b8 - start a specific child (blocking if larg not &1)
[20] 0x02019a08 - start a specific child (blocking if larg not &1)
[21] 0x02019a58 - start a specific child (blocking if larg not &1)
[22] 0x02019aa8 - start a specific child (blocking if larg not &1)
[23] 0x02019af8 - start a specific child (blocking if larg not &1)
[24] 0x02019b48 - ProcCmd_OVERLAY - load or unload overlay; overlay ID = sarg, load/unload = larg
[25] 0x02019b84 - ProcCmd_ - maybe reset overlays? Unload all except sarg? Load only sarg?

Loading Units

Recently in the FEU Discord, there was some discussion about figuring out how units are loaded, particularly in the context of hard-mode scaling bonuses.

Details

The core function responsible for loading a unit is at FE11:0x02039FF8 in the main ARM9 binary. I’ve started decompiling the function here. Based on this, the function’s signature appears to be:

void func_02039FF8(struct Unit * unit, struct Dispo * dispo);

This function takes two parameters:

  • unit: A pointer to a Unit structure, which holds the data for the character being loaded.
  • dispo: A pointer to a Dispo structure, which provides information about how the unit should be loaded onto the map.

What is a “dispo”?

“Dispos” are, as far as I understand, the internal name for the data structure used to specify how to place units on the map. It’s likely an abbreviation for “disposition”. There are code references in the FE8 prototype that mention the term “dispos”, and they’re more commonly referenced by this name in hacking when we start seeing more strings in the code (such as FE9-10, and the 3DSFE games).

Each map has several “dispo groups” associated with it, which are loaded at different times (for example, for cutscenes or reinforcements). The layout of a single “dispo” entry is like this:

Dispo Entry (Size: 0x50):
00 | short | pid (character ID)
02 | short | jid (class ID)
04 | byte  | x position on load
05 | byte  | y position on load
06 | ?
09 | byte  | ?
0A | byte  | starting level
0B | byte  | ?
0C | word  | ?
10 | short[] | items; length 5?
1A | ?
1C | ?
24 | short | flags
26 | short | ?
28 | word[] | array of pointers to AI "types" (strings); length 4
38 | word[] | array of ?; length 4?
48 | byte | ?
49 | byte | ?
4A | short | ?
4C | byte | ?
4D | byte | ?
4E | ?

For more details on the dispo format, you can refer to the FE11 Unit Disposition format documentation by Blazer.

Hard-mode scaling bonuses

When one of the hard mode difficulties are selected, FE11 modifies enemy stats and equips units with different weapons based on the difficulty level.

In the function used for loading units, the game checks the unit’s dispo flags (+0x24) field. If the flags 0x110 are set, the game performs some stat modifications (an RNG roll based on the character/class growths), and weapon modifications based on the difficulty you’ve selected.

Weapon upgrade tables

When scaling the weapons for harder difficulties, the game uses a series of “upgrade ramps” (for lack of a better term). In the ROM, there is a lookup table at FE11:0x020D4BC0 which has pointers to each weapon type’s “upgrade ramp”.

FE11:0x020D4BC0 - Weapon Type upgrade lookup table
[0] 0x020D4B88 - pointer to Sword Table
[1] 0x020D4B48 - pointer to Lance Table
[2] 0x020D4B68 - pointer to Axe Table
[3] 0x020D4B78 - pointer to Bow Table
[4] 0x020D4B58 - pointer to Magic Table

Each of these tables contain pointers to string values for each item ID (IID), corresponding to which weapon the unit will receive for each difficulty. The weapon the unit receives depends on the difficulty selected (difficulty - 1).

Sword Table (FE11:0x020D4B88)
[0] 0x020D4AC8 - "IID_IRONSWORD"
[1] 0x020D4B08 - "IID_STEELSWORD"
[2] 0x020D4BE4 - "IID_SILVERSWORD"
[3] 0x020D4B18 - "IID_BRAVESWORD"

Lance Table (FE11:0x020D4B48)
[0] 0x020D4AD8 - "IID_IRONLANCE"
[1] 0x020D4B28 - "IID_STEELLANCE"
[2] 0x020D4B38 - "IID_SILVERLANCE"
[3] 0x020D4AF8 - "IID_BRAVELANCE"

Axe Table (FE11:0x020D4B68)
[0] 0x020D4A50 - "IID_IRONAXE"
[1] 0x020D4A68 - "IID_STEELAXE"
[2] 0x020D4AA8 - "IID_SILVERAXE"
[3] 0x020D4A78 - "IID_BRAVEAXE"

Bow Table (FE11:0x020D4B78)
[0] 0x020D4A5C - "IID_IRONBOW"
[1] 0x020D4A88 - "IID_STEELBOW"
[2] 0x020D4AB8 - "IID_SILVERBOW"
[3] 0x020D4A98 - "IID_BRAVEBOW"

Magic Table (FE11:0x020D4B58)
[0] 0x020D49B4 - "IID_FIRE"
[1] 0x020D4A44 - "IID_ELFIRE"
[2] 0x020D4AE8 - "IID_BOLGANONE"
[3] 0x020D4A38 - "IID_THORON"

Forged items

There is additional logic that determines whether the unit gets an upgraded weapon or a forgeable weapon. This seems to involve the item’s data in the FE11 Database. If the item’s byte at +0x3A is equal to 0x7E, the game uses a forged weapon instead of following the upgrade ramp. Further investigation is still needed to understand this completely.

12 Likes

Battle Animations

Based on the existing research from Blazer (mirrored on Laqieer’s GitHub), along with some hints from HeartHero’s “Accessible FE12 Hacking” post about battle animations, I have been able to piece a bit more together about how Battle Animations are structured in DSFE.

It’s not perfect or totally solved yet (I haven’t been able to figure out the correct palette data, or if there is a script format embedded inside of the animations), but I’ll try to document as far as I’ve been able to get for now.

First, a GIF export of the ARCHER_F dodge animation using the data extracted from the /data/b/ARCHER_F binary file:

frames_1

Details

What a weird format!!

As HeartHero mentions in their “Accessible FE12 hacking” post, each binary file in the /data/b directory represents an “archive” for that particular class’s animations. I use the term “archive” because the binary file appears to consist multiple files.

Each class has one “archive” for its unarmed animation, and one “archive” per weapon that class can wield. Example: LORD for the unarmed Lord class, and LORD_SW for the Lord class wielding a sword.

The first part of the binary file is a list of pairs containing the offsets to an animation (e.g. dodging, attacking, etc.), as well as that animation’s size within the binary. For example, I’ll use ARCHER_F. I assume that the blanks here could be filled by other animations, since ARCHER_F_BO has more entries filled out up front.

0x00000100 0x00002CF0 -> anim starts at 0x100, of size 0x2CF0
0x00000000 0x00000000
0x00000000 0x00000000
0x00000000 0x00000000
0x00002DF0 0x00004A5C -> anim starts at 0x2DF0, of size 0x4A5C
0x00000000 0x00000000

If we go to the location of that first animation, we can see data that looks like how other DSFE files are stored – it has a file “header”:

00 | word | pointer to end of animation
04 | word | pointer to this animation's "pointer table"
08 | word | number of entries in this animation's "pointer table"
0C-20 | word | blank?

We’ll use ARCHER_F’s first animation as an example:

@ 0x100 + 0x00, we have the offset to the end of the current animation. To calculate, add the base of the animation to the value at this offset to get the final offset of the animation. For ARCHER_F, it’s 0x2CF0 + 0x100 = 0x2DF0.
@ 0x100 + 0x04, we have the offset to this animation’s pointer table. To calculate it, we need to add two values: the base of this animation (0x100), and the space taken up by the “animation header” (0x20). For our example, ARCHER_F, it’s 0x2C58 + 0x100 + 0x20 = 0x2D78.
@ 0x100 + 0x08, we have the number of entries in the pointer table. For ARCHER_F, there are 0x1E (30) entries in the first animation’s pointer table.

We can go to 0x2D78 to look at the pointer table. Each pointer should be resolved relative to the start of its animation, with an additional 0x20 added to account for the “file header”. So for ARCHER_F’s first animation, when we see 0x14 in the pointer table, it really means 0x14 + 0x100 (start of the animation) + 0x20 (size of file header) = 0x134 in the binary file.


Picture: ARCHER_F’s pointer table (highlighted)

Below, I’ll show the values of the pointer table in the following format:

[pointer as shown in the table] => [actual location in the binary file]: [value at that location]
@ ARCHER_F's pointer table at 0x2D78. There are 0x1E (30) pointers in the table.
0x00000014 => 0x134: 0x1C
0x00000018 => 0x138: 0x13C (pointer to ?)
0x00000020 => 0x140: 0x1BC (pointer to first anim frame, compressed)
0x00000028 => 0x148: 0xEC (pointer to ?)
0x00000030 => 0x150: 0x4DE (pointer to second anim frame, compressed)
0x00000038 => 0x158: 0xF2 (pointer to ?)
0x00000040 => 0x160: 0x81B (pointer to third anim frame, compressed)
0x00000048
0x00000050
0x00000058
// (more)

The compression scheme of the frame data is the same as the “FEDS portrait compression”. Nintenlord’s decompression utilities can be used to decompress the individual frames. As I understand, it’s some sort of run-length encoding (RLE) used for the DSFE games.

There are still a lot of unknowns but hopefully this can be used as a building block for further research.

6 Likes

This is very cool, and very helpful. Thanks for this!

1 Like

Sweet! How did you break down the files the /data/b folder into the multiple sub-files (the sprite frames and the ‘command’ binary files)?

Could the various “pointer to ?” be ‘camera’ commands (like for panning over to the enemy for projectile hits, or following ‘charging’ cav).