Ultima VI internal formats

From Ultima Codex
Jump to navigation Jump to search

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

Files[edit]

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)" do not. Files that aren't marked are unknown.

  • all.con
  • animdata
  • animmask.vga
  • basetile (stored) - Object type tiles as (uint16[1024]).
  • blocks.shp
  • book.dat
  • chunks (stored) - Map chunk templates (see the Map section).
  • config.u6
  • converse.a
  • converse.b
  • convert.pal
  • dither
  • end.shp
  • endpal.lbm
  • gypsy.shp
  • intro.ptr
  • intro.shp
  • intro_#.shp (# is 1 to 3)
  • look.lzd (LZW) - Tile type names (see the Tile Types section).
  • lzdngblk (LZW)
  • lzobjblk (LZW) - Initial object state (see the Objects section).
  • mainmenu.shp
  • map (stored) - Floor map chunk indices (see the Map section).
  • 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
  • tileflag (stored) - Contains 24 bits of flags for tile types (see the Tile Types section), with the object type weights mixed in (see the Object Types section).
  • 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 palette (see the Palettes section).
  • vellum1.shp
  • woods.shp
  • worldmap.bmp

These files are music files:

  • bootup.m
  • brit.m
  • create.m
  • dungeon.m
  • end.m
  • engage.m
  • forest.m
  • gargoyle.m
  • hornpipe.m
  • intro.m
  • stones.m
  • ultima.m

These files are for the CGA, EGA, and Tandy graphics versions, which will not 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.
  • lzmap (LZW) - Identical to "map", but compressed.
  • 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.

Formats[edit]

LZW Compressed Data[edit]

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

Part Description Size
Header Size of the Body part after being decompressed.
If the size is zero, it means the Body will already be uncompressed.
4 bytes, least significant byte first.
Body The compressed data.
If the data is actually compressed, the first byte is always 0x00, and the second one always has its first bit set to 1, as this is the reset code for LZW.
After being decompressed, the resulting block can have any format.
Variable, depending on the data itself.


Libraries[edit]

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

Part Description Size
File Size Optional, contains the length of the entire library file. 4 bytes (if present) or 0 bytes (if missing), least significant byte first.
Index A list of offsets. Each offset indicates where a Data Block starts.
If an offset has a value of zero, it is an invalid offset and must be ignored.
Variable, depending on the number of offsets.
Each offset may be 4 bytes long, or 2 bytes long, but for one file, all the offsets must be of the same size (either 4 or 2 bytes long).
Notice that as all offsets have the same size, and the first offset gives the start of the first Data Block, the first offset can be used to calculate the size of the Index part.
Data Block 1 The first block of data. The blocks can be in any format, but all blocks must be in the same format. Variable, depending on the data itself.
... ... ...
Data Block N The last block of data. Variable, depending on the data itself. Notice that if there are invalid offsets, there will be more offsets than data blocks, but there can never be more data blocks than offsets.

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.

Game Data[edit]

Conversations[edit]

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 offsets 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[edit]

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.
Part Name Part Type Binary Code Data Block Type Data Block Contents
Identification Data 0xff Text The NPC's name.
Description Data 0xf1 Text The NPC's description; that is, the text given when LOOKing at the NPC.
Keyword List Data 0xef Text A keyword or list of keywords that can be used by the player in the conversation, which lead to a specific answer.
- If there are several keywords that lead to the same answer, they are separated by commas.
- It is always followed by an Answer part (see below).
Answer Data 0xf6 Text The NPC's answer to the given keyword. It is always preceded by a Keyword List part.
- Inside an answer, a keyword will be marked by having a @ sign preceding it, such as "Many @quests".
- Inside an answer, the text "$p" will be replaced by the player's name.
Jump Command 0xb0 Binary, 4 bytes, least significant byte first. Indicates that the conversation must "jump" to the location specified by the offset, which is relative to the start of the conversation entry. An Answer part is always followed by a Jump command.

<to-do: complete table>

Maps[edit]

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 super-chunks.

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 (SuperChunk16[8 * 8]) (once again in [x + y * 8] order), where SuperChunk16 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 SuperChunk32, which is (uint24[16 * 32]) and uses the same paired indices.

Objects
[edit]

Dynamic objects are stored in the save files, and the overworld objects are initially loaded from the "lzobjblk" file (which is compressed). Each super-chunk is stored as (uint16 count, Object[count] objects), where Object is (uint8 status, uint24 position, uint16 typeAndFrame, uint8 quantity, uint8 quality). position has x in the first 10 bits (mask 1023), y in the second 10 bits (mask 1023, left shift 10), and z in the last 4 bits (mask 15, left shift 20). typeAndFrame has the object type index in the first 10 bits (mask 1023), and the frame index in the last 6 bits (mask 63, left shift 10). If the object type is stackable, then quality is the second high byte of the quantity field.

Object Types
[edit]

"tileflag" contains the object type weights, stored at offset 4096/1000h as (uint8[1024] weight).

Palettes[edit]

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[edit]

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:

  • 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.

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.

Tile Types
[edit]

In addition to the graphics, there are more data associated with tiles. "look.lzd" (compressed) contains the names of tiles. "tileflag" (stored) holds 24 flags per tile, as well as object type weights.

"look.lzd", when uncompressed, is a sequence of (uint16 endTypeIndex, charz name), terminated with an endTypeIndex of 2049 (with no name). A simple plural has a name like "book\s", where everything after '\' (up to the end of the name or a space) is used if there is more than one. A complex plural has a name like "loa/f\ves of bread", where everything after '/' (up to the end of the name, a space, or '\') is used if the object is singular, and otherwise uses the plural form.

"tileflag" contains 24 bits of flags for tile types. The first 2048 bytes are the first type flags as (uint8[2048] flags1). At offset 2048/800h are the second type flags as (uint8[2048] flags2). At offset 4096/1000h are the entity type weights, which are 1024/400h bytes long. At offset 5120/1400h are the last type flags as (uint8[2048] flags3). The following table documents these flags:

Flags
Bit(s)
Mask
Description
flags1
0
1
Water
flags1
1
2
Impassable
flags1
2
4
Wall; blocks light
flags1
3
8
Damaging (swamp, fire)
flags1
4
16/10h
Western wall, if wall is also set (bit 2)
flags1
5
32/20h
Southern wall, if wall is also set (bit 2)
flags1
6
64/40h
Eastern wall, if wall is also set (bit 2)
flags1
7
128/80h
Northern wall, if wall is also set (bit 2)
flags2
0-1
3
Lighting level. 0 is no light, 1 is a small light (like a magic helm), 2 is a medium light (like a candelabra), and 3 is a large light (like a torch).
flags2
2
4
Boundary
flags2
3
8
Window; blocks light except when nearby
flags2
4
16/10h
Top layer
flags2
5
32/20h
Missile boundary; stops missiles
flags2
6
64/40h
Double height
flags2
7
128/80h
Double width
flags3
0
1
Unknown; associated with fire and some (all?) creatures.
flags3
1
2
Table; objects can be put on top.
flags3
2
4
Passable; allows walking on even if the underlying tile is impassable, such as skiffs and docks.
flags3
3
8
Unknown.
flags3
4
16/10h
Unknown.
flags3
5
32/20h
Unknown.
flags3
6-7
192/C0h
The article to refer to the tile type as. If 0, then no article is used. If 1, then refer to it as "a <object>". If 2, refer to it as "an <object>". If 3, refer to it as "the <object>".


References[edit]


Technical Details
Game Ultima IIIUltima IVUltima VUltima VIUltima VIIUltima VIIIUltima IXUltima Underworld