This shows you the differences between two versions of the page.
| Both sides previous revision Previous revision Next revision | Previous revision | ||
| 
                    banjo_kazooie:sprites [2025/04/12 12:23] snowboundmage2  | 
                
                    banjo_kazooie:sprites [2025/05/01 12:05] (current) snowboundmage2 removed emojis for easier reading  | 
            ||
|---|---|---|---|
| Line 1: | Line 1: | ||
| ====== Sprite Data ====== | ====== Sprite Data ====== | ||
| - | This page documents the binary layout of sprite files found in the Banjo-Kazooie ROM (Nintendo 64). These files contain image data composed of one or more frames, each composed of a series of image chunks. | + | This page documents the binary sprite format derived from Banjo-Kazooie’s N64 rom. It supports both single-chunk and multi-frame layouts, with support for various pixel formats and chunk-based rendering. | 
| - | All multi-byte values are stored in **big endian**. | + | All integers are **big-endian** unless stated otherwise. | 
| - | Unless otherwise stated, all examples use sprite ID `06EB`. | + | |
| - | ===== File Structure ===== | + | ---- | 
| - | A sprite file is divided into the following sections: | + | ===== Overview ===== | 
| - | * [[#header|Header (0x00–0x10)]] | + | This format is used for static or animated sprite graphics in the ROM. Depending on frame count, it can store a simple image or a complex animation. | 
| - | * [[#frame_offset_table|Frame Offset Table (0x10–...)]] | + | |
| - | * [[#frame_data|Frame Data]] | + | |
| - | * [[#chunk_data|Chunk Data (WIP)]] | + | |
| - | ===== Header ===== | + | ---- | 
| - | The header occupies the first 0x10 bytes of the file and contains basic metadata about the sprite. | + | |
| - | ^ Offset ^ Size ^ Description ^ | + | ===== Reference Table ===== | 
| - | | 0x00 | 2 bytes | Frame count (`frameCnt`) | | + | |
| - | | 0x02 | 2 bytes | Format ID / type (`type`) — usually `0x0100` | | + | |
| - | | 0x04 | 2 bytes | Unknown (`unk4`) | | + | |
| - | | 0x06 | 2 bytes | Unknown (`unk6`) | | + | |
| - | | 0x08 | 2 bytes | Possibly default width (`unk8`) | | + | |
| - | | 0x0A | 2 bytes | Possibly default height (`unkA`) | | + | |
| - | | 0x0C | 4 bytes | Bitfield flags (`unkC`) — see below | | + | |
| - | | 0x10 | ... | Frame offset table (`offsets[]`) | | + | |
| - | The bitfield at `0x0C` may contain rendering options or format hints. | + | ==== File Header (offsets 0x00–0x03) ==== | 
| - | ==== Bitfield at 0x0C ==== | + | ^ Field ^ Offset ^ Type ^ Description  ^ | 
| + | | Frame Count | 0x00 | `uint16_t` | Number of frames  | | ||
| + | | Format ID | 0x02 | `uint16_t` | Format identifier  | | ||
| - | The 32-bit word at offset `0x0C` includes the following layout: | + | ==== Format ID Mappings ==== | 
| - | ^ Bits ^ Field ^ Description ^ | + | ^ Format ID ^ Enum ^ Description  ^ Bits per Pixel ^ | 
| - | | 31–28 | `bit31` | Possibly render mode | | + | | 0x0001  | CI4 | 4-bit color indexed  | 4 | | 
| - | | 27–25 | `bit27` | Possibly format ID | | + | | 0x0004  | CI8 | 8-bit color indexed  | 8 | | 
| - | | 24–23 | `bit24` | Possibly compression | | + | | 0x0020  | I4 | 4-bit intensity  | 4 | | 
| - | | 22–21 | `bit22` | Possibly blending or layer mode | | + | | 0x0040  | I8 | 8-bit intensity  | 8 | | 
| - | | 20–0  | `pad` | Unused or reserved | | + | | 0x0100  | IA4 | 4-bit intensity(with alpha)  | 4 | | 
| + | | 0x0200  | IA8 | 8-bit intensity(with alpha)  | 8 | | ||
| + | | 0x0400  | RGBA16  | 16-bit RGBA (5-5-5-1 format) | 16 | | ||
| + | | 0x0800  | RGBA32  | 32-bit RGBA | 32 | | ||
| - | ===== Frame Offset Table ===== | + | ---- | 
| - | Immediately following the header is the frame offset table. This table contains 32-bit pointers to each frame's data section, relative to the start of the offset table. | + | |
| - | ^ Offset ^ Size ^ Description ^ | + | ===== Single Chunk Sprite Layout ===== | 
| - | | 0x10 | 4 bytes | Frame 0 offset: `0x00000004` (points to 0x18 from file start) | | + | |
| - | Total size of this section = `4 × frameCnt` | + | Used if `frameCount > 256` (`0x0100`). | 
| - | ===== Frame Data ===== | + | ==== Structure ==== | 
| - | Each frame starts with a 20-byte structure: | + | ^ Field ^ Offset ^ Type ^ Description  ^ | 
| + | | X | 0x08 | `int16_t`  | X position  | | ||
| + | | Y | 0x0A | `int16_t`  | Y position  | | ||
| + | | Width | 0x0C | `uint16_t` | Width in pixels  | | ||
| + | | Height  | 0x0E | `uint16_t` | Height in pixels  | | ||
| + | | Data Offset | ~0x10+ | — | 8-byte aligned after header  | | ||
| + | | Data Size | — | computed  | Width × Height × bpp ÷ 8 | | ||
| - | ==== Frame Header ==== | + | ==== Notes ==== | 
| + | * No frame table or chunk headers are present. | ||
| + | * Pixel data begins after a 16-byte header, aligned to the next 8-byte boundary. | ||
| - | ^ Offset ^ Size ^ Field ^ Description ^ | + | ---- | 
| - | | +0x00 | 2 bytes | `unk0` | Possibly X offset | | + | |
| - | | +0x02 | 2 bytes | `unk2` | Possibly Y offset | | + | |
| - | | +0x04 | 2 bytes | `w` | Width in pixels | | + | |
| - | | +0x06 | 2 bytes | `h` | Height in pixels | | + | |
| - | | +0x08 | 2 bytes | `chunkCnt` | Number of image chunks | | + | |
| - | | +0x0A | 2 bytes | `unkA` | Unknown | | + | |
| - | | +0x0C | 2 bytes | `unkC` | Unknown | | + | |
| - | | +0x0E | 2 bytes | `unkE` | Unknown | | + | |
| - | | +0x10 | 2 bytes | `unk10` | Unknown | | + | |
| - | | +0x12 | 2 bytes | `unk12` | Unknown | | + | |
| - | After the frame header, a sequence of **chunk definitions** begins. | + | ===== Multi-Frame Sprite Layout ===== | 
| - | ===== Chunk Data ===== | + | Used if `frameCount ≤ 256` (`0x0100`). | 
| - | Each chunk describes a rectangular image section used to draw part of the frame. | + | ==== Frame Table ==== | 
| - | ==== Chunk Structure ==== | + | * Offset: `0x10` | 
| + | * Entry size: 4 bytes per frame | ||
| + | * Each entry contains a **relative** 32-bit offset from the start of the frame data block. | ||
| - | ^ Offset ^ Size ^ Field ^ Description ^ | + | ==== Frame Data Block ==== | 
| - | | +0x00 | 2 bytes | `x` | X position | | + | |
| - | | +0x02 | 2 bytes | `y` | Y position | | + | |
| - | | +0x04 | 2 bytes | `w` | Width | | + | |
| - | | +0x06 | 2 bytes | `h` | Height | | + | |
| - | There are `chunkCnt` chunk structures per frame, immediately following the frame header. | + | Starts at: `0x10 + (frameCount × 4)` | 
| - | ===== Image Format Enum ===== | + | === Frame Header === | 
| - | The sprite rendering system likely interprets pixel data using the following enum: | + | ^ Field ^ Offset ^ Type ^ Description  ^ | 
| + | | X | 0x00 | `int16_t`  | Frame X offset  | | ||
| + | | Y | 0x02 | `int16_t`  | Frame Y offset  | | ||
| + | | Width | 0x04 | `uint16_t` | Frame width | | ||
| + | | Height  | 0x06 | `uint16_t` | Frame height  | | ||
| + | | Chunk Count | 0x08 | `uint16_t` | Number of chunks  | | ||
| - | ^ Value ^ Name ^ Description ^ | + | === Palette Data (for CI4 / CI8 only) === | 
| - | | 0 | `CI4` | Color Indexed 4-bit | | + | |
| - | | 1 | `CI8` | Color Indexed 8-bit | | + | |
| - | | 2 | `I4` | Intensity 4-bit | | + | |
| - | | 3 | `I8` | Intensity 8-bit | | + | |
| - | | 4 | `RGBA16`  | 16-bit RGBA | | + | |
| - | | 5 | `RGBA32`  | 32-bit RGBA | | + | |
| - | | 6 | `IA4` | Intensity + Alpha 4-bit | | + | |
| - | | 7 | `IA8` | Intensity + Alpha 8-bit | | + | |
| - | | 8 | `UNKNOWN` | Fallback / unknown | | + | |
| - | Format may be deduced from the header `type` field or the bitfield at `0x0C`. | + | ^ Field ^ Offset  ^ Size ^ Description  ^ | 
| + | | Palette Data | aligned after 0x14 | 32 or 512B | 16×2 or 256×2 bytes, depending on format | | ||
| - | ===== C Definitions ===== | + | === Chunk Table === | 
| - | The following C-like structures represent the sprite format as reverse-engineered: | + | Each chunk entry is 8 bytes: | 
| - | <code c> | + | ^ Field ^ Offset ^ Type ^ Description  ^ | 
| - | typedef struct { | + | | Chunk X | 0x00 | `int16_t`  | Chunk X position  | | 
| - | u16 frameCnt; | + | | Chunk Y | 0x02 | `int16_t`  | Chunk Y position  | | 
| - | u16 type; | + | | Chunk Width | 0x04 | `uint16_t` | Width of chunk | | 
| - | u16 unk4; | + | | Chunk Height | 0x06 | `uint16_t` | Height of chunk | | 
| - | u16 unk6; | + | |
| - | u16 unk8; | + | * Pixel data for each chunk follows the chunk table. | 
| - | u16 unkA; | + | * All data is aligned to the next 8-byte boundary. | 
| - | struct { | + | |
| - | u32 bit31 : 4; | + | ---- | 
| - | u32 bit27 : 3; | + | |
| - | u32 bit24 : 2; | + | ===== Design Rationale ===== | 
| - | u32 bit22 : 2; | + | |
| - | u32 pad_bit20 : 21; | + | === Format ID and Versatility === | 
| - | } unkC; | + | * 16-bit format ID allows for clean extensibility. | 
| - | u32 offsets[]; | + | * Decoding behavior is format-specific (e.g. CI formats require palettes). | 
| - | } BKSprite; | + | |
| + | === Single vs Multi-frame Decision === | ||
| + | * A `frameCount > 0x0100` indicates the file is a simple image without a frame table. | ||
| + | * Keeps parsing logic simpler for single-use graphics. | ||
| + | |||
| + | === Frame Table Abstraction === | ||
| + | * Offsets are relative to the frame data block, not absolute. | ||
| + | * Allows reorganization and stream-friendly parsing. | ||
| + | |||
| + | === 8-byte Alignment === | ||
| + | * Ensures data structures are aligned in memory. | ||
| + | * Improves DMA or cache line performance on N64. | ||
| + | |||
| + | === Chunk-Based Rendering === | ||
| + | * Chunks allow tile reuse or sub-rectangles. | ||
| + | * Efficient layout, supports tilemaps or partial redraws. | ||
| + | |||
| + | === Optional Palette === | ||
| + | * CI4 and CI8 include palettes automatically. | ||
| + | * Size and format are inferred based on format ID. | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ===== Validation Checklist ===== | ||
| + | |||
| + | Use the following to verify whether a file matches this format: | ||
| + | |||
| + | - [ ] File size ≥ 4 bytes | ||
| + | - [ ] Read and parse frame count and format ID | ||
| + | - [ ] If frameCount > 0x0100 → parse as single-chunk | ||
| + | - [ ] If ≤ 0x0100 → validate frame table offsets | ||
| + | - [ ] Parse each frame header and chunk table | ||
| + | - [ ] Validate alignment and total chunk data sizes | ||
| + | |||
| + | ---- | ||
| - | typedef struct { | + | ===== Notes ===== | 
| - | s16 unk0; | + | |
| - | s16 unk2; | + | |
| - | s16 w; | + | |
| - | s16 h; | + | |
| - | s16 chunkCnt; | + | |
| - | s16 unkA; | + | |
| - | s16 unkC; | + | |
| - | s16 unkE; | + | |
| - | s16 unk10; | + | |
| - | s16 unk12; | + | |
| - | } BKSpriteFrame; | + | |
| - | typedef struct { | + | * All integers are stored in **big-endian** format. | 
| - | s16 x; | + | * Offsets in the frame table are **relative** to the frame data section. | 
| - | s16 y; | + | * Data structures and pixel data must be aligned to 8-byte boundaries. | 
| - | s16 w; | + | * CI4/CI8 formats require an internal palette directly after the frame header. | 
| - | s16 h; | + | |
| - | } BKSpriteTextureBlock; | + | |
| - | </code> | + | |
| ---- | ---- | ||
| - | ''This format is still under investigation. Contributions and corrections welcome.'' | + | ''This documentation is a work in progress based on reverse engineering efforts. Contributions welcome!'' |