Skip to main content

CEBK ("KBEC") - Cell Bank

Author(s): Gonhex
Research: NOCASH, Gonhex

The cell bank creates a number of cells by combining tiles and selecting a palette for each cell. It usually operates on dimensionless (1D mapped) character graphics and is used in combination with the animation runtime to display dynamic objects.

Table of Contents


Data Structure

Section Container

struct ContainerSectionCEBK
{
/* 0x0 */ struct NitroSectionHeader sectionHeader;
/* 0x8 */ struct ContainerCEBK sectionData;
/* append */ uint8_t paddingDWORD[?];
}; // entry size = sectionHeader.lengthSection
Field NameDescriptionData Type
sectionHeaderHeader of this section. sectionHeader.signature = "KBEC".NitroSectionHeader
sectionDataContent of this section.ContainerCEBK
paddingDWORDPadding for DWORD alignment, if LABL-section follows.uint8_t[]

CEBK Container

struct ContainerCEBK
{
// header
/* 0x00 */ uint16_t numberCells;
/* 0x02 */ uint16_t useBounds; // rename?
/* 0x04 */ uint32_t offsetDataCell;
/* 0x08 */ uint32_t sizeBoundary;
/* 0x0C */ uint32_t unknown0;
/* 0x10 */ uint32_t unknown1;
/* 0x14 */ uint32_t unknown2;

// data
/* offsetDataCell */ struct Cell dataCell[numberCells];
/* offsetDataCell + numberCells * (8 << useBounds) */ struct ObjectAttribute dataAttributes[?];
}; // entry size = sectionHeader.lengthSection - 0x8
Field NameDescriptionData Type
numberCellsNumber of cells.uint16_t
useBoundsExtended cell struct, if == 1.uint16_t
offsetDataCellOffset to the cell table data section relative to ContainerCEBK.uint32_t
sizeBoundarySize boundary, result = sizeBoundary << 6.uint32_t
unknown0Unused offset?uint32_t
unknown1Unused offset?uint32_t
unknown2Unused offset?uint32_t
dataCellCell configuration table.Cell[]
dataAttributesObject attributes.ObjectAttribute[]

Cell

struct Cell
{
// header
/* 0x0 */ uint16_t numberObjects;
/* 0x2 */ uint8_t unknown0;
/* 0x3 */ uint8_t unknown1;
/* 0x4 */ uint32_t offsetObject;
// }; // entry size = 0x8, if useBounds == 0

// optional. Are they redundant even if used?
/* 0x8 */ int16_t boundRight;
/* 0xA */ int16_t boundBottom;
/* 0xC */ int16_t boundLeft;
/* 0xE */ int16_t boundTop;
}; // entry size = 0x10, if useBounds == 1
Field NameDescriptionData Type
numberObjectsNumber of objects building this cell.uint16_t
unknown0What is this?uint8_t
unknown1What is this?uint8_t
offsetObjectOffset within object attribute data.uint32_t
boundRightRight border of the cell.int16_t
boundBottomBottom border of the cell.int16_t
boundLeftLeft border of the cell.int16_t
boundTopTop border of the cell.int16_t

Object Attributes

struct ObjectAttribute
{
/* 0x0 */ uint16_t positionY : 8; // 0b00000000'11111111
uint16_t transformable : 1; // 0b00000001'00000000
uint16_t doubleSized : 1; // 0b00000010'00000000
uint16_t objectMode : 2; // 0b00001100'00000000
uint16_t mosaic : 1; // 0b00010000'00000000
uint16_t paletteMode : 1; // 0b00100000'00000000
uint16_t keyShape : 2; // 0b11000000'00000000

/* 0x2 */ uint16_t positionX : 9; // 0b00000001'11111111
uint16_t transformParameter : 3; // 0b00001110'00000000
uint16_t horizontalFlip : 1; // 0b00010000'00000000
uint16_t verticalFlip : 1; // 0b00100000'00000000
uint16_t keySize : 2; // 0b11000000'00000000

/* 0x4 */ uint16_t tileIndex : 10; // 0b00000011'11111111
uint16_t priority : 2; // 0b00001100'00000000
uint16_t paletteIndex : 4; // 0b11110000'00000000
}; // entry size = 0x6
Field NameDescriptionData Type
positionYY position, cast to a signed value.uint16_t : 8
transformableEnable flag for affine transformations.uint16_t : 1
doubleSizedDouble the size transformable. Not displayed if this flag is used without transformable.uint16_t : 1
objectMode0 = normal, 1 = semi-transparent, 2 = OBJ window, 3 is not allowed.uint16_t : 2
mosaicEnable mosaic.uint16_t : 1
paletteModeSelect type of the palette. 0= 16 colors, 1 = 256 colors per palette.uint16_t : 1
keyShapeShape: 0 = square, 1 = horizontal, 2 = vertical, 3 is not allowed. See Dimension Keys.uint16_t : 2
positionXX position, cast to a signed value.uint16_t : 9
transformParameter(TODO) Currently not documented here. Read about it on GBATEK.uint16_t : 3
horizontalFlipFlip horizontally, if enabled.uint16_t : 1
verticalFlipFlip vertically, if enabled.uint16_t : 1
keySizeUsed to get the size of the OAM cell, see Dimension Keys.uint16_t : 2
tileIndexIndex of the tile selecten from the character graphic.uint16_t : 10
priorityDisplay priority if overlapping. Lower value = higher priority.uint16_t : 2
paletteIndexIndex of the palette used for this OAM cell.uint16_t : 4

Specification

Dimension Keys

The width and height of an object isn't defined directly. Instead it uses key values, which allow to reconstruct the size. For example if keyShape == 0, the cell will be a square which size depends on keySize. The dimenions can be converted using lookup tables.

Read dimensions

const uint16_t tableSizeInTiles[4][4] = // [keyShape][keySize]
{
{ 1, 2, 4, 8 },
{ 2, 4, 4, 8 },
{ 1, 1, 2, 4 },
{ 1, 2, 4, 8 } // copy of row 0 for reversed index (3 - shape)
};

uint16_t getWidthInTiles()
{
return tableSizeInTiles[keyShape][keySize];
}

uint16_t getHeightInTiles()
{
return tableSizeInTiles[3 - keyShape][keySize];
}

Write dimensions

const uint16_t tableShape[4][4] = // [width][height]
{ //h1 h2 h4 h8
{ 0, 2, 2, 0 },//w1
{ 1, 0, 2, 0 },//w2
{ 1, 1, 0, 2 },//w4
{ 0, 0, 1, 0 } //w8
};

const uint16_t tableSize[4][4] = // [width][height]
{ //h1 h2 h4 h8
{ 0, 0, 1, 0 },//w1
{ 0, 1, 2, 0 },//w2
{ 1, 2, 2, 3 },//w4
{ 0, 0, 3, 3 } //w8
};

void setSizeInTiles(uint16_t widthInTiles, uint16_t heightInTiles)
{
const uint16_t width = (widthInTiles >= 8) ? 3 : (widthInTiles >> 1);
const uint16_t height = (heightInTiles >= 8) ? 3 : (heightInTiles >> 1);

keySize = tableSize[width][height];
keyShape = tableShape[width][height];
}

Files


TODO

  • Document the transform parameters. transformParameter may also need to be fused with horizontalFlip and verticalFlip, from uint16_t : 3 to uint16_t : 5.
  • Research unknown0 and unknown1 in cell