Ultima Underworld internal formats
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
- 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).
- save#/ – Save files.
- save#/bglobals.dat
- save#/lev.ark – The dynamic game world.
- 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 III ☥ Ultima IV ☥ Ultima V ☥ Ultima VI ☥ Ultima VII ☥ Ultima VIII ☥ Ultima IX ☥ Ultima Underworld |