RES Format (Stellar 7)
| Format type | Archive |
|---|---|
| Max files | Unlimited |
| File Allocation Table (FAT) | Embedded |
| Filenames? | Yes, 4 chars |
| Metadata? | None |
| Supports compression? | Yes |
| Supports encryption? | No |
| Supports subdirectories? | Yes |
| Hidden data? | No |
| Games | Stellar 7 |
The RES format is used by Stellar 7 to store much of the game data. The PC version has 29 files using this format.
Contents |
File format
It is a binary format containing any number of entries which themselves can contain other entries. Various items are stored in these files, such as compressed images and palette data, or the mission briefing text. The entries begin with a content type which can be clearly read even with a text editor.
Signature
There is no known signature, other than carefully reading the file structure and verifying it seems correct (e.g. offsets don't go past the end of the file.)
File entry
The file consists of one or more file entries, one after the other.
| Data type | Description |
|---|---|
| char contentType[4] | Content type (see below), not null-terminated |
| UINT32LE isfolder_length | Length of the data, most significant bit is folder bit |
| BYTE data[length] | length bytes of data |
If the most significant bit in isfolder_length is set, the data is comprised of more file entries in the same format as above, like a subdirectory. None of the game's data files appear to nest folders more than one level deep. The values can be extracted like this:
length = isfolder_length & 0x7FFFFFFF if (isfolder_length & 0x80000000) then it is a folder
Content types
The following values for contentType are known. Any types that refer to sub-blocks mean "files" in the "subdirectory" as described above.
BMP: Image
The game supports VGA and earlier modes including EGA and CGA. The main palette information for VGA is located in the file STELART.RES while the EGA/CGA palettes are in STELARTE.RES.
| contentType | Description |
|---|---|
| INF: | Slice guides for the bitmap. See below. |
Some BMP: entries contain an INF: tag which appears to be layout guides for the image.
| Data type | Description |
|---|---|
| UINT16LE pair_count | Number of size pairs to read |
| UINT16LE[2 * pair_count] | Sizes; stored with the width values first followed by the height values. |
The sizes correspond to the image data. In LEVEL1.RES there are five size pairs:
(256,16) (256,16) (256,16) (256,16) (32,27)
These are combined with two images of 8,624 bytes each for a total of 17,248 bytes. If you multiply out these size pairs and add the results they should come up to the same number of bytes, as VGA palettes have a 1:1 pixel-to-byte ratio.
FNT: Font
There are two FNT: blocks in STELLAR.RES. They are both uncompressed in one of two formats; hints about these formats was found at the ScummVM-DGDS Project. Over there they are known as simple and complex.
The structure of these fonts are:
Simple Font
Simple fonts are fixed width.
| Data type | Description |
|---|---|
| BYTE width | Width of the font in bits and pixels |
| BYTE height | Height of the font in bits and pixels |
| BYTE start | First character code in the font file |
| BYTE count | Number of characters in the font file |
| BYTE[height*count] | Font data |
Therefore, the file size for a simple font would be:
4 + (height * count)
Typically, the first character code is 0x20 (32). A font with a full uppercase set has a count of at least 0x40 (64) while a font with an upper/lowercase set has a count of at least 0x60 (96).
To find a particular character, look at the following position in the font data:
(character_code - start) * height
The simple font included in STELLAR.RES has a size of 8x6 pixels and starts at character 0x20. For character code 0x41 (65, letter A) the position would be (0x41 - 0x20) * 0x6 = 0xC6 (196). The data in this location is:
0x 7c 82 fe 82 82 00
This font is just a basic raster bitmap using 1 bit-per-pixel:
.11111.. 1.....1. 1111111. 1.....1. 1.....1. ........
Complex Font
Complex fonts have varying widths.
| Data type | Description |
|---|---|
| BYTE magic | Magic value 0xff |
| BYTE width | Maximum width of the font in bits and pixels |
| BYTE height | Height of the font in bits and pixels |
| BYTE unknown | |
| BYTE start | First character code in the font file |
| BYTE count | Number of characters in the font file |
| UINT16LE size | Size of the font data |
| BYTE unknown | Compression algorithm? Seems to be 0x02 |
| UINT32LE uncompressed_size | Size of the uncompressed data |
| BYTE[font_size-13] data | LZW compressed font data |
The size and uncompressed_size appear to match. The uncompressed data contains what appears to be the following layout:
| Data type | Description |
|---|---|
| UINT16LE[count] offsets | Positions for the characters in the data stream |
| BYTE[count] widths | Bit (or pixel) width for each characters |
| BYTE[uncompressed_size - (3*count)] data | Character data |
The complex font in STELLAR.RES appears to have only two characters with widths greater than eight requiring two bytes to be read for each glyph row rather than one. These characters are 0x42 (*) and 0x64 (@).
PAL: Palette
The PAL: block contains palette data. It has sub-blocks for different palette types:
| contentType | Description |
|---|---|
| VGA: | VGA Palette (three byte, 0-63) |
| EGA: | |
| CGA: |
SCR: Fullscreen image
Fullscreen images are 320x200 pixels; 32,000 bytes for EGA and 64,000 bytes for VGA.
The VGA files are split into two 32,000 byte "planes" using two pixels per byte. The second plane contains the more significant bits. If the starting bytes of the first plane are 0xFD 0xFF and the second plane are 0x99 0x88 then the first four horizontal pixels are palette entries 0x9F 0x9D 0x8F 0x8F.
| contentType | Description |
|---|---|
| BIN: | 32000 byte (16-colour EGA / Least significant VGA bits) |
| VGA: | 32000 byte (Most significant VGA bits) |
VER: Version
Null terminated version string (e.g. "3.37")
SSM: Sounds and music
The SSM: block seems to contain media data in most cases. It has sub-blocks for different media types:
| contentType | Description |
|---|---|
| SNG: | Song? Seems to be 8-bit unsigned 11025Hz PCM data with an unknown header |
| SNG: | Song - also can contain raw MIDI data (theme song is in this format) |
The file STELLAR.RES contains three SSM: entries; two of them contain SNG: blocks while the third has entries for various sound cards, most likely a version of the theme song in a format suitable for that device. The content type tags for these are as follows:
| contentType | Description |
|---|---|
| STD: | ? |
| TAN: | Tandy |
| M32: | Roland? |
| ADL: | AdLib |
| SBL: | Sound Blaster |
| 001: | ? |
| 002: | ? |
Compression
Some file entries are compressed using the LZW algorithm. The entries which are compressed cannot be identified by the 4-byte content type alone, and it is suspected that certain types are always compressed. Those entries that are compressed are in the following layout:
| Data type | Description |
|---|---|
| BYTE unknown | Compression algorithm? Always seems to be 0x02 |
| UINT32LE decompressedSize | Size of data after decompression |
| BYTE data[] | LZW-compressed data |
The following content types never seem to be compressed:
| Data type | Description |
|---|---|
| VQT: | |
| FNT: | |
| ARR: | |
| PAG: | |
| VGA: | Only if contained within a PAL: folder entry. |
The LZW algorithm uses a dynamic bit length, from 9 bits to 12 bits. Data is split into bytes in little-endian order. The first codeword is 257, with code 256 reserved to indicate a dictionary reset.
When the dictionary is reset, some bytes in the input data are discarded. Why this happens is currently unknown. The number of bytes discarded seems to vary depending on the number of bytes read (perhaps it is aligning to a DWORD boundary?)
There is some sample decompression code for similar games which seems to (mostly) work.
Credits
This file format was reverse engineered by Malvineous, with some subformats identified by Zab. Hints about the compression algorithm were obtained from the ScummVM project. If you find this information helpful in a project you're working on, please give credit where credit is due. (A link back to this wiki would be nice too!)