Sprite set file: Difference between revisions

From Boktai Hacking Wiki
No edit summary
No edit summary
 
(3 intermediate revisions by the same user not shown)
Line 1: Line 1:
Sprite sets are stored in directory id_low = 0x5f29, id_high = 0xc8e5 in the [[master file table]]. Each sprite set contains multiple distinct sprites, and each sprite may be made of multiple [https://problemkaputt.de/gbatek-lcd-obj-overview.htm GBA OBJs].
Sprite sets are stored in directory id_low = 0x5f29, id_high = 0xc8e5 in the [[master file table]]. Each sprite set contains multiple distinct sprites, and each sprite may be made of multiple [https://problemkaputt.de/gbatek-lcd-obj-overview.htm GBA OBJs]. Not ''all'' sprites are stored in these sprite sets (example: Boks, bats, etc. are stored elsewhere).


= File format =
= File format =
Line 7: Line 7:
<syntaxhighlight lang="c">
<syntaxhighlight lang="c">
struct spriteset_header {
struct spriteset_header {
   u16 unknown_0x00;
   u16 palStart;   // palette start index in the sprite palettes file
   u16 spriteCount; // number of sprites in this sprite set
   u16 spriteCount; // number of sprites in this sprite set
   u16 unknown_0x04;
   u16 unknown_0x04;
Line 30: Line 30:
   u16 unknown_0x0c;
   u16 unknown_0x0c;
   u16 unknown_0x0e;
   u16 unknown_0x0e;
   u32 objStart; // Start index for this sprite in the spriteset_obj[] array
 
   // BYTE OFFSET from start of sprite_obj[] array to the 1st OBJ of this sprite
  u32 objOffset;
};
};


struct spriteset_obj {
struct spriteset_obj {
   u8 flip; // bitmask; 4 = horizontal, 8 = vertical
   u8 flip; // bitmask; 4 = horizontal, 8 = vertical. TODO: other bits?


   // 00=8x8,  01=16x8,  02=8x16
   // 00=8x8,  01=16x8,  02=8x16
Line 46: Line 48:


   // Bottom 12 bits: Start index for this OBJ in the raw tile data array
   // Bottom 12 bits: Start index for this OBJ in the raw tile data array
   // Top 4 bits: Palette index
   // Top 4 bits: Palette index, add to spriteset_header.palStart
   u16 tileAndPalette;
   u16 tileAndPalette;
};
};


struct spriteset_tile {
struct spriteset_tile {
   u8[32] data; // Raw GBA tile data
   u8 data[32]; // Raw GBA tile data (8x8 px, 4bpp)
}
};
</syntaxhighlight>
</syntaxhighlight>


The OBJ palette must be loaded manually first. There is no link between a sprite set file and its matching palette file.
= Palettes =
 
Each OBJ is linked to the palette it should use using the information in spriteset_obj.tileAndPalette and spriteset_header.palStart. However, the game engine can override this (used e.g. to animate menu cursors etc.).
 
Each game's master file table contains a single file that defines ''all'' palettes for each spriteset_sprite. This file is stored in the MFT directory with id_low = 0x4679 and id_high = 0x9a65. This directory will only contain a single file with the ID 0xc5e9 in every game.
 
The structure of this file is simple: A 4 byte header, containing the number of palettes in this file, followed by an array of that many palettes. Each palette is 0x20 bytes in size (16 entries per palette, 2 bytes per entry, see [https://problemkaputt.de/gbatek-lcd-color-palettes.htm GBATEK]). The game engine will automatically copy palettes from this file into palette RAM as neccessary.
 
To get the palette index for a spriteset_obj, add <code>(spriteset_obj.tileAndPalette >> 12) + spriteset_header.palStart</code>.


= Usage example =
= Usage example =


<syntaxhighlight lang="c">
<syntaxhighlight lang="c">
void load_sprite(spriteset_header* spriteset, int spriteId)
void load_sprite(spriteset_header* spriteset, void* spritePalettes, int spriteId)
{
{
  // spriteset should point to the start of a spriteset file
  // spritePalettes should point to the start of the sprite palettes file (there is only one per game)
   spriteset_sprite* sprites = (u8*)spriteset + spriteset->offsetToSprites;
   spriteset_sprite* sprites = (u8*)spriteset + spriteset->offsetToSprites;
  spriteset_obj* objs = (u8*)spriteset + spriteset->offsetToOBJs;
   spriteset_tile* tiles = (u8*)spriteset + spriteset->offsetToTiles;
   spriteset_tile* tiles = (u8*)spriteset + spriteset->offsetToTiles;


   spriteset_sprite* sprite = &sprites[spriteId];
   spriteset_sprite* sprite = &sprites[spriteId];
   // do something with sprite
   // do something with sprite
  spriteset_obj* objs = (u8*)spriteset + spriteset->offsetToOBJs + sprite->objOffset;


   for (int objIndex=0; objIndex < sprite->objCount; objIndex++) {
   for (int objIndex=0; objIndex < sprite->objCount; objIndex++) {
     spriteset_obj* obj = &objs[sprite->objStart + objIndex];
     spriteset_obj* obj = &objs[objIndex];
     // do something with obj
     // do something with obj


     int tileIndex = obj->tileAndPalette & 0xfff;
     int firstTileIndex = obj->tileAndPalette & 0xfff;
     spriteset_tile* firstTile = &tiles[tileIndex];
     spriteset_tile* firstTile = &tiles[firstTileIndex];
    // do something with the tiles
     // number of tiles is implied by sprite->shape
     // number of tiles is implied by sprite->shape
     // do something with the tiles
     // for OBJs with a height of more than 1 tile, successive rows of tiles are stored 16 tiles apart.
    // example: assuming startTileIndex = 4 and shape = 8x16: tile indices are 4 and 20.
 
    // color lookup example:
    int pixelColor = firstTile->data[0] & 0xf; // extract color of 1st pixel in the tile
    int paletteIndex = spriteset->palStart + (obj->tileAndPalette >> 12);
    // First 4 bytes are an ignoreable header, and each palette is 0x20 bytes in size (16 colors * 2 bytes per color)
    u16* palettePtr = (u8*)spritePalettes + 4 + 0x20*paletteIndex;
    u16 paletteEntry = palettePtr[pixelColor];
   }
   }
}
}
</syntaxhighlight>
</syntaxhighlight>
= Sprite set list =
== Boktai 1 ==
{| class="wikitable exportable"
! File ID || Description
|-
| 1bc7 || Flamethrowers
|-
| 2117 || Muspell
|-
| 2137 || Carmilla
|-
| 31ce || Hel 2
|-
| 41a8 || Multiplayer menus
|-
| 4e23 || Garmr
|-
| 575d || Count (with coat)
|-
| 576d || Count (without coat)
|-
| 66a8 || Solar sensor help screens
|-
| 746a || Armors (shield only)
|-
| 766a || Armors (sword and shield)
|-
| 769a || Armors (hammer and shield)
|-
| 786a || Armors (sword only)
|-
| 789a || Armors (hammer only)
|-
| 7a6a || Armors (no weapon)
|-
| 8ab4 || Select menu – items & weapon
|-
| a1f7 || Solar tree
|-
| a3ae || Splash screens, settings, new game, etc.
|-
| a493 || Bank/Loans
|-
| a6aa || Select menu – map
|-
| a9c5 || Markers (Immortal, Undead, Arrows, Trap) & Yes/No
|-
| aac7 || Main menu
|-
| b11e || Hel 1
|-
| e894 || Gun parts 1
|-
| edbe || Unknown – maybe link battle?
|-
| fa48 || Gun parts 2 (Dark gun, star lens, astro/infinite battery)
|-
| ff88 || Dungeon titles (shown when entering a dungeon)
|}
== Boktai 2 ==
{| class="wikitable exportable"
! File ID || Description
|-
| 0c23 || Big ray of sun (end of Jormy?)
|-
| 0f54 || Main menu
|-
| 1774 || Big picture of Django with the Sol de Vice
|-
| 18c2 || Multiplayer (contains unused JOYSPOT image)
|-
| 1b12 || Solar sensor help screens
|-
| 1bc7 || Flamethrowers
|-
| 24a0 || “boktai2” and Dream Avenue xF screens
|-
| 3f12 || World map screen
|-
| 5d04 || Inventory icons (and MegaMan and ShadeMan icons)
|-
| 5d36 || ShadeMan
|-
| 6893 || Bank/Loans (contains unused JP art)
|-
| 7ee2 || Splash screens and region select
|-
| 82f1 || Markers (Undead, Immortal, Trap, arrows), mission completed/failed, purification completed
|-
| 9524 || Jormy
|-
| 9abf || Library
|-
| ae52 || Skeletons and Liches
|-
| af82 || MegaMan
|-
| b991 || Candle on a box (where is this used?)
|-
| c153 || Mall (e.g. solar forging, shops, …)
|-
| cef0 || Bank/Loans (and “Restart”?)
|-
| d27a || Dialog box portraits
|-
| d3ca || Duneyrr
|-
| d3da || Ringo/Dainn
|-
| d3ea || Cheyenne
|-
| d3fa || Durathror
|-
| d40a || Dvalinn
|-
| de23 || Django/Sabata
|-
| dfce || Smith and Marcello
|-
| dfde || Lita
|-
| dfee || Zazie
|-
| dffe || Violet and her doll
|-
| e00e || Nero
|-
| e01e || Kid
|-
| e02e || Librarian
|-
| e03e || ??? and the coffin seller
|-
| e04e || Clocktower guy and old guy
|-
| e2de || Carmilla
|-
| e349 || Dainn
|-
| ef43 || In-game menu
|-
| f099 || Chandelier
|}
== Boktai 3 ==
{| class="wikitable exportable"
! File ID || Description
|-
| 0658 || MegaMan
|-
| 1289 || Multiplayer
|-
| 158a || Ringo/Dainn
|-
| 1778 || Cheyenne
|-
| 1bc7 || Flamethrowers
|-
| 2bec || Gun parts
|-
| 32ba || Bank/Loans
|-
| 35e3 || Trinity
|-
| 3d99 || Bike
|-
| 3db6 || Critter that steals your solar nut in the tutorial
|-
| 530d || Select menu, San Miguel, maybe unused stuff?
|-
| 546b || Cannons
|-
| 5d04 || Inventory icons (and Count/MegaMan/Django icons)
|-
| 731d || Main menu
|-
| 7516 || Chair
|-
| 761e || Armors
|-
| 7c8b || Multiplayer (wireless)
|-
| 82f1 || Markers (Undead, Immortal, Trap, arrows), mission completed/failed, purification completed
|-
| 999b || Torch
|-
| a2db || World map
|-
| ae52 || Skeletons
|-
| af82 || MegaMan
|-
| b3de || Vanargand
|-
| b991 || Candle on a box (where is this used?)
|-
| de23 || Django/Sabata
|-
| df11 || Solar sensor help screens
|-
| dfce || Smith/Marcello
|-
| dfde || Lita
|-
| dfee || Zazie
|-
| dffe || Violet and her doll
|-
| e00e || Nero
|-
| e01e || Kid
|-
| e02e || Librarian
|-
| e03e || ??? and the bike shop guy
|-
| e04e || Clocktower guy and old guy
|-
| e2ab || Splash screens and region select
|-
| e2de || Carmilla
|-
| e2ee || Townspeople
|-
| e2fe || Gari
|-
| e349 || Dainn
|-
| e89f || Dungeon titles (shown when entering a dungeon)
|-
| f099 || Chandelier
|-
| f761 || Count
|-
| f771 || Bird and lots of “No”?
|-
| f7d2 || Trance
|-
| fb81 || Ratatosk
|}


[[Category:Documentation]] [[Category:File formats]]
[[Category:Documentation]] [[Category:File formats]]
[[Category:Boktai 1]] [[Category:Boktai 2]] [[Category:Boktai 3]]
[[Category:Boktai 1]] [[Category:Boktai 2]] [[Category:Boktai 3]]

Latest revision as of 16:22, 18 September 2024

Sprite sets are stored in directory id_low = 0x5f29, id_high = 0xc8e5 in the master file table. Each sprite set contains multiple distinct sprites, and each sprite may be made of multiple GBA OBJs. Not all sprites are stored in these sprite sets (example: Boks, bats, etc. are stored elsewhere).

File format

TODO: very incomplete

struct spriteset_header {
  u16 palStart;    // palette start index in the sprite palettes file
  u16 spriteCount; // number of sprites in this sprite set
  u16 unknown_0x04;
  u16 unknown_0x06;
  u16 unknown_0x08;
  u16 unknown_0x0a;

  u32 offsetToSprites; // byte offset from start of this struct to the spriteset_sprite[spriteCount] array
  u32 offsetToUnknown_0x10;
  u32 offsetToOBJs;    // byte offset from start of this struct to the spriteset_obj[] array
  u32 offsetToUnknown_0x18;
  u32 offsetToTiles;   // byte offset from start of this struct to the spriteset_tile[] array
};

struct spriteset_sprite {
  u16 unknown_0x00;
  u16 objCount; // Sprite is made up of this many OBJs
  u16 unknown_0x04;
  u16 unknown_0x06;
  u16 unknown_0x08;
  u16 unknown_0x0a;
  u16 unknown_0x0c;
  u16 unknown_0x0e;

  // BYTE OFFSET from start of sprite_obj[] array to the 1st OBJ of this sprite
  u32 objOffset;
};

struct spriteset_obj {
  u8 flip; // bitmask; 4 = horizontal, 8 = vertical. TODO: other bits?

  // 00=8x8,   01=16x8,  02=8x16
  // 04=16x16, 05=32x8,  06=8x32
  // 08=32x32, 09=32x16, 10=16x32
  // 12=64x64, 13=64x32, 14=32x64
  u8 shape;

  i16 xOffset;
  i16 yOffset;

  // Bottom 12 bits: Start index for this OBJ in the raw tile data array
  // Top 4 bits: Palette index, add to spriteset_header.palStart
  u16 tileAndPalette;
};

struct spriteset_tile {
  u8 data[32]; // Raw GBA tile data (8x8 px, 4bpp)
};

Palettes

Each OBJ is linked to the palette it should use using the information in spriteset_obj.tileAndPalette and spriteset_header.palStart. However, the game engine can override this (used e.g. to animate menu cursors etc.).

Each game's master file table contains a single file that defines all palettes for each spriteset_sprite. This file is stored in the MFT directory with id_low = 0x4679 and id_high = 0x9a65. This directory will only contain a single file with the ID 0xc5e9 in every game.

The structure of this file is simple: A 4 byte header, containing the number of palettes in this file, followed by an array of that many palettes. Each palette is 0x20 bytes in size (16 entries per palette, 2 bytes per entry, see GBATEK). The game engine will automatically copy palettes from this file into palette RAM as neccessary.

To get the palette index for a spriteset_obj, add (spriteset_obj.tileAndPalette >> 12) + spriteset_header.palStart.

Usage example

void load_sprite(spriteset_header* spriteset, void* spritePalettes, int spriteId)
{
  // spriteset should point to the start of a spriteset file
  // spritePalettes should point to the start of the sprite palettes file (there is only one per game)

  spriteset_sprite* sprites = (u8*)spriteset + spriteset->offsetToSprites;
  spriteset_tile* tiles = (u8*)spriteset + spriteset->offsetToTiles;

  spriteset_sprite* sprite = &sprites[spriteId];
  // do something with sprite

  spriteset_obj* objs = (u8*)spriteset + spriteset->offsetToOBJs + sprite->objOffset;

  for (int objIndex=0; objIndex < sprite->objCount; objIndex++) {
    spriteset_obj* obj = &objs[objIndex];
    // do something with obj

    int firstTileIndex = obj->tileAndPalette & 0xfff;
    spriteset_tile* firstTile = &tiles[firstTileIndex];
    // do something with the tiles
    // number of tiles is implied by sprite->shape
    // for OBJs with a height of more than 1 tile, successive rows of tiles are stored 16 tiles apart.
    // example: assuming startTileIndex = 4 and shape = 8x16: tile indices are 4 and 20.

    // color lookup example:
    int pixelColor = firstTile->data[0] & 0xf; // extract color of 1st pixel in the tile
    int paletteIndex = spriteset->palStart + (obj->tileAndPalette >> 12);
    // First 4 bytes are an ignoreable header, and each palette is 0x20 bytes in size (16 colors * 2 bytes per color)
    u16* palettePtr = (u8*)spritePalettes + 4 + 0x20*paletteIndex;
    u16 paletteEntry = palettePtr[pixelColor];
  }
}

Sprite set list

Boktai 1

File ID Description
1bc7 Flamethrowers
2117 Muspell
2137 Carmilla
31ce Hel 2
41a8 Multiplayer menus
4e23 Garmr
575d Count (with coat)
576d Count (without coat)
66a8 Solar sensor help screens
746a Armors (shield only)
766a Armors (sword and shield)
769a Armors (hammer and shield)
786a Armors (sword only)
789a Armors (hammer only)
7a6a Armors (no weapon)
8ab4 Select menu – items & weapon
a1f7 Solar tree
a3ae Splash screens, settings, new game, etc.
a493 Bank/Loans
a6aa Select menu – map
a9c5 Markers (Immortal, Undead, Arrows, Trap) & Yes/No
aac7 Main menu
b11e Hel 1
e894 Gun parts 1
edbe Unknown – maybe link battle?
fa48 Gun parts 2 (Dark gun, star lens, astro/infinite battery)
ff88 Dungeon titles (shown when entering a dungeon)

Boktai 2

File ID Description
0c23 Big ray of sun (end of Jormy?)
0f54 Main menu
1774 Big picture of Django with the Sol de Vice
18c2 Multiplayer (contains unused JOYSPOT image)
1b12 Solar sensor help screens
1bc7 Flamethrowers
24a0 “boktai2” and Dream Avenue xF screens
3f12 World map screen
5d04 Inventory icons (and MegaMan and ShadeMan icons)
5d36 ShadeMan
6893 Bank/Loans (contains unused JP art)
7ee2 Splash screens and region select
82f1 Markers (Undead, Immortal, Trap, arrows), mission completed/failed, purification completed
9524 Jormy
9abf Library
ae52 Skeletons and Liches
af82 MegaMan
b991 Candle on a box (where is this used?)
c153 Mall (e.g. solar forging, shops, …)
cef0 Bank/Loans (and “Restart”?)
d27a Dialog box portraits
d3ca Duneyrr
d3da Ringo/Dainn
d3ea Cheyenne
d3fa Durathror
d40a Dvalinn
de23 Django/Sabata
dfce Smith and Marcello
dfde Lita
dfee Zazie
dffe Violet and her doll
e00e Nero
e01e Kid
e02e Librarian
e03e ??? and the coffin seller
e04e Clocktower guy and old guy
e2de Carmilla
e349 Dainn
ef43 In-game menu
f099 Chandelier

Boktai 3

File ID Description
0658 MegaMan
1289 Multiplayer
158a Ringo/Dainn
1778 Cheyenne
1bc7 Flamethrowers
2bec Gun parts
32ba Bank/Loans
35e3 Trinity
3d99 Bike
3db6 Critter that steals your solar nut in the tutorial
530d Select menu, San Miguel, maybe unused stuff?
546b Cannons
5d04 Inventory icons (and Count/MegaMan/Django icons)
731d Main menu
7516 Chair
761e Armors
7c8b Multiplayer (wireless)
82f1 Markers (Undead, Immortal, Trap, arrows), mission completed/failed, purification completed
999b Torch
a2db World map
ae52 Skeletons
af82 MegaMan
b3de Vanargand
b991 Candle on a box (where is this used?)
de23 Django/Sabata
df11 Solar sensor help screens
dfce Smith/Marcello
dfde Lita
dfee Zazie
dffe Violet and her doll
e00e Nero
e01e Kid
e02e Librarian
e03e ??? and the bike shop guy
e04e Clocktower guy and old guy
e2ab Splash screens and region select
e2de Carmilla
e2ee Townspeople
e2fe Gari
e349 Dainn
e89f Dungeon titles (shown when entering a dungeon)
f099 Chandelier
f761 Count
f771 Bird and lots of “No”?
f7d2 Trance
fb81 Ratatosk