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/04/12 13:01] (current) snowboundmage2 [Sprite Asset] |
||
---|---|---|---|
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 | | + | | 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!'' |