Ultima IV Internal Formats

Introduction
This is a reference to the various files comprising the DOS version of the game Ultima IV.

EGA Files
These files contain the graphics used in the game. They can be uncompressed, Run-Length encoded, or LZW encoded. When uncompressed, each pixel value is represented by a 4-bit value indicating one of 16 colors:

SHAPES.EGA
This file contains the bitmaps for all 256 tiles and is 32,768 bytes long. Each tile is 16x16 pixels, and each byte represents two pixels. Therefore, each tile is 128 bytes. In other words, the first 8 bytes represent the 16 pixels on the first row of the first tile (read left to right), and the next 8 bytes represent the 16 pixels on the second row of the first tile, and so on.

The EGA Tile set is :



CHARSET.EGA
This contains the bitmaps for each character in the font. Each character is 8x8 pixels, and each byte represents two pixels. In other words, the first 4 bytes represent the 8 pixels on the first row of the first character.

The EGA Character Set is:



The order is the standard ASCII mapping; 'A' is character 65, 'z' is character 122. The lowest 32 characters have the following glyphs:

Run-Length Encoded EGA Files
The following .EGA files are RLE encoded: START.EGA, KEY7.EGA, RUNE_x.EGA (where x is between 0 and 5), STONCRCL.EGA, HONESTY.EGA, COMPASSN.EGA, VALOR.EGA, JUSTICE.EGA, SACRIFIC.EGA, HONOR.EGA, SPIRIT.EGA, HUMILITY.EGA, TRUTH.EGA, LOVE.EGA, and COURAGE.EGA. Each of these files represents a 16 color 320x200 bitmap. Each pixel is encoded in 4 bits, like SHAPES.EGA and CHARSET.EGA. However, runs of identical *bytes* (not pixels) are encoded with a special run indicator value (0x02) followed by a one byte count and a one byte value. This allows a single run of up to 255 bytes (510 pixels) to be represented as three bytes. For example, the byte values "28 28 28 28" could be represented as "02 04 28". If the value 0x02 needs to be represented, it *must* be encoded in a run ("02 01 02").

LZW Encoded EGA Files
The remaining .EGA files are LZW encoded: ABACUS.EGA, ANIMATE.EGA, GYPSY.EGA, INSIDE.EGA, OUTSIDE.EGA, PORTAL.EGA, TREE.EGA, WAGON.EGA, HONCOM.EGA, SACHONOR.EGA, SPIRHUM.EGA, VALJUS.EGA, and TITLE.EGA. Also, the file SHAPES.EGZ is a LZW compressed version of SHAPES.EGA. Like the RLE encoded files, each of these files represents a 16 color 320x200 bitmap. LZW encoded files only seem to be used in the introduction sequence (i.e. in TITLE.EXE); the images used by AVATAR.EXE are RLE encoded instead.

For more details on the LZW algorithm: Lempel-Ziv-Welch

WORLD.MAP
This is the map of Britannia. It is 256x256 tiles in total and broken up into 64 32x32 chunks; the total file is 65,536 bytes long. The first chunk is in the top left corner; the next is just to the right of it, and so on. The last chunk is in the bottom right corner. Each tile is stored as a byte that maps to a tile in SHAPES.EGA. The chunks are stored in the same way as the overall map: left to right and top to bottom.

The "chunked" layout is an artifact of the limited memory on the original machines that ran Ultima 4. The whole map would take 64k, too much for a C64 or an Apple II, so the game would keep a limited number of 1k chunks in memory at a time. As the player moved around, old chunks were thrown out as new ones were swapped in.

ULT files
These contain town information. Specifically, a 32x32 map, plus the starting position, movement behavior, and conversation index of each NPC. The conversation index gives the starting position of the NPC's dialog block in the corresponding .TLK file. I'm not sure why some of the data is duplicated.

TLK files
These files contain the dialogue for up to 16 NPCs in a given area. Each NPC has a 288 (0x120) byte block containing his or her dialogues, followed by the keywords that trigger his or her responses. Most of the fields are null-terminated, variable length strings, with the following field starting immediately after. The blocks are then padded with zero bytes to 288 bytes.

CON files
These files contain the 11x11 battleground maps shown when combat starts. It has the map itself plus starting positions for up to 16 monsters and 8 party members.

DNG files
These files define each of the dungeons. The standard file size is 4,608 bytes. ABYSS.DNG is 16,896 bytes.

3D Dungeon Map
The first 512 bytes are the 8x8 maps for each of the 8 levels. These are used for the dungeons in 3D mode. They are read, as usual, from left to right and then top to bottom.

Dungeon Floor Tiles
Each point on the grid can be one of several types.

ABYSS.DNG Map Format
The Stygian Abyss also follows the same format with one important caveat: since it has 64 rooms instead of 16, several levels share D# values. Levels 1&amp;2 each have eight rooms. Levels 3&amp;4 each have eight rooms. Level 5 has 5 rooms, and Level 6 has 11 rooms. Level 7 has 4 rooms, and Level 8 has 12 rooms. The D# values still define the order the rooms appear; they simply reset every two dungeon levels.

Dungeon Rooms
The rest of the file is a set of 16 (64 in the case of the ABYSS.DNG) 256 byte blocks that define the dungeon rooms.

Trigger format
TRIGGER EXAMPLE: 46 85 75 65 - Places a Fire Field at (7, 5) and (6, 5) when you step on (8, 5).

SAV Files
These files save the state of the program between sessions.

PARTY.SAV
This file stores the information that is stored when the game is saved. It includes party-wide information like gold and food, plus a 39 byte record for each character.

Party Location
Since you can only save on the surface or in a dungeon, the only valid party locations are 0 and 0x11-0x18.

MONSTERS.SAV
Ultima 4 maintains a structure (in memory) that I've decided to call the "monster table." When you Journey Onward, U4 loads MONSTERS.SAV into the monster table. When you Quit & Save, U4 writes the monster table to MONSTERS.SAV. The purpose of the monster table fields depends on the current party location (surface, dungeon, settlement).

Surface
On the surface, the monster table contains information about monsters and inanimate objects (chests, horses, ships, balloon). A table entry is free if previousTile[i]==0. When u4dos deletes an entry, it sets both currentTile[i] and previousTile[i] to 0. Slots 0-7 are for monsters, while slots 8-31 are for inanimate objects. Non-empty entries don't have to be contiguous. Whirlpools and twisters are special cases; they must be placed in slot 0-3, or they won't swallow ships / damage the party. When you leave Hythloth, u4dos creates the balloon at (233 / 242). If there is more than one inanimate object on a tile, objects closer to the beginning of the table are drawn on top of those closer to the end. Monsters and the party are always drawn on top of inanimate objects. The current tile and previous tile values are used during runtime. However, when the game is saved, the previous tile must be set to the monster's base tile. The current tile can be any of the monster's tiles, except for pirate ships, where the current tile determines which direction the pirate ship is facing. If the previous tile is not set to the monster's base tile, it won't behave properly in u4dos: monsters with ranged attacks won't use them in combat, twisters and whirlpools will attack the party, and pirates will appear as pirate ships in battle. For transports with more than one tile (horse, ship), the previous tile determines which direction the transport is facing, while the current tile can be any of the transport's tiles.

Dungeons
In a dungeon, the monster table contains information about the monsters in the dungeon. A monster stored in the monster table is *also* stored in DNGMAP.SAV. Inanimate objects are not stored in the monster table; they are stored *only* in DNGMAP.SAV.

Settlements
In a castle/town/village, the monster table is loaded from the settlement's .ULT file, starting at offset 0x400. It contains information about the settlement's NPC's.

OUTMONST.SAV
OUTMONST.SAV is a backup of the monster table, created when you enter a dungeon or settlement. When you leave a dungeon or settlement, the monster table is reloaded from OUTMONST.SAV.

Exception: When you enter Hythloth through the ladder in LB's castle, the game does not write the current monster table to OUTMONST.SAV, because it already did that when you entered LB's castle.

DNGMAP.SAV
When you Quit & Save in a dungeon, the game saves objects (orbs, chests) and monsters in DNGMAP.SAV. DNGMAP.SAV contains the 8x8 maps for all 8 levels. Every tile is encoded as a byte. The upper nibble of a tile determines the tile type (see below). The lower nibble encodes the monster standing on the tile, except for tiles 0x80 0x90 0xA0 0xD0 (see below).

Orbs
When a PC touches an orb, one or more attributes are raised by 5 points each. Which attributes are raised depends on the dungeon.

EXE Files
These files are the executable programs that power the game. They also contain some data that does not fit elsewhere.

TITLE.EXE
This executable is launched by ULTIMA.COM to display the introduction and create new characters. When "Journey Onward" is selected from the intro menu, it exits and ULTIMA.COM passes control off to AVATAR.EXE, the main game executable. AVATAR.EXE has lots of the data necessary for the intro screens embedded in it.

AVATAR.EXE
This is the main game executable, launched from ultima.com (or run on its own to skip the intro). Lots of the special case data is embedded in it.

Item Location Record
Each item location record has the following structure: