TEX File Format
Hover! uses the Microsoft Foundation Classes (MFC) serialization framework to store texture data in TEX files. This documentation describes the binary file structure and explains how to read TEX files without relying on MFC.
All multi-byte integer values are stored in little-endian byte order, meaning the least significant byte comes first. For example, the 16-bit value 0x1234 is stored as 34 12.
The TEX format consists of a global 256-color palette, a collection of serialized CMerlinTexture objects, and an optional extension data block. Each texture contains one or more mipmap images, including pixel data and transparency information stored as spans.
General Structure
The structure shows the general layout using 4 entries the collection to demonstrate counting of the MFC objects. The structure is split into 2 sections for better readability.
| Color Table |
|---|
| Color Definition (index 0) |
| Color Definition (index 1) |
| Color Definition (index 2) |
| ... |
| Color Definition (index 255) |
| CMerlinTexture Collection |
|---|
| CObArray (object_count) |
| NEW_CLASS_TAG |
| MFC Class Definition "CMerlinTexture" (MFC object 1) |
| CMerlinTexture (index 0, MFC object 2) |
| CLASS_TAG |
| CMerlinTexture (index 1, MFC object 3) |
| CLASS_TAG |
| CMerlinTexture (index 2, MFC object 4) |
| CLASS_TAG |
| CMerlinTexture (index 3, MFC object 5) |
| Extension Data Block |
|---|
| Extension Data |
MFC Data Structure
MFC CObArray
| Name | Datatype | Description |
|---|---|---|
| object_count | uint16 | Number of elements in the serialized CObArray. |
MFC Tags
The CLASS_TAG is formed by performing a bitwise OR operation between 0x8000 and the reference ID of an existing class definition. For example, class ID 1 results in 0x8001.
| Name | Datatype | Description |
|---|---|---|
| NEW_CLASS_TAG | uint16 | 0xFFFF – Defines and registers a new class in the MFC reference table. |
| CLASS_TAG | uint16 | 0x8000 | class_id – References an existing class definition (e.g., 0x8001 for class definition at ID 1). |
MFC Class Definition
| Name | Datatype | Description |
|---|---|---|
| schema_version | uint16 | 0x0100 |
| name_length | uint16 | Length of the string in bytes. |
| name | unit8[name_length] | String data (ANSI). The class name of serialized object. |
MFC CString
This is a simplified representation of the MFC CString type. The length prefix uses a variable-size encoding depending on the string length. Since all texture names in Hover! are shorter than 255 characters, only the single-byte length prefix is required.
| string_length | uint8 | Length of the string in bytes (0–254). |
| string | uint8[string_length] | String data (ANSI, not null-terminated). |
CMerlin Data Structures
Color Definition
| Name | Datatype | Description |
|---|---|---|
| red | uint8 | Red color component (0–255). |
| green | uint8 | Green color component (0–255). |
| blue | uint8 | Blue color component (0–255). |
| reserved | uint8 | Always 0x00. |
CMerlinTexture
| Name | Datatype | Description |
|---|---|---|
| name | CString | Name of the texture. |
| extension_data | Extension Data | Optional length-prefixed data block, normally empty (0x0000). |
| transparent | uint16 | Enables transparent rendering when non-zero. |
| image_count | uint16 | Number of images, including the base texture and all mipmap levels. |
| images | Image[image_count] | Array of images including mipmaps, ordered from highest to lowest resolution. |
Image
| Name | Datatype | Description |
|---|---|---|
| image_width | uint16 | Width of this mipmap image in pixels (must be a power of two). |
| u_wrap | uint16 | Bitmask used to wrap horizontal texture coordinates. Typically width - 1. |
| image_height | uint16 | Height of this mipmap image in pixels (must be a power of two). |
| v_wrap | uint16 | Bitmask used to wrap vertical texture coordinates. Typically height - 1. |
| height_log2 | uint16 | Base-2 logarithm of the texture height. Used to calculate pixel offsets using bit shifts instead of multiplication. |
| pixel_count | uint32 | Number of stored palette indices, excluding transparent pixels. |
| pixel | uin8[pixel_count] | Palette indices stored in column-major order. |
| total_span_count | uint32 | Total number of spans across all columns. |
| span_columns | Span Column[image_width] | Span information for each image column. |
| image_extension_data | Extension Data | Optional length-prefixed data block, normally empty (0x0000). |
Span Column
| span_count | uint16 | Number of spans in this column. |
| spans | Span[span_count] | Array of spans defining the non-transparent pixel ranges. |
Span
| start | uint16 | Inclusive start coordinate, relative to the original texture height. |
| end | uint16 | Inclusive end coordinate, relative to the original texture height. |
Extension Data
A length-prefixed data block containing additional object-specific information. Its exact purpose is currently unknown. It is read by Hover! but the bytes are discarded.
| Name | Datatype | Description |
|---|---|---|
| length | uint16 | Number of additional bytes to read. |
| data | uint8[length] | Optional data, ignored by Hover! |
Example values of a texture
Image Dimensions
In the original TEX files, mipmap levels are generated down to a minimum image height of 4 pixels, regardless of the texture width.
| Image Size | u_wrap, v_wrap | MipMap Level | height_log2 |
|---|---|---|---|
| 256 x 128 | 255, 127 | 0 | 7 |
| 128 x 64 | 127, 63 | 1 | 6 |
| 64 x 32 | 63, 31 | 2 | 5 |
| 32 x 16 | 31, 15 | 3 | 4 |
| 16 x 8 | 15, 7 | 4 | 3 |
| 8 x 4 | 7, 3 | 5 | 2 |
Span Calculation
Span coordinates are stored relative to the base texture dimensions. For each mipmap level, the actual pixel coordinates are scaled by 2mipLevel before being stored. This allows all mipmap levels to use the same coordinate space.
| MipMap Level | Effective Pixel Range | Stored Span Coordinates |
|---|---|---|
| 0 | 20 - 108 | 20 - 108 |
| 1 | 10 - 54 | 20 - 108 (pixel value multiplied by 2 or value << 1) |
| 2 | 5 - 27 | 20 - 108 (pixel value multiplied by 4 or value << 2) |
| 3 | 2 - 13 | 16 - 104 (pixel value multiplied by 8 or value << 3) |
| 4 | 1 - 6 | 16 - 96 (pixel value multiplied by 16 or value << 4) |
| 5 | 0 - 3 | 0 - 96 (pixel value multiplied by 32 or value << 5) |
Details on pixel and span data
The following image illustrates an 8x8 texture and how its color and transparency information is stored. Pixel data is stored in a single array in column-major order, with each pixel represented by an index into the TEX file's global color palette. Transparency is represented by spans, which define the ranges of non-transparent pixels within each column. Both pixel data and span information are organized and processed column by column.
