Ultima VI Internal Formats

This page deals with the details on the specifications of the file formats for the different files included in Ultima VI.

Files
These are the files in an Ultima VI installation, and what is known about them. Files that are marked "(LZW)" use the LZW compression scheme described below. Files that are marked "(stored)" don't. Files that aren't marked are unknown.


 * all.con
 * animdata
 * animmask.vga
 * basetile
 * blocks.shp
 * book.dat
 * bootup.m
 * brit.m
 * chunks - Map chunk templates (Template[1024]), where Template is (uint8[8 * 8]), where each value is a map tile index (see the Tile Graphics section) in [x + y * 8] order.
 * config.u6
 * converse.a
 * converse.b
 * convert.pal
 * create.m
 * dither
 * dungeon.m
 * end.m
 * end.shp
 * endpal.lbm
 * engage.m
 * forest.m
 * gargoyle.m
 * gypsy.shp
 * hornpipe.m
 * intro.m
 * intro.ptr
 * intro.shp
 * intro_#.shp (# is 1 to 3)
 * look.lzd
 * lzdngblk
 * lzmap
 * lzobjblk
 * mainmenu.shp
 * map
 * maptiles.vga (stored) - Map tile graphics data (see the Tile Graphics section).
 * masktype.vga (LZW) - Tile graphics information (see the Tile Graphics section).
 * midi.dat
 * montage.shp
 * newmagic.bmp
 * objtiles.vga (LZW)
 * palettes.int (stored) - Cutscene palettes (see the Palettes section).
 * paper.bmp
 * portrait.a
 * portrait.b
 * portrait.z
 * schedule
 * starpos.dat
 * stones.m
 * tileflag
 * tileindx.vga (stored) - Tile graphics file offsets (see the Tile Graphics section).
 * titles.shp
 * u6.# (# is 1-8)
 * u6.ch
 * u6.set
 * u6mcga.ptr
 * u6pal (stored) - In-game palete (see the Palettes section).
 * ultima.m
 * vellum1.shp
 * woods.shp
 * worldmap.bmp

These files are for the CGA, EGA, and Tandy graphics versions, which won't be relevant to most:


 * u6curs.cga
 * u6curs.ega
 * u6curs.tga

These files are not relevant to most people:


 * copyu6.bat, insub.bat - Batch files for performing various tasks.
 * cgadrv.bin, eegadrv.bin, egadrv.bin, mcgadrv.bin, tgadrv.bin - Video graphics driver programs.
 * end.exe, game.exe, install.exe, makemode.exe, t2cga.exe, t2ega.exe, t2hrc.exe, 2tga.exe, tandypin.exe, u.exe, ultima6.com, ultima6.exe - Game executables.
 * u6adlib.drv, u6cga.drv, u6cms.drv, u6covox.drv, u6ega.drv, u6innova.drv, u6mcga.drv, u6roland.drv, u6tandy.drv, u6tmus.drv - Sound and video graphics drivers.

LZW Compressed Data
Some files or blocks inside files are LZW compressed. LZW compressed data has the following format:

Libraries
Libraries are contain several items of the same type, inside the same file. Libraries have the following structure:

For simplicity, the following names are used for specific types of library files:
 * s_lib_16: a library file which does contain the File Size part, and where each offset in the Index part is 2 bytes (16 bits) long.
 * s_lib_32: a library file which does contain the File Size part, and where each offset in the Index part is 4 bytes (32 bits) long.
 * lib_16: a library file which does not contain the File Size part, and where each offset in the Index part is 2 bytes (16 bits) long.
 * lib_32: a library file which does not contain the File Size part, and where each offset in the Index part is 4 bytes (32 bits) long.

Conversations
Conversations are stored in the files CONVERSE.A and CONVERSE.B, which have the following characteristics:
 * They both are lib_32 library files.
 * Each Data Block consists of LZW compressed data, each corresponding to the conversation lines of one NPC.
 * Both files contain a few invalid offets in the Index parts.
 * Data Block 27 in CONVERSE.B is LZW compressed data, but with a Header equal to zero, meaning it contains uncompressed data.

Conversation Entry
After being decompressed, each Data Block corresponds to a Conversation entry. There is a total of 200 Conversation entries between both files.

Each of these entries consist of a list of several parts.
 * Each part is composed of a Binary Code, and possibly an inner Data Block.
 * Each Binary Code is 1 byte long.
 * The Data Block, if present, can be of any size, and its end is basically marked by the start of the next part's Binary Code.



Maps
The floor map data is stored in the "map" and "chunks" file. A chunk is a 16x16 group of tiles that is used as a template for common shapes, like houses, mountains, and trees. There are five maps for Britannia and the four underworlds, and they are composed of chunks, with Britannia further grouping chunks into blocks.

The "chunks" file is simply (Chunk[1024]), where Chunk is (uint8[8 * 8]). Each value is an index into the map tiles (see Tile Graphics) in [x + y * 8] order.

There are five maps in the "map" file, with some variation in storage. Britannia, the first map, is stored as (Block16[8 * 8]) (once again in [x + y * 8] order), where Block16 is (uint24[8 * 16]). Each uint24 is a pair of chunk indices, with the first in the lower 12 bits (mask 4097), and the second in the upper 12 bits (right shift 12). The next four maps are the underworlds, and they are each Block32, which is (uint24[16 * 32]) and uses the same paired indices.

Palettes
There are two groups of palettes. The in-game palette is stored in "u6pal". The cutscene palettes are in "palettes.int".

The "u6pal" format is (Palette palette, uint8[0x100] unknown), where Palette is ((uint8 red, green, blue)[256]). Each color element is from 0 (darkest) to 63 (brightest). The data after it are unknown.

The "palettes.int" format is (Palette[6] palettes), where Palette is as above. However, each color element is from 0 (darkest) to 255 (brightest).

Tile Graphics
Much of the game's world graphics use the tile graphics, which are 16x16 indexed images. Decoding them requires four files: "maptiles.vga" (LZW-compressed), "objtiles.vga" (not compressed), "tileindx.vga" (not compressed), and "masktype.vga" (LZW-compressed).

There are 2048/800h tiles in total: 512/200h map tiles stored in "maptiles.vga" and 1536/600h object tiles stored in "objtiles.vga".

"tileindx.vga" stores the offset of a tile's data as uint16[800h], where each value needs to be multiplied by 16 to get the byte offset. "maptiles.vga" and "objtiles.vga" are indexed as if they were a contiguous file. So if "maptiles.vga" is 117408 bytes uncompressed (which it is), when an offset of 130412 is actually an offset into "objtiles.vga" of 13004 bytes.

"masktype.vga" contains the storage format of the tiles in its first 2048/800h bytes as a uint8[800h] array. The values are: The compressed file type is stored as a sequence of horizontal spans of opaque pixels. It starts with a (uint8 tileLength), which is the size in 16-byte pages of the tile data, and is followed by a sequence of spans. Each span is (uint16 displacement, uint8 length, uint8[length] data). If length is zero, then the graphic is complete. displacement has a complex meaning specific to the U6 engine; the easiest way to deal with it is to convert it into a byte offset with (displacement % 160 + (displacement >= 1760 ? 160 : 0)), and add that to an output offset. Then copy the data to the output, and add length to the offset. Here is pseudo-code for performing these operations: uint8 tileLength = ReadUInt8; uint offset = 0; while(true) { uint16 displacement = ReadUInt16; uint8 length = ReadUInt8; if(length == 0) break; offset += displacement % 160; if(displacement >= 1760) offset += 160; Read(output, offset, length); offset += length; } These tiles use the "u6pal" palette.
 * 0 - Opaque - An opaque tile stored as (uint8[16 * 16]) in [x + y * 16] order.
 * 5 - Transparent - Stored as with Opaque, but pixels with a value of 255 are skipped as transparent.
 * 10/0Ah - Compressed - Described below.