Evan Zhang
    Blog About Me Projects Résumé Rating Predictor

Fire Emblem: Radiant Dawn (FE10) Modding #2: Data Reference


January 19, 2022 19 minutes Fire Emblem, Reversing

In post 1 of Fire Emblem 10 Modding, we delved into the structure of the ISO and locations of key files that we care about. One of these files was FE10Data.cms, which contains a substantial amount of data that we want to modify.

This post will describe FE10Data.cms's sub-sections in more detail, as they were brushed over in post 1. It should act as a decent reference for anyone who plans to modify this file.

Part 1: Prerequisites

You'll need to do many of the steps in post 1 if you haven't already. Recall that we extracted the ISO and decompressed FE10Data.cms into FE10Data.cms.decompressed.

It is recommended going into part 2 to open FE10Data.cms.decompressed in your favourite hex editor and follow along.

Part 2: Breaking down each sub-section

For convenience, I've attached the sub-section table from post 1:

Start location Number of objects Object byte size Data stored (Official label)
0x2c 461 Varies Character (PersonData)
0x926c 171 Varies Class (JobData)
0xdf44 296 Varies Item (ItemData)
0x12810 142 0x2c bytes Skill (SkillData, SID_*)
0x146d4 1 0x10 bytes Support Gain Bonus (RelianceParam)
0x146e4 9 0xc bytes Affinity (DivineData)
0x14754 1 0x40 bytes Attack XP (GameData)
0x14794 1 0x48 bytes Unknown (GameData)
0x147dc 43 0x2c bytes Terrain (TerrainData)
0x15bb8 44 0x194 bytes Battle Map (BattleTerrIndex)
0x1a12c 106 0x4 bytes Battle Map Background (BattleTerrName)
0x1a2d4 71 0x240 bytes Buddy Support (RelianceData)
0x24298 60 0xb4 bytes Chapter (ChapterData)
0x26ccc 45 0xc bytes Group (GroupData)
0x26eec 46 0xc bytes Bond Support (KiznaData)
0x2711c 64 0xc bytes Affinity Bonus (DivineParam)
0x27420 27 0xc bytes Weapon Triangle Bonus (3SukumiData)
0x27568 10 0x10 bytes Biorhythm Type (BioData)
0x27608 5 0xc bytes Biorhythm Level Bonus (BioParam)
0x27644 22 Varies Pacifist (NOBTL_*)

We'll go over in detail all the sub-sections. Note that some sub-sections start with 4 bytes denoting the number of objects in the list.

Character (PersonData)

Example: Ike's character data

00000030  0200 0b00 0003 245e 0003 035f 0002 fa34
00000040  0002 a1de 0002 b766 0003 42d6 0003 328a
00000050  0003 3766 0003 3624 0000 0000 0000 0000
00000060  0002 8cd0 0002 8cda 0000 0000 0703 0800
00000070  0300 0000 0006 0f01 0f0c 0e0c 0500 0041
00000080  370a 3c23 1e28 0f00
  1. 1 byte [unsigned int]: number of skills.
  2. 1 byte: null byte.
  3. 1 byte [unsigned int]: character's starting level.
  4. 1 byte [bool]: gender. 0 means male, 1 means female.
  5. 4 bytes [string pointer]: person (character) id (e.g. PID_IKE).
  6. 4 bytes [string pointer]: mpid (base person id).
  7. 4 bytes [string pointer]: mnpid.
  8. 4 bytes [string pointer]: face id.
  9. 4 bytes [string pointer]: job (class) id.
  10. 4 bytes [string pointer]: affinity.
  11. 4 bytes [string pointer]: starting weapon ranks (e.g. S-----------).
  12. (number of skills) * 4 bytes [string pointers]: starting skills.
  13. 4 bytes: null bytes.
  14. 4 * 4 bytes [string pointers]: action / animation ids.
    • For Beorc, the first 3 pointers correspond to the character as a tier 1/2/3 unit, respectively (null bytes if they don't have a tier 1/2). Last pointer is always null bytes.
    • For Laguz, the first pointer correspond to the character untransformed. The last 3 pointers are identical and correspond to the character transformed.
  15. 1 byte [unsigned int]: biorhythm. An integer between 0 and 9 corresponding to the biorhythm type, or 0xff if the character has no biorhythm.
  16. 3 bytes: unknown.
    • First byte is always 0x3 for playable characters, and vary from 0x0 to 0x6 only for bosses.
    • Second byte is always 0x8 for playable characters, and are a power of 2 only for bosses (0x4, 0x8 or 0xc).
    • Third byte is either 0x0, 0x3, 0x7, 0xf, or 0x1f for playable characters, and 0x0 for everyone else.
  17. 1 byte [unsigned int]: number of authority stars.
  18. 4 * 1 bytes [2's complement int]: Laguz transform related values.
    • For Beorc, these are all null bytes.
    • For Laguz, these correspond to [amount gained per turn when untransformed, amount gained per battle when untransformed, amount lost per turn when transformed, amount lost per battle when transformed]. The first 2 values are always positive. The last 2 values are always negative.
  19. 10 bytes [2's complement int]: stat additions to be added to the character's current class base stats to calculate the starting stats.
    • Correspond to [HP, STR, MAG, SKL, SPD, LCK, DEF, RES, CON, MOV].
  20. 8 bytes [unsigned int]: growth rates as a percentage.
    • Correspond to [HP, STR, MAG, SKL, SPD, LCK, DEF, RES]
Class (JobData)

Example: Vanguard class data

000092e0  0002 be97 0002 f790 0003 4a76 0002 dd01
000092f0  0002 b766 0000 0000 0000 0000 0000 0000
00009300  0002 8cda 0003 3297 0002 7a14 0c01 0300
00009310  0004 0100 0107 3c04 0000 0000 0003 3783
00009320  0003 39a1 0003 388a 0003 33ea 0000 0000
00009330  0003 3318 4125 0f28 251e 2017 3513 0317
00009340  1300 1106 5032 0a46 281e 280f 0402 0402
00009350  0200 0204
  1. 4 bytes [string pointer]: job (class) id.
  2. 4 bytes [string pointer]: mjid (base / genderless job id).
  3. 4 bytes [string pointer]: Shift JIS encoded Japanese of the class name.
  4. 4 bytes [string pointer]: mh_j.
  5. 4 bytes [string pointer]: job id that promotes to this class.
  6. 4 bytes [string pointer]: job id that this class promotes to.
  7. 4 bytes [string pointer]: alternate job id.
    • For Beorc class, always null bytes.
    • For Laguz class, the transformed job id if this class is the untransformed version, and vice verse.
  8. 4 bytes [string pointer]: item id.
    • For Beorc class, always null bytes.
    • For Laguz class, the Laguz's attack item (e.g. fang / beak / claw).
  9. 4 bytes [string pointer]: action / animation id.
  10. 4 bytes [string pointer]: base weapon ranks.
  11. 4 bytes [string pointer]: max weapon ranks.
  12. 1 byte [unsigned int]: base constitution (CON).
  13. 1 byte [unsigned int]: armour type.
  14. 1 byte [2's complement int]: armour weight.
  15. 1 byte [unsigned int]: mount type (0: no mount, 2: horse, 3: pegasus, 4: wyvern).
  16. 1 byte [2's complement int]: mount weight.
  17. 1 byte [unsigned int]: number of skills.
  18. 1 byte [unsigned int]: number of sfxc (attributes).
  19. 1 byte [unsigned int]: promote level. Character promotes to the next class when hitting this level. Capped at 21 (Technically not capped, but characters gain no XP after level 21 so you'll not be able to promote at all). 0x0 if this is the final class and cannot promote.
  20. 1 byte [unsigned int]: movement type.
  21. 1 byte [unsigned int]: default movement amount.
  22. 1 byte [unsigned int]: skill capacity.
  23. 1 byte [unsigned int]: field-of-war vision.
  24. 4 bytes: null bytes.
  25. (number of skills) * 4 bytes [string pointer]: skills gained upon promotion to this class.
  26. 4 bytes [string pointer]: skill gained if a Satori Sign is used (null if this class cannot use a Satori Sign).
  27. (number of sfxc) * 4 bytes [string pointer]: sfxc (attributes). e.g. SFXC_FLY, SFXC_DRAGON. This categorizes the class to determine whether a skill can be learned and item effectiveness.
  28. 8 bytes [int]: max stats
    • Correspond to [HP, STR, MAG, SKL, SPD, LCK, DEF, RES]
  29. 8 bytes [int]: base stats.
    • Correspond to [HP, STR, MAG, SKL, SPD, LCK, DEF, RES]
  30. 8 bytes [unsigned int]: growth rates. Most characters have custom growth rates. This is used when they do not.
    • Correspond to [HP, STR, MAG, SKL, SPD, LCK, DEF, RES]
  31. 8 bytes [int]: stat additions on promotion to this class. Null bytes if this is the initial class.
Item (ItemData)

Example: Ragnell item data

0000e3f8  0002 af92 0002 eba6 0002 d0ff 0003 42d0
0000e408  0003 42d0 0003 1608 0000 0000 0002 9cb2
0000e418  0000 0000 0012 0000 1250 0514 0f02 0102
0000e428  ff00 0000 0406 0001 0003 431b 0003 416a
0000e438  0003 42c8 0003 42b5 0003 41d0 0003 425d
0000e448  0000 0000 0000 0500 0000 0000
  1. 4 bytes [string pointer]: item id.
  2. 4 bytes [string pointer]: miid (base item id).
  3. 4 bytes [string pointer]: mh_i.
  4. 4 bytes [string pointer]: displayed weapon type,
  5. 4 bytes [string pointer]: actual weapon type. The only times this differs from the displayed weapon type are the white dragon breath and Ashera's judge items.
  6. 3 * 4 bytes [string pointer]: effect ids.
  7. 1 byte: null byte.
  8. 1 byte [unsigned int]: icon.
  9. 2 bytes [unsigned int]: cost per use. The cost in the shop is given by (cost per use * number of uses).
  10. 1 byte [int]: might.
  11. 1 byte [int]: accuracy as a percentage.
  12. 1 byte [int]: critical chance as a percentage.
  13. 1 byte [int]: weight.
  14. 1 byte [int]: number of uses. The cost in the shop is given by (cost per use * number of uses).
  15. 1 byte [int]: weapon experience gained per use.
  16. 1 byte [unsigned int]: minimum range.
  17. 1 byte [unsigned int]: maximum range.
  18. 1 byte: unknown. Is either 0x01, 0x02, or 0xff.
  19. 3 bytes: null bytes.
  20. 1 byte: always 0x4.
  21. 1 byte [unsigned int]: number of special attributes.
    • crit0: cannot critical.
    • poison: applies poison to the enemy if the hit connects.
    • twice: attacks twice.
    • valuable: cannot be sold.
    • stormsw: (for swords only) can attack from range.
    • infinity: infinite durability.
    • sealsteel: cannot be stolen.
    • lookschange: (for Ragnell only) unknown.
    • expandrange: can attack at 3 range for tier 3 marksman.
    • longfar: can attack from far range.
    • stone: for onagers (probably for doing a diamond-shaped area of effect).
    • sh: for ballistae.
    • absolutehit: 100% hit rate.
    • resire: heals character the amount of damage dealt.
    • blow: physical attack.
    • magblow: magical attack.
    • breath: for dragon Laguz.
    • finalonly: unknown.
    • healing: heals user if equipped.
    • rest: restores user if equipped.
    • curesilence: cures silence if equipped.
    • curesleep: cures sleep if equipped.
    • humanonly: for Beorc only.
    • beastonly: for Laguz only.
    • lycdamhalf: halves Laguz damage (skill).
    • movtw: allows using up leftover movement after performing an action (skill). Similar to the Canto skill.
    • flutter: unknown.
    • ownerreinforce: gives the user defence points.
    • 特効封印: negates effective bonus (skill).
    • ラグズ特効封印: negates Laguz effective bonus (skill).
    • eqA / eqB / ... / eqI: only characters with the hidden skill SID_EQ_* can equip.
  22. 1 byte [unsigned int]: number of sfxc (attributes).
  23. 1 byte [bool]: whether equipping / using this item gives the character stat boosts. 0 means no stat boosts, 1 means there are stat boosts.
  24. (number of attributes) * 4 bytes [string pointer]: attributes.
  25. (number of sfxc) * 4 bytes [string pointer]: sfxc (special attributes). This item is effective against classes with any of these attributes, or can perform a certain task (i.e. SFXC_DOORBREAK can break doors).
  26. 12 bytes [int]: stat boosts. These 12 bytes only exist if field 21 was a 1.
    • Correspond to [HP, STR, MAG, SKL, SPD, LCK, DEF, RES, CON, MOV].
    • The last 2 bytes are always null bytes, so I'm not sure what they correspond to. It's probably just for alignment purposes.
Skill (SkillData, SID_*)

Skill data are broken into 2 sections: a list of skills and skill table beginning at 0x1407c. Each skill entry is also listed separately in section 3.5 under SID_* (e.g. SID_ADEPT).

Example: Nihil skill data

00013ce0  0003 388a 0003 0ff4 0002 e0db 0002 c6d7
00013cf0  0002 9c7f 0000 0000 0002 aee9 7901 143c
00013d00  0102 0000 0001 4404 0001 440c

The actual skill data consists of:

  1. 4 bytes [string pointer]: skill id.
  2. 4 bytes [string pointer]: msid (base skill id).
  3. 4 bytes [string pointer]: mh_skill.
  4. 4 bytes [string pointer]: mh2_skill.
  5. 2 * 4 bytes [string pointer]: effect ids.
    • There's some difference between the first effect id and the second, but I haven't taken a close look.
  6. 4 bytes [string pointer]: item id. Item used to learn the skill.
  7. 1 byte [int]: counter.
  8. 1 byte: unknown. Either 0x1, 0x2, or 0x3.
    • 0x3 means hidden (skill is not visible in-game).
  9. 1 byte [int]: capacity.
  10. 1 byte: unknown.
  11. 1 byte [unsigned int]: table pointer 1 count.
  12. 1 byte [unsigned int]: table pointer 2 count.
  13. 2 bytes: null bytes.
  14. 4 bytes [raw pointer]: table pointer 1 (offset by 0x20). 0x0 if no pointer.
  15. 4 bytes [raw pointer]: table pointer 2 (offset by 0x20). 0x0 if no pointer.

Example: Mercy table pointer 1 data (contains 3 entries):

000143f4  0100 0000 0003 3869 0000 0000 0003 3318
00014404  0000 0000 0003 22d1

One skill table entry consists of:

  1. 1 byte [bool]: explained later.
  2. 3 bytes: null bytes.
  3. 4 bytes [string pointer]: skill id / sfxc / job id / person id.

For a person/job id/sfxc, if the first byte in the entry is:

  • 0x0: skill is whitelisted (only these classes/characters/classes with this attribute can learn the skill).
  • 0x1: skill is blacklisted (these classes/characters/classes with this attribute cannot learn the skill).

For a skill id, if the first byte in the entry is:

  • 0x0, skill is a prerequisite. For example, the Shove skill is a prerequisite for the Smite skill.
  • 0x1, skill is an antirequisite. For example, the Formshift skill is an antirequisite for the Halfshift skill.

There doesn't seem to be any differentiation between the 2 pointers. My guess is two table pointers are used to save space. That is, multiple skills can point to the same location in the table if their entries are the same, and having 2 pointers increases the likelihood of entries being the same.

Support Gain Bonus (RelianceParam)

The support gain consists of 0x10 bytes:

000146d4  3264 9697 0f1e 0305 0401 0202 0100 fffe
  1. 1 byte [unsigned int]: max support points for no support.
  2. 1 byte [unsigned int]: max support points for a C support.
  3. 1 byte [unsigned int]: max support points for a B support.
  4. 1 byte [unsigned int]: max support points for an A support.
  5. 1 byte [int]: base chapter bonus.
  6. 1 byte [int]: unknown bonus. Is exactly 0x1e.
  7. 1 byte [int]: base adjacent bonus.
  8. 1 byte [int]: base carry bonus.
  9. 1 byte [int]: base heal bonus.
  10. 1 byte [int]: base shove bonus.
  11. 1 byte [int]: unknown bonus. Is exactly 0x2.
  12. 5 bytes [2's complement int]: delta type.

There are 5 different support types (00, 01, 02, 03, 04). The base bonus corresponds to support type 02, and the deltas determine the actual bonus using the base bonus and the support type.

Serenes Forest has great info on support bonuses, so I'm not going to restate what they wrote.

Affinity (DivineData)

Example: Thunder affinity data

00014700  0003 42dd 0001 0005 0000 0000
  1. 4 bytes [string pointer]: affinity name.
  2. 1 byte [int]: might delta.
  3. 1 byte [int]: defence delta.
  4. 1 byte [int]: hit delta.
  5. 1 byte [int]: avoid delta.
  6. 4 bytes: null bytes.
Attack XP (GameData)

The attack XP data consists of 0x40 bytes:

00014754  0028 001c 000f 000f 001e 0013 000a 0005
00014764  0064 0064 0064 0064 0064 0064 0064 0064
00014774  0005 0005 0005 0000 0005 0005 0005 0000
00014784  000f 000a 000a 0005 0028 003c 0028 001e

The data are broken into groups of 0x8 bytes. Each group corresponds to some modifier for each of the 3 difficulties and an unknown (probably debug) difficulty in the order [unknown, easy, normal, hard].

  1. 4 * 2 bytes [2's complement int]: kill modifier.
  2. 4 * 2 bytes [2's complement int]: attack modifier.
  3. 4 * 2 bytes [2's complement int]: kill percentage (always 100).
  4. 4 * 2 bytes [2's complement int]: attack percentage (always 100).
  5. 4 * 2 bytes [2's complement int]: "tier 2" kill modifier.
  6. 4 * 2 bytes [2's complement int]: "tier 3" kill modifier.
  7. 4 * 2 bytes [2's complement int]: Laguz attack modifier.
  8. 4 * 2 bytes [2's complement int]: boss kill modifier.

Some formulae for computing XP using these modifiers will be described later on as they are complex.

Unknown (GameData)

The relevant bytes are:

00014794  0028 001e 0014 000a 00c8 00c8 0064 0064
000147a4  0000 0000 0000 0000 0000 0000 0000 0000
000147b4  0000 0000 0000 0000 0005 0505 0505 0505
000147c4  0005 0505 0505 0505 0000 0000 0000 0000
000147d4  0000 0000 0000 0000

These bytes don't seem to affect the game in any way, but I may have missed something.

Terrain (TerrainData)

Terrain data are broken into 2 sections: a list of terrains, and a list of pointers to terrain data beginning at 0x14f40.

Example:

00014c54  0000 0000 0000 0000 0002 9b30 0003 33ab
00014c64  0003 33bf 0303 0303 0303 0202 0303 0101
00014c74  0101 0202 0101 0202 0202 0300

The actual terrain data consists of:

  1. 1 byte [int]: ground avoid bonus.
  2. 1 byte [int]: ground defence bonus.
  3. 1 byte [int]: ground resistance bonus.
  4. 1 byte [int]: air avoid bonus.
  5. 1 byte [int]: air defence bonus.
  6. 1 byte [int]: air resistance bonus.
  7. 1 byte [int]: HP recover percentage.
  8. 1 byte: unknown.
  9. 4 bytes [string pointer]: effect id.
  10. 2 * 4 bytes [string pointer]: sfx (sound effects).
  11. 17 * 1 bytes [unsigned int]: movement costs.
    • Corresponds to [Foot 1, Foot 2, None, None, Armor 1, Armor 2, Mage 1, Mage 2, Horse 1, Horse 2, Flying, Thief 1, THief 2, Bandit, Beast 1, Dragon 1, Beast 2, Dragon 2, Civilian, None, None, Heron (Rafiel), Black Knight].
  12. 1 byte: null byte.

Example:

00014f90  0400 0000 0003 490d 0003 14d0 0001 4898

The terrain list of pointers consists of:

  1. 1 byte: unknown. Probably just an index.
  2. 3 bytes: null bytes.
  3. 4 bytes [string pointer]: Shift JIS encoded Japanese name
  4. 4 bytes [string pointer]: mt. Also Shift JIS encoded Japanese.
  5. 4 bytes [raw pointer]: pointer to terrain data (offset by 0x20).
Battle Map (BattleTerrIndex)

Note: only the last 2 of the 4 bytes are used to denote the number of objects. The first 2 bytes seem to be used for something else.

Example:

00016b84  0003 3ff7 0019 ffff 001a ffff ffff ffff
00016b94  ffff ffff ffff ffff ffff ffff ffff ffff
00016ba4  ffff ffff ffff ffff ffff ffff ffff ffff
00016bb4  ffff ffff ffff ffff ffff ffff ffff ffff
00016bc4  ffff ffff ffff ffff ffff ffff ffff ffff
00016bd4  ffff ffff ffff ffff ffff ffff ffff ffff
00016be4  ffff ffff ffff ffff ffff ffff ffff ffff
00016bf4  ffff ffff ffff ffff ffff ffff ffff ffff
00016c04  ffff ffff ffff ffff ffff ffff ffff ffff
00016c14  ffff ffff ffff ffff ffff ffff ffff ffff
00016c24  ffff ffff ffff ffff ffff ffff ffff ffff
00016c34  ffff ffff ffff ffff ffff ffff ffff ffff
00016c44  ffff ffff ffff ffff ffff 001a ffff ffff
00016c54  ffff ffff ffff ffff ffff ffff ffff ffff
00016c64  ffff ffff ffff ffff ffff ffff ffff ffff
00016c74  ffff ffff ffff ffff ffff ffff ffff ffff
00016c84  ffff ffff ffff ffff ffff ffff ffff ffff
00016c94  ffff ffff ffff ffff ffff ffff ffff ffff
00016ca4  ffff ffff ffff ffff ffff ffff ffff ffff
00016cb4  ffff ffff ffff ffff ffff ffff ffff ffff
00016cc4  ffff ffff ffff ffff ffff ffff ffff ffff
00016cd4  ffff ffff ffff ffff ffff ffff ffff ffff
00016ce4  ffff ffff ffff ffff ffff ffff ffff ffff
00016cf4  ffff ffff ffff ffff ffff ffff ffff ffff
00016d04  ffff ffff ffff ffff ffff ffff ffff ffff
00016d14  ffff 0000
  1. 4 bytes [string pointer]: bmap.
  2. 398 bytes: unknown. I'm not sure what this data is. Most of it is 0xff, with non-0xff bytes sparsely placed throughout.
  3. 2 bytes: null bytes.
Battle Map Background (BattleTerrName)

Consists of 106 string pointers to background ids (e.g. bgE3). Not too sure what they're used for.

Buddy Support (RelianceData)

This one's interesting. Each of the 71 playable characters are paired with all of the other 71 playable characters (including themselves!) and a buddy support type is listed.

Example: Elincia's buddy support data

0001b058  0003 22d1 0000 0047 0003 245e ff02 0000
0001b068  0003 22c9 0002 0000 0003 22b2 0002 0000
0001b078  0003 307f 0002 0000 0003 303d 0002 0000
0001b088  0003 22a8 0002 0000 0003 22d1 0002 0000
0001b098  0003 277f 0002 0000 0003 276a 0002 0000
0001b0a8  0003 2775 0002 0000 0003 1b32 0001 0000
0001b0b8  0003 2347 0002 0000 0003 1b40 0001 0000
0001b0c8  0003 2531 0002 0000 0003 251b 0002 0000
0001b0d8  0003 2544 0002 0000 0003 2506 0001 0000
0001b0e8  0003 241f 0002 0000 0003 2c96 0002 0000
0001b0f8  0003 2baa ff01 0000 0003 2be3 0002 0000
0001b108  0003 1b7d 0002 0000 0003 237d 0002 0000
0001b118  0003 2352 ff01 0000 0003 24e6 0002 0000
0001b128  0003 2c7a 0002 0000 0003 2ce9 0001 0000
0001b138  0003 2bc1 0002 0000 0003 2c86 0002 0000
0001b148  0003 2d34 0002 0000 0003 2d0f 0002 0000
0001b158  0003 1b68 0002 0000 0003 30e7 0002 0000
0001b168  0003 2d63 0002 0000 0003 2d7c ff02 0000
0001b178  0003 2d9b 0002 0000 0003 2713 0002 0000
0001b188  0003 271d 0002 0000 0003 2737 0002 0000
0001b198  0003 2707 0002 0000 0003 272a 0002 0000
0001b1a8  0003 2749 0002 0000 0003 2435 0002 0000
0001b1b8  0003 2f6f 0002 0000 0003 2f78 0002 0000
0001b1c8  0003 1b1a 0002 0000 0003 231e 0002 0000
0001b1d8  0003 243e 0002 0000 0003 2789 0002 0000
0001b1e8  0003 1ade 0002 0000 0003 2677 0001 0000
0001b1f8  0003 2603 0001 0000 0003 2694 0002 0000
0001b208  0003 26b1 0002 0000 0003 26fc 0002 0000
0001b218  0003 268c 0002 0000 0003 26e4 0002 0000
0001b228  0003 24d2 0002 0000 0003 2f55 0001 0000
0001b238  0003 25e1 0002 0000 0003 2576 0001 0000
0001b248  0003 27c8 0002 0000 0003 257e ff01 0000
0001b258  0003 25c9 0002 0000 0003 2b78 0002 0000
0001b268  0003 25f7 ff00 0000 0003 25bc 0002 0000
0001b278  0003 2b5e 0002 0000 0003 2594 0000 0000
0001b288  0003 256c 0002 0000 0003 305a 0002 0000
  1. 4 bytes [string pointer]: person id.
  2. 4 bytes [unsigned int]: number of buddy supports.
  3. (number of buddy supports) * 8 bytes:
    1. 4 bytes [string pointer]: person id with which this support is for.
    2. 2 bytes [bytes]: support type.
    3. 2 bytes: null bytes.

Once again, Serenes Forest has some great info on buddy support types.

Chapter (ChapterData)

Example: Part 4 Chapter 13 chapter data

000257b4  0002 9695 0002 c00e 0003 40ab 0002 9695
000257c4  0002 9695 0000 0000 0000 0000 0000 0000
000257d4  0000 0000 0003 15ca 0003 1560 0000 0000
000257e4  0002 f882 0002 f83e 0002 f8f6 0000 0000
000257f4  0000 0000 0003 15ca 0003 1560 0000 0000
00025804  0002 f882 0002 f83e 0002 f8f6 0000 0000
00025814  0000 0000 0003 15ca 0003 1560 0000 0000
00025824  0002 f882 0002 f83e 0002 f8f6 0000 0000
00025834  0000 0000 1e00 0000 0200 01fe 0100 0406
00025844  0003 42d6 0003 3183 0003 3dcf 0003 48bc
00025854  0003 30f7 0003 2694 0000 0000 0000 0000
00025864  0000 0000
  1. 4 bytes [string pointer]: chapter.
    • usually of the form C<part><chapter><sub-chapter>, one indexed where 01 is the prologue chapter.
    • C0000 is the debug chapter.
    • CFINAL is the final chapter.
    • T<index> is a tutorial chapter.
  2. 4 bytes [string pointer]: mct.
  3. 4 bytes [string pointer]: bmap.
  4. 4 bytes [string pointer]: identical to chapter.
  5. 4 bytes [string pointer]: base chapter, excluding the <sub-part>. Tutorial chapters have tut here.
  6. 16 bytes: null bytes.
  7. 32 bytes: easy chapter conditions.
  8. 32 bytes: normal chapter conditions.
  9. 32 bytes: hard chapter conditions.
  10. 1 byte [int]: counter.
  11. 3 bytes: null bytes.
  12. 8 bytes: unknown.
    • First byte is always 0x2, except for C0000 and CFINAL, in which case it is 0x0.
    • Second byte seems to be the convoy.
  13. 4 bytes [string pointer]: affinity.
  14. 4 bytes [string pointer]: rid 1. Shift JIS encoded Japanese.
  15. 4 bytes [string pointer]: background.
  16. 4 bytes [string pointer]: setting. Shift JIS encoded Japanese.
  17. 4 bytes [string pointer]: rid 2. Shift JIS encoded Japanese.
  18. 4 bytes [string pointer]: commander (person id).
  19. 12 bytes: null bytes.

Chapter conditions:

  1. 4 bytes [string pointer]: main win condition. Condition is Shift JIS encoded Japanese.
  2. 4 bytes [string pointer]: alternate win condition. Condition is Shift JIS encoded Japanese.
    • 0x0 if there is no alternate win condition.
  3. 4 bytes: null bytes.
  4. 4 * 4 bytes [string pointer]: lose conditions. 0x0 is padded if there are less than 4 lose conditions.

Note that these conditions are the ones that are displayed. The ones that are actually used to determine a win / loss are written into each chapter script.

Group (GroupData)

Example:

00026cdc  0000 0001 0003 45c3 0002 c223
  1. 4 bytes [int]: counter.
  2. 4 bytes [string pointer]: Shift JIS encoded Japanese name.
  3. 4 bytes [string pointer]: mg. Also Shift JIS encoded Japanese.
Bond Support (KiznaData)

Example: Ike and Mist's bond support data

00026ef0  0003 245e 0003 26b1 010a 0000
  1. 4 bytes [string pointer]: person 1 id.
  2. 4 bytes [string pointer]: person 2 id.
  3. 1 byte: always 0x1.
  4. 1 byte [int]: critical chance boost.
  5. 2 bytes: null bytes.
Affinity Bonus (DivineParam)

Note: there's 4 null bytes preceding this section that seems to have been added by mistake.

Example:

00027120  0003 41a4 0003 41a4 0505 0000
  1. 4 bytes [string pointer]: character's affinity.
  2. 4 bytes [string pointer]: map affinity.
  3. 1 byte [2's complement int]: hit bonus.
  4. 1 byte [2's complement int]: avoid bonus.
  5. 2 bytes: null bytes.

The affinity bonus should be read like "If this character has X affinity, and the map has Y affinity, the character will gain H hit bonus and A avoid bonus".

Weapon Triangle Bonus (3SukumiData)

Example:

00027538  0003 432a 0003 42dd 010a 0000

This has identical structure to affinity bonus. Instead of character affinity and map affinity, it's character weapon type and enemy weapon type respectively. Also, instead of hit/avoid bonus, it's might/hit bonus.

Biorhythm Type (BioData)

Example:

00027568  3f80 0000 3f80 0000 3f80 0000 0000 0000

This is one sub-section I can't determine what the bytes mean. However, based on some testing, it seems:

  1. 4 bytes [int]: amplitude
  2. 4 bytes [int]: scaling
  3. 4 bytes [int]: period
  4. 4 bytes [int]: vertical scaling

I haven't determined the formula that uses these values to compute the actual biorhythm curve.

Biorhythm Level Bonus (BioParam)

Example:

00027608  3f4c cccd 0a0a 000a 1400 0000
  1. 4 bytes: unknown. Seems like some sort of threshold.
  2. 1 byte [2's complement int]: hit bonus.
  3. 1 byte [2's complement int]: avoid bonus.
  4. 1 byte: null byte.
  5. 1 byte [2's complement int]: skill activation bonus.
  6. 1 byte [2's complement int]: hidden treasure find bonus.
  7. 3 bytes: null bytes.
Pacifist (NOBTL_*)

Each pacifist entry is listed separately in section 3.5 under NOBTL_* (e.g. NOBTL_JILL).

Example: Micaiah's pacifist data

00027648  0003 2694 0003 22b2 0000 0000 0003 24e6
00027658  0000 0000 0003 30e7 0000 0000 0003 2b78
00027668  0000 0000 0003 257e 0000 0000 0000 0000
  1. 4 bytes [string pointer]: person id.
  2. (number of pacifists) * 8 bytes: pacifist.
    1. 4 bytes: null bytes.
    2. 4 bytes [string pointer]: pacifist (person id).

Note that the number of pacifists is not explicitly given. The list simply stops when the person id is 0x0.

Part 3: Miscellaneous Formulae

More formulae will be added as they are derived. Serenes Forest contains a bunch of formulae that you can reference.

Attack XP formula

Recall we have the following modifiers:

  • kill modifier (\(\text{kill}\)).
  • attack modifier (\(\text{attack}\)).
  • kill percentage (\(\text{kill_pct}\)).
  • attack percentage (\(\text{attack_pct}\)).
  • "tier 2" kill modifier (\(\text{tier2_kill}\)) .
  • "tier 3" kill modifier (\(\text{tier3_kill}\)).
  • Laguz attack modifier (\(\text{laguz_attack}\)).
  • boss kill modifier (\(\text{boss_kill}\)).

Define the following helper functions:

  • \(\text{race}(x) = \text{laguz}\) if \(x\) is a Laguz, \(\text{beorc}\) if \(x\) is a Beorc.
  • \(\text{base_level}(x) = x\)'s level displayed in-game (a value between 1 and 20).
  • \(\text{base_tier}(x) = x\)'s tier (a value between 1 and 3).
  • \(\text{actual_level}(x)\):
    • \(\text{base_tier}(x) = 1 \land \text{race}(x) = \text{beorc} \Longrightarrow \text{base_level}\)
    • \(\text{base_tier}(x) = 2 \land \text{race}(x) = \text{beorc} \Longrightarrow \text{base_level} + 20\)
    • \(\text{base_tier}(x) = 3 \land \text{race}(x) = \text{beorc} \Longrightarrow \text{base_level} + 40\)
    • Transformed \(\text{race}(x) = \text{laguz} \Longrightarrow \text{base_level} \times 2\)
    • Untransformed \(\text{race}(x) = \text{laguz} \Longrightarrow \text{base_level}\)
  • \(\text{actual_tier}(x)\):
    • \(\text{race}(x) = \text{beorc} \Longrightarrow \text{base_tier}(x)\)
    • \(\text{race}(x) = \text{laguz}\):
      • Untransformed \(\Longrightarrow 1\)
      • \(\text{base_level}(x) \lt 15 \Longrightarrow 1\)
      • \(\text{base_level}(x) \ge 1\Longrightarrow 2\)
  • \(\text{actual_power}(x)\):
    • \(\text{actual_tier}(x) = 1 \land \text{race}(x) = \text{beorc} \Longrightarrow 0\)
    • \(\text{actual_tier}(x) = 2 \land \text{race}(x) = \text{beorc} \Longrightarrow \text{tier2_kill}\)
    • \(\text{actual_tier}(x) = 3 \land \text{race}(x) = \text{beorc} \Longrightarrow \text{tier3_kill}\)
    • \(\text{actual_tier}(x) = 1 \land \text{race}(x) = \text{laguz} \Longrightarrow 0\)
    • \(\text{actual_tier}(x) = 2 \land \text{race}(x) = \text{laguz} \Longrightarrow \text{tier2_kill} \times 2\)

Consider a battle between your character \(\text{player}\) and an enemy character \(\text{enemy}\).

  • \(\text{laguz_mod}\):
    • If \(\text{race}(\text{enemy}) = \text{laguz} \Longrightarrow \text{laguz_attack}\)
    • Otherwise \(\Longrightarrow 0\)
  • \(\text{delta_level} = \text{actual_level}(\text{enemy}) - \text{actual_level}(\text{player})\)
  • \(\text{delta_power} = \text{actual_power}(\text{enemy}) - \text{actual_power}(\text{player})\)

The attack XP formula is given by: \[\text{base_attack_xp} = \left\lfloor\frac{\text{attack_pct}}{100} \times \left(\text{attack} + \left\lceil \frac{\text{delta_level}}{2} \right\rceil + \text{laguz_mod}\right)\right\rfloor\] \[\text{attack_xp} = \text{max}(1, \text{base_attack_xp})\]

The kill XP formula is given by: \[\text{base_kill_xp} = \left\lfloor\frac{\text{kill_pct}}{100} \times (\text{kill} + \text{delta_level} + \text{delta_power} + \text{boss_kill})\right\rfloor\] \[\text{kill_xp} = \text{max}(\text{base_kill_xp} + \text{attack_xp}, \text{attack_xp})\]

When your character engages but doesn't attack the enemy, your character will gain \(1\) XP.
When your character attacks but doesn't kill the enemy, your character will gain \(\text{attack_xp}\) XP.
When your character attacks and kills the enemy, your character will gain \(\text{kill_xp}\) XP.

Disclaimer: Scouring the internet, I found this link describing FE10's XP formula, which helped with deriving the formula above. However, the formula at that link is not completely correct.

Part 4: Final thoughts

Phew, that was a long one! Describing FE10Data.cms in this much detail took a while, but on the bright side, we now know where almost every single byte in the file comes from. For the remaining bytes, if you figure out what they're used for, please contact me, and I'll be sure to update this post (with credit if you wish, of course!).

Fortunately, even with the unknown bytes, we have enough information to introduce my work in progress FE10 library, which we will do in the next post. Stay tuned!

© 2018 – 2025 Evan Zhang
Available through Tor at evanzhanglvinaengh2az5hcntvolh2tu6rv2rt5pqo2lif7xiacuqid.onion.