Breath of Fire 3 - Data documentation

BreathOfFireLogo

In this documentation, you will find all the information on the data and data structures used in Breath Of Fire 3.

This documentation is incomplete and will be updated as we learn more about the different formats used. It's a long job, so please be patient!

The "EMI" files format

This document is still being researched and is not yet complete. This means that some information could be erroneous or simply missing.

Table of content:

  1. Introduction
  2. Header
  3. Data
  4. Libraries and tools

Introduction

The EMI files are the containers for all the game's data, apart from the videos. These files are located in the BIN directory on the CD, themselves organized by category in sub-folders.

They are binary files, with a header containing a table of contents of the data they contain, followed by the data in raw format.

Information

The same data can be found identically in several EMI files.

The reason for this is that loading several pieces of data in different places on a disk can be slow. So, if the data to be loaded is grouped together in the same place, loading will be much faster, limiting the number of readings required.

The header structure is composed as follows:

  • The number of data contained in the file,
  • A magic word MATH_TBL,
  • A table of contents of the various data.

Let's take a closer look at the data in this header:

FromToSizeTypeDescription
0x000x034u32Data entry count
0x040x074???? Version ???
0x080x0F8u8[8]Magic word "MATH_TBL"

Just after these 16 bytes, we have the table of contents. This consists of several (same number as the number of data) “lines” indicating:

  • The size of the data,
  • Data parameters (details below),
  • The first 4 bytes of the data,
  • Data type (to be confirmed),

Here are the details for one line in the table of content:

FromToSizeTypeDescription
0x000x034u32Data size
0x040x074u32Pointer in RAM
0x080x0B4u8[4]First 4 bytes
0x0C0x0D2u16Data type ?
0x0E0x0F2?Garbage data

A list of known data types:

Type IDDescription
3Image
6Header samples audio (PSX/VH)
7Data samples audio (PSX/VB)
10Music Sequencer/Midi (PSX/SEQ)

A lot of data has type 0, but it's varied data that doesn't correspond to a particular type.

Data

Note that in EMI files, the start of the data is always on a multiple of 2048 (0x800).

For example:

  • The start of the first data in EMI files will always be at file position 2048 (0x800),
  • The position of the second data will depend on:
    • where the previous data begins,
    • the size of the previous data,
    • finding the next multiple of 2048 in relation to the previous data.

If we were to write it in code, it would look like this (In C++, the binary shift loses data):

unsigned int next_data_position = current_data_position + (current_data_size + 0x7FF >> 0xB) * 0x800;

/*
For example, my first data starts at 0x800 and has a size of 2100 bytes
To find the next data, I will do:
- First let's understand the complex part: "current_data_size + 0x7FF >> 0xB" -> "(2100 + 2047) >> 11" -> "4147 >> 11" -> "2".
  This is the times we have to multiple 2048 from the current data to find the next one.
  
- Let's continue: "current_data_position + (current_data_size + 0x7FF >> 0xB) * 0x800" -> "2048 + 2 * 2048" -> 6144 (0x1800).
  This is the actual position of the next data in the file
*/

Between two data, you will see mostly garbage bytes like a series of 2E or 5F.

Libraries and tools

You can use the Emi Extractor tool made by Navarchos/Red Herring to extract the data from your EMI files.

Texture and Palette

This document is still being researched and is not yet complete. This means that some information could be erroneous or simply missing.

Table of content:

  1. Introduction
  2. Texture
  3. Palette
  4. How palettes are used in textures
  5. Todo - Missing elements

Introduction

Textures and palettes represent most of the visuals that will appear on screen:

  • Characters, monsters, ...
  • Texts and menus,
  • Textures applied to 3D meshes,
  • ...

In this document, we'll look at how these data are encoded and how they relate to each other.

Texture

Textures in the EMI files all seem to have the same Data Type, which is 3. They have no header, and no information about their width, height, bits per pixel and palette used. There's only raw texture data.

Information

There are ways of finding this information relatively easily:

  • Find out what platform the game is running on (Playstation), and what formats are available on it,
  • Use raw image-reading software (such as TileMolester), and change the settings until you find the right format.

To compress textures size, a palette system (also known as CLUT: Color Lookup table) allows you to use only a color index found in a table:

  • If an image's pixels are encoded in 4 bits, then the image can contain 16 different colors,
  • If an image's pixels are encoded in 8 bits, then the image can contain 256 different colors.

However, in BoF3, two formats in particular stand out:

  • For textures encoded in 4 bits per pixel, the width is 128 pixels,
  • For textures encoded in 8 bits per pixel, the width is 64 pixels.

Keep in mind that there are other possible formats.

Textures can themselves be split into several tiles, with a size not specified in the data. (This information is probably hardcoded directly when the texture is used)

Now, if you try to visualize a texture with the above information, you'll see elements, but in a “broken” way. This is because the texture is composed of “strips”.

In fact, to see the texture properly, you'll need to glue the strips together correctly:

  • A stripe is 256 pixels wide by 32 pixels high,
  • This seems to apply to both 4bpp and 8bpp images.
Stripped texture
Example stripped and unstripped texture

Here's an algorithm in C++ to perform this transformation from one data array to another of the same size:

u64 ComputeUnstrippedIndex(u64 stripped_index)
{
    auto stripped_x = stripped_index % STRIPPED_TEXTURE_WIDTH;
    auto stripped_y = stripped_index / STRIPPED_TEXTURE_WIDTH;

    auto unstripped_x = stripped_x + ((stripped_y & 0x20) >> 5) * STRIPPED_TEXTURE_WIDTH;
    auto unstripped_y = (STRIPE_HEIGHT * stripped_y / 64)) + (stripped_y & 0x1F);

    return unstripped_x + unstripped_y * STRIPE_WIDTH;
}

int main()
{
    // Read the stripped texture data, and create another array of the same size (initialized to 0)
    vector<u8> stripped_texture_data  = ReadData();
    vector<u8> unstripped_texture_data(input_data.size());

    // We compute the index of each "unstripped" pixel, and push it to the output
    for(u64 i = 0; i < stripped_texture_data.size(); ++i)
        unstripped_texture_data[ComputeUnstrippedIndex(i)] = stripped_texture_data[i];

    // Just write the new data in a file, the texture has now a width of 256, with the encoding
    WriteData(unstripped_texture_data); 
}

Palette

Palettes are used in textures to provide them colors. They are composed by multiple rows of colors. The number of max rows possible in a palette is not defined, yet.

Again, like textures, palettes does not have any header, but you can use the same tools to find their content.

Palette
Example of a palette

Most of the time, palettes are not too far from the textures they are used (in the same EMI file), but for a very few of them, they seem to be loaded once (NEED TO BE CONFIRMED WITH MORE RESEARCH).

Generally, when a palette is used:

  • In a 4bpp texture: each row of this palette would be composed with 16 colors,
  • In a 8bpp texture: NEED MORE INVESTIGATION.

Again, other formats are possible.

Now, each Color of the palette is encoded in 16 bits, (more info on TIM_format):

REDGREENBLUESTP*
0 to 4 (5 bits)5 to 9 (5 bits)10 to 14 (5bits)15

The STP is the "Special transparency processing": Because by default, the color Black (r0, g0, b0) is treated as transparent, unless the STP bit is set to 1.

Now, let's do some code! How to convert those 16 bits color to a more standard 24 bits ?

using u8 = uint8_t;
using u16 = uint16_t;

// A classic 24 bits color
struct ColorRgb
{
    u8 r{};
    u8 g{};
    u8 b{};
};

ColorRgb Convert16BitsTo24Bits(const u16 pixel)
{
    // I'm using at least C++20 here (https://en.cppreference.com/w/cpp/language/aggregate_initialization)
    // but you can initialize the old fashion way too.
    return
    {
        // ([SBBBBBGGGGGRRRRR] << 3) & 0xF8 = [BBBGGGGGRRRRR000] & [0000000011111000] = [RRRRR000]
        .r = static_cast<u8>((pixel << 3) & 0xF8),
        // ([SBBBBBGGGGGRRRRR] >> 2) & 0xF8 = [00SBBBBBGGGGGRRR] & [0000000011111000] = [GGGGG000]
        .g = static_cast<u8>((pixel >> 2) & 0xF8),
        // ([SBBBBBGGGGGRRRRR] >> 7) & 0xF8 = [0000000SBBBBBGGG] & [0000000011111000] = [BBBBB000]
        .b = static_cast<u8>((pixel >> 7) & 0xF8)
    };
}

You are now the master of colors!

TIPS

Palettes of the games can be found in EMI files by using their Pointer/RAM value of the EMI's table of content:

The Pointer/RAM of palettes should be between 0x80033XXX and 0x8003AXXX.

How palettes are used in textures

From initial research and results, it would appear that for 4BPP textures, a row from the palette is applied to a tile of a texture: 1 row of 16 colors == Colors of a single tile.

Still needs a lot of investigation on this side.

Todo - Missing elements

  • Find where are stored the tiles structure of a texture,
  • Find a way to know which palettes are used in a texture,
  • Find a way to know which rows in palette is used in a tile,

STATUS.EMI

This document is still being researched and is not yet complete. This means that some information could be erroneous or simply missing.

Table of content:

  1. Introduction
  2. The StatusMenu texture
  3. Two palettes for menu

Introduction

This file represent all the Status Menu data, when you press Square in game.

The StatusMenu textures

This texture is located at the second index of the STATUS.EMI file, and contains tiles of:

  • Characters portraits
  • Menu Icons
  • Menu Layout

The texture has a width of 128 pixel, and is encoded in 4bpp.

For the characters portraits, they are tiles of 40x48. They use the following palette in file like this:

CharactersPalette indexPalette Row
Adult Ryufourth01
Young Ryufourth11
Tepofourth12
Young Reifourth13
Adult Reifourth13
Young Ninafourth15
Adult Ninafourth15
Momofourth00
Garrfourth02
Picofifth13
Dragonfourth14

Information

Looks like the tiles are aligned in rows of 256 x 40 pixels

Two palettes for menu

They are the forth and fifth data of the STATUS.EMI: They contain characters portraits and menu colors.

Todo

Still a lot to do in this file!