RES Format (Stellar 7)
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.
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.)
The file consists of one or more file entries, one after the other.
|char||contentType||Content type (see below), not null-terminated|
|UINT32LE||isFolder_length||Length of the data, most significant bit is folder bit|
|BYTE[length]||data||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
The following values for contentType are known. Any types that refer to sub-blocks mean "files" in the "subdirectory" as described above.
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.
|INF:||Slice guides for the bitmap. See below.|
Some BMP: entries contain an INF: tag which indicates the amount of sub-images inside the file, and the dimensions for all of them.
|UINT16LE||imgCount||Number of frames|
|UINT16LE[imgCount]||widths||The width values of all frames.|
|UINT16LE[imgCount]||heighs||The height values of all frames.|
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.
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 two types are detailed on this wiki by the designations given to them in the Westwood Font Editor, which are Dynamix Font Format v3 and Dynamix Font Format v4.
The PAL: block contains palette data. It has sub-blocks for different palette types:
|VGA:||VGA Palette (three byte, 0-63)|
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. For example, if the starting bytes of the first and second plane are 0xFD 0xFF and 0x99 0x88 respectively, then the first four horizontal pixels will be palette entries 0x9F 0x9D 0x8F 0x8F.
|BIN:||32000 byte (16-colour EGA / Least significant VGA bits)|
|VGA:||32000 byte (Most significant VGA bits)|
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:
|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:
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:
|BYTE||compressionType||Compression algorithm. 0x00 for uncompressed, 0x01 for RLE, 0x02 for LZW, 0x03 for LH1.|
|UINT32LE||decompressedSize||Size of data after decompression|
Most games use only LZW as compression, though all Sierra/Dynamix games that support LZW should accept the two other compression types too.
The following content types never seem to have a compression header:
|VQT:||Might be its own type of compression. Format still not figured out so far. They seem to always be accompanies by an "OFF:" chunk.|
|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, if the total number of codewords read so far isn't divisible by 8, additional codewords are read (and ignored) until it is divisible.
There is some sample decompression code for similar games which seems to (mostly) work. As of now, a compression algorithm does not seem to be available, but the fact files can be re-saved with simpler RLE compression, or no compression at all, still means the compressed formats can be modded.
The LH1 algorithm is the same as the 'lh1' method in LHA files.
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. Further research into the fonts was done by Nyerguds for the Westwood Font Editor. 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!)