Ultima Underworld internal formats

From Ultima Codex
Jump to navigation Jump to search

This page documents the Ultima Underworld engine's internal formats (used in Ultima Underworld and Ultima Underworld II). System Shock used an overhauled, vastly different engine that is not discussed here.

If not specifically noted, anything here refers to Ultima Underworld (UW1) only. Ultima Underworld II (UW2) changes some formats, which will be documented later.

General Types[edit]

These types are used in this page:

  • char – A 7-bit (1-byte) ASCII character code.
  • charz – As with char, but a null character (code 0) terminates a string.
  • uint8 – Unsigned 8-bit (1-byte) integer, between 0 and 255 (28-1).
  • uint16 – Unsigned 16-bit (2-byte) integer, between 0 and 65535 (216-1), in little-endian byte-order.
  • uint32 – Unsigned 32-bit (4-byte) integer, between 0 and 4294967295 (232-1), in little-endian byte-order.

Files[edit]

This is a hierarchical index of the files in Ultima Underworld and what is known about them.

  • uw.exe
  • crit/ – Creature animations.
    • cr##page.n##
  • cuts/ – Cutscenes.
    • cuts/cs###.n##
  • data/ – General static data.
    • data/3dwin.gr
    • data/allpals.dat – A collection of uint8 that is used by various graphics to map from a 4-bit value to the 8-bit palette. See the “Graphics Files” section for details.
    • data/animo.gr – Spell effect animation graphics (palette 0).
    • data/armor_f.gr – Female armor paper doll graphics (palette 0).
    • data/armor_m.gr – Male armor paper doll graphics (palette 0).
    • data/babglobs.dat
    • data/bodies.gr – Paper doll body graphics (palette 0); five male, then five female.
    • data/buttons.gr – Contains editor buttons (palette 0).
    • data/chains.gr – Chain animation graphics (palette 0).
    • data/charhead.gr – Character portrait graphics (palette 0), each 34x34.
    • data/chrbtns.gr – Character creation screen graphics (palette 3).
    • data/chrgen.dat
    • data/cmb.dat
    • data/cnv.ark
    • data/comobj.dat
    • data/compass.gr – Compass graphics (palette 0).
    • data/converse.gr – Conversation pane graphics (palette 0).
    • data/cursors.gr – Cursor graphics (palette 0). In order, they are the crosshair, forward, backward, left, right, turn left, turn right, bear left, bear right, circle, x, writing quill, well, quill cursor, ankh frame 1, ankh frame 2, ankh frame 3, and ankh frame 4.
    • data/doors.gr – Door textures (palette 0); each 32x64. This does not include the portcullis texture.
    • data/dragons.gr – Graphics for the dragons on the borders of the screen (palette 0).
    • data/eyes.gr – Graphics for the eyes on the borders of the screen (palette 0).
    • data/flasks.gr – Health/mana/poisoned flask graphics (palette 0).
    • data/font4x5p.sys – A 5x4 font (see the “Font file” section). Note the name lists the height first.
    • data/font5x6i.sys – A 6x5 italicised font (see the “Font” section).
    • data/font5x6p.sys – A 6x5 font (see the “Font files” section).
    • data/fontbig.sys – An 11x15 font (see the “Font files” section]]).
    • data/fontbutn.sys – A 9x6 font only containing capital A-Z (see the “Font files” section]]).
    • data/fontchar.sys – A 9x10 font (see the “Font files” section).
    • data/genhead.gr – Generic character portrait graphics (palette 0); many entries are empty.
    • data/grave.dat
    • data/heads.gr – Player portrait graphics (palette 0).
    • data/inv.gr – Apparently unused graphics (palette 0). Mostly a bunch of yellow rectangles and a cut-off window.
    • data/lev.ark – The initial game state archive.
    • data/lfti.gr – Game action button graphics (palette 0). These are options, options highlight, speak, speak highlight, move, move highlight, look, look highlight, combat, combat highlight, use, and use highlight.
    • data/light.dat
    • data/lights.dat
    • data/mono.dat
    • data/objects.dat
    • data/objects.gr – Object icon graphics (palette 0). This includes some editing icons, or possibly icons that would be displayed in-game with a special mode.
    • data/opbtn.gr – Title screen button graphics (palette 2).
    • data/optb.gr – More option screen button graphics (palette 0).
    • data/optbtns.gr – Option screen button graphics (palette 0); see the “Enumerations” section for the list of buttons.
    • data/pals.dat – The 8 palettes used throughout the normal game. 0 is the main game palette, 1 is the map screen palette, 2 is the title screen palette, 3 is the character creation screen palette, 4 is unknown, 5 is the splash screen palette, 6 is unknown, and 7 is the win screen palette.
    • data/panels.gr – Panel graphics file (palette 0, explicit sizes 83x114, 83x114, 83x114, and 6x60). Images are the paper doll background, item list background, character sheet background, and some kind of borders.
    • data/player.dat
    • data/power.gr – Strike power animation graphics (palette 0).
    • data/question.gr – Question cursor graphics (palette 0); contains one image, which is the question cursor.
    • data/scrledge.gr – Ledger scroll edge animation graphics (palette 0).
    • data/shades.dat
    • data/skills.dat
    • data/spells.gr – Spell icon graphics (palette 0); each 16x18.
    • data/strings.pak – Game strings file (see the Strings section).
    • data/terrain.dat
    • data/tmflat.gr – Flats graphics (palette 0). These are the buttons and levers shown on dungeon walls. Each is 16x16.
    • data/tmobj.gr – Miscellaneous graphics (palette 0). This contains a variety of graphics: door sides, dial flats, tombstone textures, signs, a door, and big numbers that appear to be developer-related (considering the yellow 'HI' written on one).
    • data/uw.cfg
    • data/views.gr – Only contains an icon labeled "MV".
  • data/weapons.cm (UW1) or weap.cm (UW2)
    • data/weapons.dat (UW1) or weap.dat (UW2) – Weapon animation offsets from the bottom-left corner of the screen. The format is (WeaponOffset[8]), where WeaponOffset is for each weapon and has the format (int8[28] xOffsets, int8[28] yOffsets).
    • data/weapons.gr (UW1) or weap.gr (UW2) – Weapon animation graphics (palette 0). There are 8 weapons, 28 images per weapon. "data/weapons.dat" contains the offsets.
    • data/win1.byt – Win screen with text as a raw 320x200 graphic (palette 7).
    • data/win2.byt – Win screen without text as a raw 320x200 graphic (palette 7).
    • data/xfer.dat
  • save#/ – Save files.
  • sound/ – Sound files and drivers.
    • sound/*.adv – Sound device drivers.
    • sound/sounds.dat
    • sound/uw.ad
    • sound/uw.mt
  • install.exe
  • uw.exe
  • uwsound.exe

These files are only in Ultima Underworld:

  • uw2.exe
  • crit/
    • crit/assoc.anm
  • sound/
    • data/blnkmap.byt – Map background as a raw 320x200 graphic (palette 1).
    • data/chargen.byt – Character creation screen background as a raw 320x200 graphic (palette 3).
    • data/conv.byt – Sample conversation as a raw 320x200 graphic (palette 0). This is obviously not used.
    • data/f16.tr – Floor texture graphics (palette 0); each is 16x16. The ceiling texture is index 15. This is the same as "data/f32.tr", just 1/4th the size.
    • data/f32.tr – Floor texture graphics (palette 0); each is 32x32. The ceiling texture is index 15. This is the same as "data/f16.tr", just 4 times the size.
    • data/main.byt – Game play background as a raw 320x200 graphic (palette 0).
    • data/opscr.byt – Title screen background as a raw 320x200 graphic (palette 2).
    • data/pres1.byt – First splash screen as a raw 320x200 graphic (palette 5); this is the Origin title.
    • data/pres2.byt – Second splash screen as a raw 320x200 graphic (palette 5); this is the Blue Sky Productions title.
    • data/w16.tr – Wall texture graphics (palette 0); each is 16x16. These are the same as "data/w64.tr", just 1/16th the size.
    • data/w64.tr – Wall texture graphics (palette 0); each is 64x64. These are the same as "data/w16.tr", just 16 times the size.
  • sound/
    • sound/aw##.xmi
    • sound/uw##.xmi
    • sound/##.voc

These files are only in Ultima Underworld II:

  • crit/
    • crit/as.an
    • crit/cr.an
    • crit/pg.mp
  • cuts/
    • cuts/lback00#.byt (# is 0 to 7)
  • data/
    • data/byt.ark
    • data/controls.dat
    • data/dl.dat
    • data/gempt.gr
    • data/ghed.gr
    • data/lighting.dat
    • data/scd.ark
    • data/t64.tr
  • save#/
    • save#/desc
    • save#/player.dat
    • save#/scd.arc
  • sound/
    • sound/bsp##.voc
    • sound/sample.opl
    • sound/sp##.voc
    • sound/uw.opl
    • sound/uwa##.xmi
    • sound/uwr##.xmi

File Formats[edit]

Font files (.sys extensions)[edit]

Fonts are stored in a binary format.

Offset Length Type Name Description
00h 2 uint16 WidthBytes The width in bytes of a character; always 1.
02h 2 uint16 CharacterBytes The size in bytes of a character.
04h 2 uint16 SpaceWidth The width in pixels of a space character.
06h 2 uint16 CharacterHeight The height in pixels of a character.
08h 2 uint16 RowBytes The number of bytes in a row.
0Ah 2 uint16 MaximumWidth Maximum width of a character in pixels.
0Ch
X
Character[X]
Characters
Character images, described below. There are as many characters as there is space in the file, although no font has more than 127.
This is the format of the Character type:
00h
CharacterSize
uint8[CharacterSize]
Graphic
Bitmap of the character graphic in descending bit order. So the first pixel is mask 128, the second is mask 64, and so on.
XX 1 uint8 Width Width in pixels of the character.

Level Archives (data/lev.ark and save#/lev.ark)[edit]

This contains the entire state of the levels of the game. Its header has the following format:

Offset Length Type Name Descripion
00h 2 uint16 ChunkCount Number of chunks in the file.
02h 4*ChunkCount uint32[ChunkCount] ChunkOffsets Offsets from the start of the file to the chunk, or zero if there are no more chunks.

The type of the chunk depends upon how many bytes it is, which you can find out by subtracting the next chunk offset from this one, or the file size from this offset if this is the last chunk. All chunks of one type are in order. For example, in UW1 there are nine 31752-byte Level chunks followed by nine 384-byte unknown chunks, followed by 9 122-byte TextureList chunks, followed by 0-9 4096-byte map data chunks.

Length Name Description
31752 Level A new level description.
384 ?? ??
122 TextureList The textures used by the last-defined level.
4096 Map Map data, present only in savegames.

Level Chunk (31752 bytes)[edit]

In the below, MobileCount is 256 and ObjectCount is 768.

Offset Length Type Name Descripion
0000h 4*64*64 Tile[64*64] Tiles Level tiles in [x + y * 64] order, described below.
4000h 27*MobileCount Mobile[MobileCount] Mobiles List of mobiles in the level.
5B00h 8*ObjectCount Object[ObjectCount] Objects Immobile objects in the level.
7300h 2*(MobileCount - 2) uint16[MobileCount - 2] FreeMobiles Free mobile list. There are two few because mobile 0 is null and doesn't exist, and 1 is the Avatar and cannot be freed.
74FCh 2*ObjectCount uint16[ObjectCount] FreeObjects Free object list.
7AFCh 260 ?? ?? ??
7C00h 2 uint16 ?? ??
7C02h 2 uint16 LastFreeMobile Index of the last free mobile index in the FreeMobiles list.
7C04h 2 uint16 LastFreeObject Index of the last free object index in the FreeObjects list.
7C08h 2 char[2] Marker The text 'uw'.

This is the bit format of a Tile, which is 4 bytes long and treated as a uint32:

Shift Bits Mask Name Description
0 4 15 Type Shape of the tile, see below.
4 4 15 FloorHeight Basic height of the floor. A value of 15 connects with the ceiling.
8 2 3 ?? Always zero in UW1. Likely to be an unused part of FloorHeight.
10 4 15 FloorTexture Index into the level's floor texture list for the floor texture. Note that the level itself only stores 10.
14 2 3 ?? Sometimes non-zero, seemingly always for tiles containing doors, but not exhaustively checked.
16 6 63 WallTexture Index into the level's wall texture list for the wall texture.
22 10 1023 FirstObject Index of the first object on the tile or 0 if there are none.

In the game, each height unit is 11 world units and tiles are 48 units across. However, the game used a 320x200 resolution with non-square pixels, so the aspect ratio needs to be corrected (multiplied by 4/3 in the perspective matrix) or the image will look squashed. These are the tile types:

Value Name Description
0 Solid The tile is completely solid. Floor height is ignored.
1 Open A square tile of empty space.
2 DiagonalSW Diagonal, open to the south-west.
3 DiagonalSE Diagonal, open to the south-east.
4 DiagonalNW Diagonal, open to the north-west.
5 DiagonalNE Diagonal, open to the north-east.
6 SlopeN Slope to the north.
7 SlopeS Slope to the south.
8 SlopeE Slope to the east.
9 SlopeW Slope to the west.

Slope tiles raise the edge they're sloping towards by one unit. For example, if the tile height is 4 and the tile Type is SlopeN, then the northwest and northeast corners are height 5 and the southwest and southeast corners are height 4.

This is the format of an Object, which is 8 bytes long:

Offset First bit Bits Mask Name Description
00h-01h 0 9 511 TypeIndex The zero-based index of the type of this object.
  9 3 7 Option Meaning depends upon type.
  12 1 1 IsEnchanted ??
  13 1 1 IsTurned ??
  14 1 1 IsInvisible ??
  15 1 1 IsUnlinked ??
02h-03h 0 7 127 PositionZ Vertical position of the object.
  7 3 7 Angle Angle in 45-degree increments.
  10 3 7 PositionY Vertical position within the tile.
  13 3 7 PositionX Horizontal position within the tile.
04h-05h 0 6 63 Quality Meaning depends upon type.
  6 10 1023 ChainIndex Chain object index or 0 for none.
06h-07h 0 6 63 OwnerIndex Object index of the owner of this item or 0 for none.
  6 10 1023 Special Meaning depends upon type.

For an object index, values from 0-255 are a mobile, and values from 256-1023 are an object.

The Mobile type is complex enough that it warrants a different table. It starts with an Object header, then is followed by a packed 19 bytes:

Offset First bit Bits Mask Name Description
00h 0 7 127 HitPoints Current hit points.
  7 1 1 ?? ??
01h 0 8 255 ?? ??
02h 0 8 255 ?? ??
03h 0 3 7 Goal ??
03h-04h 3 8 255 GoalTarget ??
  11 5 31 ?? ??
05h 0 4 15 GoalLevel ??
  4 4 15 ?? ??
06h 0 4 15 ?? ??
  4 1 1 TalkedTo Has the Avatar talked to this character?
  6 2 3 Attitude ??
07h-08h 0 6 63 ?? ??
  6 7 127 Height ??
  13 3 7 ?? ??
09h-0Dh X X X ?? ??
0Eh-0Fh 0 4 15 ?? ??
  4 6 63 HomeY Home location on the map.
  10 6 63 HomeX Home location on the map.
10h 0 5 31 Heading ??
  5 3 7 ?? ??
11h 0 7 127 Hunger ??
  7 1 1 ?? ??
12h 0 8 255 ConversationIndex Conversation index when talking to this mobile, or zero for none.

Texture list chunk (122 bytes)[edit]

This maps floor, wall, and door textures from their values in the tiles and entities to graphics from the "data/f16.tr" or "data/f32.tr" files for floors, and the "data/w16.tr" or "data/w64.tr" files for walls. This chunk has the following format:

Offset Length Type Name Description
00h 2*48 uint16[48] Walls Indices into the "data/w16.tr" or "data/w64.tr" files for wall graphics.
60h 2*10 uint16[10] Floors Indices into the "data/f16.tr" or "data/f32.tr" files for floor graphics. The final value is the graphic index to use for the ceiling.
74h 6 uint8[6] Doors ??

Graphics Files (.gr and .tr extensions)[edit]

These contain sets of images. They start with a header:

Offset Length Type Name Description
00h 01h uint8 IsForcedSize Either 1 or 2. If 2, then this field is followed by (uint8 ForcedSize), which is the width and height of all images in the file (except for any with an explicit size, which depends upon the file); the images themselves do not have Width and Height fields in this case.
01h 02h uint16 Count Number of images.
03h 4*Count uint32[Count + 1] Offsets Image offsets relative to start of file. The last offset is for the end of the file.

If the image has an explicit size, either due to IsForcedSize or the file itself, then at the image offset is (uint8[Width * Height] Data), where Data are in [x + y * Width] order. Otherwise at each image offset is this structure:

Offset Length Type
Name Description
00h 01h uint8
Format
Storage format.
01h 01h uint8 Width Width of the image in pixels.
02h 01h uint8 Height Height of the image in pixels.

The Format field dictates the structure of the following data.

Format 4 – Raw
[edit]

This has the simple format (uint16 DataSize, uint8[DataSize] Data), where DataSize must match (Width * Height) and Data are in [x + y * Width] order.

Note that the UW engines draw the floor tiles with positive Y facing North. If you are extracting the floor graphics for an engine that draws them with the Y facing South, you will need to flip the Y values, ie use [x + (Height - y - 1) * Width] order. This is noticable on tiles that connect with other tiles, eg the partial water or partial lava tiles.

Format 6 – 5-bit RLE[edit]

This uses the RLE format discussed below with a 5-bit code size. This is always encountered with an explicit auxiliary palette.

Format 8 – 4-bit RLE[edit]

This uses the RLE format with a 4-bit code size. If there is no explicit auxiliary palette, then a (uint8 AuxiliaryPalette) index, multiplied by 16, indexes the "static/allpals.dat" auxiliary palette.

These formats both start with (uint16 DataSize) (after the auxiliary palette index, if present). Pseudocode would explain reading bit values most clearly:

int CodeBits = 4 or 5, depending upon the format
int Buffer = 0, BufferBits = 0;

int ReadCode() {
    BufferBits -= CodeBits;
    if(BufferBits < 0) {
        Buffer = (Buffer << 8) | ReadByte();
        BufferBits += 8;
    }
    return (Buffer >> BufferBits) & ((1 << CodeBits) - 1);
}

// Note that this does not use CodeBits for the shift as it should.
// This is an error in the RLE format itself.
int ReadCode2() {
    int code1 = ReadCode(), code2 = ReadCode();
    return (code1 << 4) | code2;
}

int ReadCode3() {
    int code1 = ReadCode(), code2 = ReadCode(), code3 = ReadCode();
    return (code1 << 8) | (code2 << 4) | code3;
}

int ReadCount() {
    int value = ReadCode();
    if(value != 0)
        return value;
    value = ReadCode2();
    if(value != 0)
        return value;
    return ReadCode3();
}

int ReadAux() {
    return AuxPalette[ReadCode()];
}

Pseudocode also explains the code itself:

void OutputLine(int value, int count) {
    while(count-- > 0)
        Output(value);
}

bool state = false;
int count, repeats, value;

while(NotEnded) {
    count = ReadCount();

    state = !state;
    if(state) {
        if(count == 2) {
            int repeats = ReadCount();
            while(repeats-- > 0) {
                count = ReadCount();
                value = ReadAux();
                OutputLine(value, count);
            }
        } else if(count > 0) {
            value = ReadAux();
            OutputLine(value, count);
        }
    } else {
        while(count-- > 0)
            Output(ReadAux());
    }
}

Format 10 – 4-bit[edit]

This uses the system auxiliary palette from "static/allpals.dat". It starts with a header of (uint8 AuxiliaryPalette, uint16 DataSize), where AuxiliaryPalette is multiplied by 16 to compute a byte index. This is then followed by (Width * Height * 4 + 7) / 8 bytes. The top nibble (mask F0h) is the first pixel's index into the auxiliary palette, and the bottom nibble (mask 0Fh) is the second pixel's index.

Models (uw.exe, uw2.exe)[edit]

Models (chairs, tables, stones, shrines) are stored in the executables as instruction streams. The first task is to locate the table. For UW1 (including the demo), search for the byte sequence (B6h, 4Ah, 06h, 40h); the TableOffset (discussed later) is from the end of the magic sequence plus 138. For UW2 search for (D4h, 64h, AAh, 59h); the TableOffset is the offset from the end of the magic sequence plus 150.

There are a number of data types unique to models:

  • fixed – 8.8 fixed-point stored as an int16. To convert to float, divide by 256f.
  • bigfixed – 24.8 fixed-point stored as an int32. To convert to float, divide by 256f.
  • normal – A value between -1 and 1 stored as int16. To convert to float, divide by 32767f. -32768 doesn't seem to occur.
  • unormal – A value between 0 and 1 stored as uint16. To convert to float, divide by 65535f.
  • vertex – A uint16 multiplied by 8. Divide it by 8 to get the vertex index. This is probably a data index.

Then rewind four bytes and read (uint16[64] ModelOffsets) – that is, the magic numbers were the first two model offset values. Each offset is based off the TableOffset, which is where each Model is located.

This is the format of a model at this offset:

Offset Length Type Name Description
00h 4 bigfixed Radius Radius of the model. If this is zero, this model slot is empty, and nothing beyond this field exists.
04h 6 fixed[3] Extents Bounding box of the model.
0Ah X Intruction[X] Instructions Instructions of the model.

In UW1, model indices 14 and 15 (zero-based index) point into the middle of other models and are therefore invalid.

Each instruction begins with (uint16 opcode), and has varying types of arguments. This has one special type, vertex, which is encoded as uint16 and multiplied by 8 (there is no remainder). The opcode are:

Value Name Notes
0000h End () – Terminate the mesh processing.
0006h Sort (normal NormalX, fixed DistanceX, normal NormalY, fixed DistanceY, normal NormalZ, fixed DistanceZ, uint16 LeftNodeOffset, uint16 RightNodeOffset)
000Ch SortZY (normal NormalZ, fixed DistanceZ, normal NormalY, fixed DistanceY, uint16 LeftNodeOffset, uint16 RightNodeOffset)
000Eh SortXY (normal NormalX, fixed DistanceX, normal NormalY, fixed DistanceY, uint16 LeftNodeOffset, uint16 RightNodeOffset)
0010h SortXZ (normal NormalX, fixed DistanceX, normal NormalZ, fixed DistanceZ, uint16 LeftNodeOffset, uint16 RightNodeOffset)
0012h ?? (uint16 Unknown)
0014h VertexColor (vertex Index, uint8 ColorA, uint8 ColorB, vertex NewIndex)
0016h ?? (uint16[3] Unknown)
002Eh ?? ??
0040h FaceIntroUnknown ()
0044h FaceIntroUnknown2 ()
0058h FacePlane (uint16 SkipWhenHidden, normal NormalX, fixed DistanceX, normal NormalY, fixed DistanceY, normal NormalZ, fixed DistanceZ)
005Eh FacePlaneZY (uint16 SkipWhenHidden, normal NormalZ, fixed DistanceZ, normal NormalY, fixed DistanceY)
0060h FacePlaneXY (uint16 SkipWhenHidden, normal NormalX, fixed DistanceX, normal NormalY, fixed DistanceY)
0062h FacePlaneXZ (uint16 SkipWhenHidden, normal NormalX, fixed DistanceX, normal NormalZ, fixed DistanceZ)
0064h FacePlaneX (uint16 SkipWhenHidden, normal NormalX, fixed Distance)
0066h FacePlaneZ (uint16 SkipWhenHidden, normal NormalZ, fixed Distance)
0068h FacePlaneY (uint16 SkipWhenHidden, normal NormalY, fixed Distance)
0078h Center (vertex OriginIndex, fixed[3] Origin, uint16 Unknown)
007Ah Vertex (fixed[3] Point, vertex ListIndex) – Set a vertex list index.
007Eh FaceVertices (uint16 Count, vertex[Count] Indices)
0082h Vertices (uint16 Count, vertex FirstIndex, (fixed[3])[Count] Vertices)
0086h VertexOffsetX (vertex Index, fixed AddToX, vertex NewIndex)
0088h VertexOffsetZ (vertex Index, fixed AddToZ, vertex NewIndex)
008Ah VertexOffsetY (vertex Index, fixed AddToY, vertex NewIndex)
008Ch VertexVariableZ (vertex Index, uint16 Unknown, vertex NewIndex) – Used for pillars and doorframes where the model reaches to the ceiling.
0090h VertexOffsetXZ (fixed AddX, fixed AddZ, vertex Index, vertex NewIndex)
0092h VertexOffsetXY (fixed AddX, fixed AddY, vertex Index, vertex NewIndex)
0094h VertexOffsetYZ (fixed AddY, fixed AddZ, vertex Index, vertex NewIndex)
00A0h DrawFaceShort (vertex Unknown, uint8 IndexA, uint8 IndexB, uint8 IndexC, uint8 IndexD)
00A8h DrawFaceTextured (uint16 TextureNumber?, uint16 VertexCount, (vertex VertexIndex, texel[2] TextureCoordinate)[VertexCount] Vertices)
00B4h DrawFaceTexels (uint16 VertexCount, (vertex Vertex, texel[2] TextureCoordinate)[VertexCount] Vertices)
00BCh FaceShade (uint16 ColorIndex, uint16 ShadeDark)
00BEh FaceShade2 (uint16 Variable1, uint16 Variable2)
00CEh DrawFaceTexels2 (uint16 VertexCount, (vertex Vertex, texel[2] TextureCoordinate)[VertexCount] Vertices)
00D2h ShortFace2 ??
00D4h DrawFaceDark (uint16 Count, uint16 BaseColor, (vertex Index, uint8 Dark)[Count] Vertices, ?uint8 PadIfOdd)
00D6h Gouraud ()

Palette Files (data/pals.dat)[edit]

This contains 8 palettes, each 256 colours in RGB order, with components from 0 to 63.

Palettes 0 and 2 have rotating colours, where a span of colours is rotated once per period. For example, if the rotating colors are represented as [ABCD], then they will become [BCDA], then [CDAB], then [DABC], then the pattern will repeat.

Palette 0 has two rotating spans, from 16 to 23 (inclusive) and 48 to 51 (inclusive). Palette 2 has one rotating span, from 64 to 127 (inclusive), and it rotates in blocks of 16 colours.

Palette 0 rotates 5 colours per second. Palette 2 rotates 10 colours per second, which is 0.625 blocks per second (since it rotates 16 colours at a time).

Player saves (save#/player.dat)[edit]

Strings (data/strings.pak)[edit]

Game strings are stored in this Huffman-coded file. It has the following format:

Offset Length Type Name Description
00h 2 uint16 NodeCount Number of nodes in the tree.
02h NodeCount*4 Node[NodeCount] Nodes List of nodes, described below.
XX 2 uint16 BlockCount Number of blocks.
XX BlockCount*6 BlockHeader[BlockCount] BlockHeaders Block headers, described below.
Node format:
00h 1 char Value The character at this node.
01h 1 uint8 Parent Parent node index, zero-based.
02h 1 uint8 Left Left child node index, zero-based, 255 for a leaf node.
03h 1 uint8 Right Right child node index, zero-based, 255 for a leaf node.
BlockHeader format:
00h 2 uint16 Id String set identifier.
02h 4 uint32 Offset Offset to the block from the start of the file.
At each BlockHeader.Offset is the Block itself:
00h 2 uint16 Count Number of strings in the block.
02h 2*Count uint16[Count] Offsets Offset to the string relative to the end of Offsets.

At the string offset is the string itself. It's much easier simply to show pseudocode than to describe the format:

int bits = 0, value = 0;
string result = "";

while(true) {
    Node node = Nodes[NodeCount - 1];
    while(node.Left != 255) {
        if(bits == 0) {
            value = ReadByte();
            bits = 8;
        }

        if((value & 128) == 0)
            node = node.Left;
        else
            node = node.Right;

        value *= 2;
        bits -= 1;
    }

    if(node.Value == '|')
        break;
    Output(node.Value);
}

Enumerations[edit]

This appendix contains lists of data that are important but would break the flow of the main body.

"data/optbtns.gr" images[edit]

These are the images for the options screen.

Value Name Description
0 Normal Background panel for the normal icons (options, talk, move, look, attack, use).
1 Options Background panel for the game options (save, restore, music, sound, detail, return to game, quit game).
2 Save Background panel for save/restore game (save/restore, 1, 2, 3, 4, cancel).
3 Quit Background panel for quit game (yes, no).
4 Audio Background panel for music/sound options (current state, music/sound, on, off, done).
5 Detail Background panel for detail option (current state, low, medium, high, very high, done).
6 OptionsSaveLow Lowlight for Save button on Options panel.
7 OptionsSave Highlight for Save button on Options panel.
8 OptionsRestoreLow Lowlight for Restore button on Options panel.
9 OptionsRestore Highlight for Restore button on Options panel.
10 OptionsMusicLow Lowlight for Music button on Options panel.
11 OptionsMusic Highlight for Music button on Options panel.
12 OptionsSoundLow Lowlight for Sound button on Options panel.
13 OptionsSound Highlight for Sound button on Options panel.
14 OptionsDetailLow Lowlight for Detail button on Options panel.
15 OptionsDetail Highlight for Detail button on Options panel.
16 OptionsReturnLow Lowlight for Return to Game button on Options panel.
17 OptionsReturn Highlight for Return to Game button on Options panel.
18 OptionsQuitLow Lowlight for Quit button on Options panel.
19 OptionsQuit Highlight for Quit button on Options panel.
20 AudioOnLow Lowlight for On button on Music/Sound panel.
21 AudioOn Highlight for On button on Music/Sound panel.
22 AudioOffLow Lowlight for Off button on Music/Sound panel.
23 AudioOff Highlight for Off button on Music/Sound panel.
24 SaveCancelLow Lowlight for Cancel button on Save/Restore panel.
25 SaveCancel Highlight for Cancel button on Save/Restore panel.
26 AudioDoneLow Lowlight for Done button on Music/Sound panel.
27 AudioDone Highlight for Done button on Music/Sound panel.
28 DetailDoneLow Lowlight for Done button on Detail panel – note this isn't the same graphic as MusicDoneLow.
29 DetailDone Highlight for Done button on Detail panel – note this isn't the same graphic as MusicDone.
30 Save1Low Lowlight for I button on Save/Restore panel.
31 Save1 Highlight for I button on Save/Restore panel.
32 Save2Low Lowlight for II button on Save/Restore panel.
33 Save2 Highlight for II button on Save/Restore panel.
34 Save3Low Lowlight for III button on Save/Restore panel.
35 Save3 Highlight for III button on Save/Restore panel.
36 Save4Low Lowlight for IV button on Save/Restore panel.
37 Save4 Highlight for IV button on Save/Restore panel.
38 DetailLowLow Lowlight for Low button on Detail panel.
39 DetailLow Highlight for Low button on Detail panel.
40 DetailMediumLow Lowlight for Medium button on Detail panel.
41 DetailMedium Highlight for Medium button on Detail panel.
42 DetailHighLow Lowlight for High button on Detail panel.
43 DetailHigh Highlight for High button on Detail panel.
44 DetailVeryHighLow Lowlight for Very High button on Detail panel.
45 DetailVeryHigh Highlight for Very High button on Detail panel.
46 SaveRestore "Restore Game:" overlay turning the Save panel into a Restore panel.
47 AudioMusicOn "Music is on." overlay for the Music/Sound panel.
48 AudioMusicOff "Music if off." overlay for the Music/Sound panel.
49 AudioSoundOn "Sound is on." overlay for the Music/Sound panel.
50 AudioSoundOff "Sound is off." overlay for the Music/Sound panel.
51 AudioMusic "Turn music:" overlay for the Music/Sound panel.
52 AudioSound "Turn sound:" overlay for the Music/Sound panel.
53 DetailIsLow "Detail level is: Low" overlay for the Detail panel.
54 DetailIsMedium "Detail level is: Medium" overlay for the Detail panel.
55 DetailIsHigh "Detail level is: High" overlay for the Detail panel.
56 DetailIsVeryHigh "Detail level is: VeryHigh" overlay for the Detail panel.
57 QuitYesLow Lowlight for the Yes button on the Quit panel.
58 QuitYes Highlight for the Yes button on the Quit panel.
59 QuitNoLow Lowlight for the No button on the Quit panel.
60 QuitNo Highlight for the No button on the Quit panel.

Object Types[edit]

Object types are a messy combination of builtin understanding of what numbers correspond to what type of object, builtin data regarding texture indices and other features (called "inherent parameters" here), and data stored in the game files themselves ("stored parameters").

Many objects of different types have shared behaviours, which is given as the "behavior" field.

Ultima Underworld[edit]

Behaviours[edit]
First Count Behavior Inherent parameters
0 16 Melee None
16 16 Ranged None
32 32 Clothing None
64 64 Critter None
128 16 Container None
144 8 Light None
152 168  ??  ??
320 1 ClosedDoor OpenDoor is type 328; Graphic 0; SideGraphic 1
321 1 ClosedDoor OpenDoor is type 329; Graphic 1; SideGraphic 0
322 1 ClosedDoor OpenDoor is type 330; Graphic 2; SideGraphic 1
323 1 ClosedDoor OpenDoor is type 331; Graphic 3; SideGraphic 0
324 1 ClosedDoor OpenDoor is type 332; Graphic 4; SideGraphic 1
325 1 ClosedDoor OpenDoor is type 333; Graphic 5; SideGraphic 0
326 1 ClosedDoor OpenDoor is type 334; Portcullis
327 1 ClosedDoor OpenDoor is type 335; Secret door
328 8 OpenDoor OpenDoor forms of the ClosedDoor types from 320-327.
336 1 Model 'a bench'; ModelIndex 3
337 3  ?? 'an arrow', 'a crossbow bolt', 'a large boulder'
340 1 Model 'a large boulder'; ModelIndex 7
341 2  ?? 'a boulder', 'a small boulder'
343 1 Model 'a shrine'; ModelIndex 11
344 8  ?? 'a table', 'a beam', 'a moongate', 'a barrel', 'a chair', 'a chest', 'a nightstand', 'a lotus turbo esprit'
352 1 Pillar No special information.
353 1 Decal.Lever 'a lever'; GraphicFile "data/tmobj.gr"; GraphicIndices (4, 5, 6, 7, 8, 9, 10, 11)
354 2  ?? 'a switch', null
356 1 Bridge None
357 1 Gravestone None
358 1 Decal.Sign 'some writing'; GraphicsFile "data/tmobj.gr"; GraphicIndices (20)
359 7  ?? null to 364, 365 = 'force field'
366 1 TextureMapC 'special tmap obj'
367 6  ?? 'special tmap obj', 'a button', 'a button', 'a button', 'a button', 'a button'
373 1 Decal 'a lever'; GraphicsFile "data/tmflats.gr"; GraphicIndices (5, 13)
374 1  ?? 'a pull chain'
375 1 Decal 'a pull chain'; GraphicsFile "data/tmflats.gr"; GraphicIndices (7, 15)
376 1  ?? 'a button'
377 1 Decal 'a button'; GraphicsFile "data/tmflats.gr"; GraphicIndices (1, 9)
378 2  ?? 'a button', 'a switch'
380 1 Decal 'a switch'; GraphicsFile "data/tmflats.gr"; GraphicIndices (12, 4)
381 4  ?? 'a lever', 'a pull chain', 'a pull chain', 'a damage trap'
385 1 Teleport 'a teleport trap'
386 1  ?? 'a arrow trap'
387 1 Do 'a do trap'
388 4  ?? 'a pit trap', 'a change terrain trap', 'a spelltrap', 'a create object trap'
392 1 Unlock 'a door trap'
393 7  ?? 'a ward trap', 'a tell trap', 'a delete object trap', 'an inventory trap', 'a set variable trap', 'a check variable trap', 'a combination trap'
400 1 Text 'a text string trap'
401 15  ?? null strings all
416 1 Move 'a move trigger'
417 1  ?? 'a pick up trigger'
418 1 Use 'a use trigger'
419 1 Look 'a look trigger'
420 92  ?? 'a step on trigger', 'an open trigger', 'an unlock trigger', null strings...
448 64 ?? Various names, many null.
Stored Parameters[edit]

Here are the stored parameters based upon the behavior for an object type, stored in the "data/objects.dat" file.

Offset Size Type Name Description
Clothing (4 bytes):
00h 1 uint8 Protection ??
01h 1 uint8 Durability ??
02h 1 uint8 ?? ??
03h 1 uint8 Category Shield = 0, Body = 1, Legs = 3, Hands = 4, Feet = 5, Head = 8, Ring = 9
Container (3 bytes):
00h 1 uint8 Capacity ??
01h 1 uint8 Contains Runes = 0, Ammo = 1, Maps = 2, Liquids = 3, Anything = 255
02h 1 ?? ?? ??
Critter (48 bytes):
00h 1 uint8 Level Level of the creature.
01h 3 ?? ?? ??
04h 2 uint16 HitPoints Average hit points.
06h 1 uint8 AttackPower Damage on attack.
07h 1 ?? ?? ??
08h 1 uint8 FluidAndRemains A combination of remains after death and the type of blood splatters this produces. Mask 0x0F is the splatter type, 0 for dust, 8 for red blood. Mask 0xF0 is the remains; Nothing = 0x00, RotwormCorpse = 0x20, Rubble = 0x40, WoodChips = 0x60, Bones = 0x80, GreenBloodPool = 0xA0, RedBloodPool = 0xC0, RedBloodPoolGiantSpider = 0xE0.
09h 1 uint8 GeneralType An index into the strings on page 8, offset 370. This string is the generic name for the creature, like "a creature" for "a goblin" or "a rat" for "a giant rat".
0Ah 1 uint8 Passiveness Relative passiveness. 255 will never take a swing at you, even if you kill them.
0Bh 1 ?? ?? ??
0Ch 1 uint8 MovementSpeed Speed of movement; 0 is immobile, maxes out at 12 for vampire bat.
0Dh 2 ?? ?? ??
0Fh 1 uint8 PoisonDamage Amount of poison damage this is capable of on attack.
10h 1 uint8 Category Ethereal = 0x00 (Ethereal creatures like ghosts, wisps, and shadow beasts), Humanoid = 0x01 (Humanlike non-thinking forms like lizard men, trolls, ghouls(?), and mages(?)), Flying = 0x02 (Flying critters like bats and imps), Swimming = 0x03 (Swimming critters like lurkers), Creeping = 0x04 (Creeping critters like rats and spiders), Crawling = 0x05 (Crawling critters like acid slugs, white worms, reapers (!), and fire elementals (!!)), EarthGolem = 0x11 (Only used for the earth golem), human = 0x51 (Humanlike thinking forms like goblins, skeletons, mountain-folk, fighters(?), exiles, and stone and metal golems).
11h 1 uint8 EquipmentDamage Amount of equipment damage this is capable of on attack.
12h 1 ?? ?? ??
13h 9 Probability[3] Probabilities Each has the form (uint16 value, uint8 percent). What this means is unknown.
1Ch 12 ?? ?? ??
28h 2 uint16 Experience Experience provided when killed.
2Ah 5 ?? ?? ??
2Fh 1 uint8 ?? Always 73.
Light (1 byte):
00h 1 ?? ?? ??
Melee (8 bytes):
00h 1 uint8 SlashModifier Modifier for the slash attack.
01h 1 uint8 BashModifier Modifier for the bash attack.
02h 1 uint8 StabModifier Modifier for the stab attack.
03h 3 ?? ?? ??
06h 1 uint8 Skill Skill this weapon uses; sword = 3, axe = 4, mace = 5, unarmed = 6.
07h 1 uint8 Durability Maximum durability.
Ranged (3 bytes):
00h 2 ?? ?? ??
02h 1 uint8 Durability Maximum durability.

References[edit]


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