FSH SHPI Header
The FSH header is 16 bytes in length. The first 4 bytes are always the characters 'SHPI'. This is the file identifier, indicating that it should be a valid FSH. The structure of the header is as follows:
| 4 bytes | SHPI identifier|
| int | file size |
| int | # of entries |
| 4 bytes | directory id |
Directory ID legal entries. -- G354 - Building Textures G264 - Network Textures, Sim Textures, Sim heads, Sim animations, Trees, props, Base textures, Misc colours G266 - 3d Animation textures (ie the green rotating diamond in loteditor.dat G290 - Dispatch marker textures G315 - Small Sim texture, Network Transport Model Textures (trains etc) GIMX - Ui Editor textures G344 - BAT gen texture maps --
Directly after the 16 byte FSH file header is the directory. The directory is made up of 8 byte entries, and there should be as many entries as indicated by the third value in the FSH header (# of entries). The structure of an FSH directory entry is as follows:
| 4 bytes | entry name |
| int | offset in FSH | | | at which the | | | bitmap or pal | | | begins. |
The entry name sometimes has significance. When searching for a global palette for 8-bit bitmaps, the directory entry name for the gobal palette will always '!pal'. Once the '!pal' directory entry has been found, the global palette can be extracted and used for any bitmaps that use 8-bit indexed color. If no global palette is found, FSH decoders should look for a local palette directly folowing the indexed bitmap. If no palette is found, then no palette will be created or associated with the bitmap. Most tools, like FSHTool, simply ignore missing palettes and save the bitmap with an empty palette with all indexes set to black.
Other than !pal entries the only ones that are really useful to us are 0000, rail, and the TB values
0000 is pretty much solidly used for buildings, props, network intersections,and terrain textures. There dont seem to be any exceptions to this so far.
rail is always used for a rail texture, whereas for street road intersections its always by instance.
TB2 and TB3 are for any sprite animation. This could be useful I think. The first entry in the file is the TB2. All other entries in the texture are TB3 if they're in the directory.
FSH Entry Header
Each directory entry has an offset, which points to the start of a bitmap or palette. Before the actual palette or bitmap data is an entry header. This header specifies critical information about the data that follows it. The structure of an FSH entry header is as follows:
offset len data
0x00 0x01 record ID 0x01 0x03 Size of the block (= width x length + 10h(hex)) - Not used for a long time, Size of the texture block + 10(16,size of this entry) 0x04 0x02 width of record in record units (pixels usually) 0x06 0x02 heigth of record in record units (pixels usually) 0x08 0x02 Center X (X axis coordinate for center of image or for image to spin around. 65535 Max.) 0x0A 0x02 Center Y (Y axis coordinate for center of image or for image to spin around. 65535 Max.) 0x0C 0x02 x position to display bitmap from left 0x0E 0x02 y position to display bitmap from top 0x10 size record data
Bitmap or Palette data
After the entry header is the bitmap or palette pixel or color information. Palettes are generally arrays of 1 byte each, 256 entries long. Bitmaps may store their pixel data in one of many ways. FSH images can store their pixel data raw, or they can make use of Microsoft DXTC compressed formats. The following formats and their corrosponding codes are as follows:
NOTE: The entry code in the bitmaps entry header is logically anded with 0x7F to arrive at the color code (entry.code&0x7F).
Color Code = 0x7B Pixel Format: 8-bit indexed Palette: directly follows bitmap or uses global palette Compression: none
Color Code = 0x7D Pixel Format: 32-bit A8R8G8B8 Palette: none Compression: none
Color Code = 0x7F Pixel Format: 24-bit A0R8G8B8 Palette: none Compression: none
Color Code = 0x7E Pixel Format: 16-bit A1R5G5B5 Palette: none Compression: none
Color Code = 0x78 Pixel Format: 16-bit A0R5G6B5 Palette: none Compression: none
Color Code = 0x6D Pixel Format: 16-bit A4R4G4B4 Palette: none Compression: none
Color Code = 0x61 Pixel Format: DXT3 4x4 packed, alpha premultiplied Palette: none Compression: 4x4 grid compressed, half-byte per pixel (DXT3 compression/decompression code is readily available on the net)
Color Code = 0x60 Pixel Format: DXT1 4x4 packed, no alpha Palette: none Compression: 4x4 grid compressed, half-byte per pixel (DXT1 compression/decompression code can be found on the net)
Here are some more relating to palates: 22 24-bit DOS 24 24-bit 29 16-bit NFS5 2A 32-bit 2D 16-bit For Text entries the ID will be: 6F (Standard Text file) ETXT is 69 and 70 69 eTXT of arbitrary length with full entry header 70 eTXT of 16 bytes or less including the header 7C defined Pixel region Hotspot data for image.
This entry can also contain Binary data, however the identifier byte for different binary types anything that isnt already defined with a type so make sure to try and get all codes down correctly. Examples of binary data consist of Palatte animations, binary links to outside files, or plain binary data.
Overview of DXT compression
Microsofts DirectX Texture Compression uses what they call a 4x4 encoding. Basically, an image must be a multiple of 4 in width and height, because 4x4 blocks of pixels are compressed at a time, similar to encoding used in AVI/MPEG files. Each 4x4 block contains 16 pixels, each pixel using either 24bits or 32bits before compression. All 16 pixels use 512 bits of storage before compression. After compression, that block of 16 pixels is reduced to 64 bits, for an 8:1 compression ratio. The nice thing about DXT compression is its hardware accelerated by nVidia and ATI GPU's all the way back to the GeForce2 and Raedon 8000 series, leaving the CPU free to simulate.
The compression itself works in the following way. First, all 16 pixels in the 4x4 block are checked, and unique colors stored in a vector (usually just an array of 16 unsigned ints). Once all the pixels in a block are checked, the two color extremes are found among all the unique colors. (This is something I had a hard time with in my own version, so I'm currently just using the method from FSHTool.) These two color extremes make up color1 and color2.
Once color1 and color2 are found, they are reduced from 32bit color to 16bit color (RGB 5:6:5), and stored in the first 32bits of the compressed 64bit chunk of data. The rest of the colors in the 4x4 block are interpolated between thse two colors. Each pixel is only represented by two bits of information, as follows:
bits color used for pixel
00 color1 01 color2 10 2/3 color1 + 1/3 color2 11 1/3 color1 + 2/3 color2
Here is the layout of a 64bit compressed block:
|----------------------------| | 16bit RGB (5:6:5) color1 | <- 2 bytes |----------------------------| | 16bit RGB (5:6:5) color2 | <- 2 bytes |----------------------------| | 00 | 01 | 01 | 11 | <- 1 byte, first 4 pixels |----------------------------| | 01 | 11 | 00 | 11 | <- 1 byte, second 4 pixels |----------------------------| | 00 | 01 | 01 | 01 | <- 1 byte, third 4 pixels |----------------------------| | 11 | 11 | 00 | 00 | <- 1 byte, fourth 4 pixels |----------------------------|
You can see the second part of the block is very basic. Its a simple bitmap of 2 bits each representing 1 of four possible color values. A 00 means use color1, 01 means use color2, 10 means use two-thirds of color1 mixed with one-third of color2, and 11 means use one-third of color1 mixed with two-thirds of color2. This is DXT1 compression, as no alpha information is saved. DXT3 compression uses the same technique, but also stores another 64bits of information for the alpha component of each pixel, in a similar block. So DXT3 compression achieves a 4:1 compression ratio with alpha information included, rather than the 8:1 compression ratio without.