Ultima V Internal Formats

This text is from Nodling's information. It is only a copy paste, as I go, I'll be reformating it and then adding information.

= LZW compressed =

Some files from the PC version of Ultima 5 have been compressed with the LZW algorithm: (and possibly others)
 * .4
 * .16

The compressed files are stored as (uint32 uncompressedLength, uint8[] compressedData).

Graphics files (*.4 and *.16 extensions)
These are all LZW-compressed image files. Those with a .4 extension have four-colour, two-bit data; those with a .16 extension have 16-colour, four-bit data.

Pixels are stored in descending bit order. So for a 16-color byte the first pixel is mask 0xF0, and the second is mask 0x0F. For a 4-color byte, the first pixel is mask 0xC0, the second pixel is mask 0x30, the third pixel is mask 0x0C, and the fourth pixel is mask 0x03.

Rows of pixels are padded to a four-pixel (byte) boundary for 4-color data, and an eight-pixel (four byte) boundary for 16-color data. So for a given width in pixels, a 4-color row is ((width + 3) / 4) bytes long, while a 16-color row is ((width + 7) / 8 * 4) bytes long.

"tiles.4" and "tiles.16"
These files, when uncompressed, contain 512 (0x200) tiles that are each 16x16 pixels stored in order without any headers. Using the above rules, the 4-color rows are stored as 4 bytes per row, and the 16-color rows are 8 bytes per row.

The 16-color tile sets are :

PC:



Amiga:



Here is the 4-color tileset using a gray-scale scheme with this file:



All other *.4 and *.16 files
These LZW-compressed files are composed of a number of images of varying sizes. The header is (uint16 count, uint32[count] offsets), where count is the number of images, and offsets is the start of an image from the beginning of the file, or zero if there is no image in that slot (this only occurs in "dng*.16" files).

Each image is composed of (uint16 width, uint16 height, ImageRow[height] data). Each row has the number of bytes discussed above.

"items.*" and "mon*.*" contain dungeon ladders, chests, and monster graphics, and have an additional twist:
 * The offsets are actually in uint16 and not uint32


 * Immediately following the image data is an image mask, composed of (uint16 width, uint16 height, uint8[(width + height + 7) / 8] data); width and height must be identical to those in the image. Pixels are stored in descending bit order with no padding, so the first pixel is mask 0x80, the second pixel is mask 0x40, and so on. If a value is set, then that pixel is masked and should not be drawn.

= *.CBT =

The combat files define a series of combat maps that are 11 x 11 in size.

File Format
struct CBT_File { Combat_Map c_maps[n]; };

struct Combat_Map { Map_Row             row0[1];  //Information contains the new tiles once a trigger happens // row 1 = east // row 2 = west // row 3 = south // row 4 = north Map_PlayerPos_Row   row1_4[4]; Map_MonsterTile_Row row5[1]; Map_MonsterX_Row    row6[1]; Map_MonsterY_Row    row7[1];

// row 8: positions of triggers, one position hits to new tiles // row 9: position X of the new tiles // row 10: position Y of the new tiles Map_Row             row8_10[3]; };

struct Map_Row { uint8 tiles[11]; uint8 newTiles[8]; uint8 padding[13]; };

struct Map_PlayerPos_Row { uint8 tiles[11]; uint8 initial_X[6]; // initial x position of each party member uint8 initial_Y[6]; // initial y position of each party member uint8 zeroes[9] = {0,0,0,0,0,0,0,0,0}; };

struct Map_MonsterTile_Row { uint8 tiles[11]; uint8 monster_tiles[16]; // tile for each monster uint8 zeroes[5] = {0,0,0,0,0}; };

struct Map_MonsterX_Row { uint8 tiles[11]; uint8 initial_X[16]; // initial x position of each monster uint8 zeroes[5] = {0,0,0,0,0}; };

struct Map_MonsterY_Row { uint8 tiles[11]; uint8 initial_Y[16]; // initial y position of each monster uint8 zeroes[5] = {0,0,0,0,0}; };

Notes:
 * Certain fields are explained in the next couple of sections
 * The initial party member positions depend on the direction from which the party entered the map.

Information field in the Map_Row
The information field has different meanings depending on the considered row. The information relates to map changes that happen when a character moves onto a tile.


 * The position of the tiles are provided in row 8 where the 16 bytes of information are divided into two 8-byte sections (X position and Y position)
 * The type of tiles are obtained in row 0
 * The position of the triggers is provided in rows 9 and 10. Row 9 provides the X position, row 10 provides the Y position.

It seems that one trigger automatically changes two tiles.

Example of a dungeon room
Here is an example of a dungeon room entirely decoded.

FF 4F 44 44 44 44 44 4F FF FF FF 4F 4F 4F 44 44 44 44 44 00 00 00 00 00 00 00 00 00 00 00 00 00
 * 1) Level 1
 * 1) New tiles for the changes
 * 1) Unknown
 * 1) Unkown

FF 4F 44 44 44 44 44 4F 4F 4F 4F 08 09 09 0A 0A 0A 04 03 05 04 02 06 00 00 00 00 00 00 00 00 00  FF 4F 44 44 44 44 44 44 44 44 44 00 00 00 00 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 00  FF 4F 4F 44 44 44 44 44 44 44 44 04 05 03 04 06 02 08 09 09 0A 0A 0A 00 00 00 00 00 00 00 00 00 FF FF 4F 4F 44 44 44 44 44 44 44 04 03 05 04 02 06 02 01 01 00 00 00  00 00 00 00 00 00 00 00 00  FF FF FF 4F 44 44 44 44 44 44 44 DC D0 D0 D0 94 94 94 94 DC 00 00 00 00 00 00 00 00 00 00 00 00
 * 1) Level 2
 * 1) Position X (east)
 * 1) Position Y (east)
 * 1) Unkown
 * 1) Level 3
 * 1) Position X (west)
 * 1) Position Y (west)
 * 1) Unkown
 * 1) Level 4
 * 1) Position X (south)
 * 1) Position Y (south)
 * 1) Unkown
 * 1) Level 5
 * 1) Position X (north)
 * 1) Position Y (north)
 * 1) Unkown
 * 1) Level 6
 * 1) Monster tiles (what monsters are in the room)
 * 1) Unkown

FF FF 4F 4F 44 44 44 44 44 44 44 09 02 01 01 01 01 01 01 09 00 00 00 00 00 00 00 00 00 00 00 00  FF 4F 4F 44 44 44 44 44 44 46 46 09 05 04 06 05 05 05 05 08 00 00 00 00 00 00 00 00 00 00 00 00
 * 1) Level 7
 * 1) Initial X position for monsters
 * 1) Unkown
 * 1) Level 8
 * 1) Initial Y position for monsters
 * 1) Unkown

FF 4F 44 44 44 44 44 44 46 44 46 06 06 06 06 06 06 06 06 06 06 06 06 06 06 06 06  00 00 00 00 00
 * 1) Level 9
 * 1) Position X of trigger for 2 elements of level 1, positions are in level 10 and level 11
 * 1) Position Y of trigger for 2 elements of level 1, positions are in level 10 and level 11
 * 1) Unknown

FF 4F 44 44 44 44 44 46 44 44 46 00 00 00 01 01 02 02 03 03 05 07 04 06 04 06 05  00 00 00 00 00
 * 1) Level 10
 * 1) Position X of changes in map from trigger in Level 9
 * 1) Position Y of changes in map from trigger in Level 9
 * 1) Unkown

FF 4F 44 44 44 44 44 46 46 46 46 00 00 00 01 02 03 03 03 04 06 06 05 05 04 06 05  00 00 00 00 00
 * 1) Level 11
 * 1) Position X of changes in map from trigger in Level 9
 * 1) Position Y of changes in map from trigger in Level 9
 * 1) Unkown

Tiles for each monster
In the case of dungeon maps, the tile for each monster gives the engine which monster to load where. With the initial_X and initial_Y fields of the Map_MonsterX_Row or Map_MonsterY_Row, the system can load up any dungeon map with a maximum of 16 monsters in a given room.

The values of the the monster_tile array in the Map_MonsterTile_Row element coincide with the elements of the tile set. However, since we are considering monsters the first 256 tiles are ignored.

Thus 1 is a chest, 2 is gold, 3 is a potion, etc.

Notes:
 * For some reason, certain values provide unexpected results
 * 5C would normally be the bard but is the jocker instead
 * EC, ED, EE, and EF seem to be a random monster value
 * Though the system seems to use the first frame of each element, any frame seems to work indifferently:
 * 0x78 is Blackthorne but so is: 0x79, 0x7A, and 0x7B

DUNGEON.CBT
= *.CH =

Font files with 128 characters each: - ibm.ch contains the Latin characters and a number of symbols. - runes.ch contains the Britannian runes and the remaining symbols.

File format:

Each character is 8x8 pixels, 1 bit per pixel (0 = black, 1 = white). I don't know which palette index is associated with a 0 or 1. Each byte represents 8 pixels. The most significant bit represents the leftmost pixel, and the least significant bit represents the rightmost pixel.

= *.DAT =

BRIT.DAT
The Britannian map. Its size is 256x256 tiles. It is divided into 256 chunks. Each chunk has a size of 16x16 tiles, and each tile is stored as a uint8 (in Ultima 4, the chunk size was 32x32 tiles).

To save space, chunks that are all water (tile 0x1) were left out. The location of the all-water chunks is stored in DATA.OVL. The chunks are stored from west to east, north to south, i.e. the first chunk in the uncompressed map is the one in the northwest corner.

The tiles in a chunk are also stored from west to east, north to south.

The complete map of Britannia is: 

The complete map of Britannia with the original tileset is: 

CASTLE.DAT
The castle file contains all the maps concerning the castles.


 * Lord British's Castle




 * Blackthorn's Castle




 * Britannies




 * Small towns



DUNGEON.DAT
The DUNGEON.DAT file contains the dungeon maps for the game. It is made of 8 * 8 * 8 * 8 tiles where :


 * The first 8 is a line of Deceit
 * The first 8 * 8 is the first level of Deceit
 * The first 8 * 8 * 8 is the dungeon deceit, each level being 8 * 8

The code for each tile is actually divided in two groups of 4-bits:

Higher 4 bits:


 * 0: Nothing
 * 1: Ladder Up
 * Lower bit: 8 provides an upper trap
 * 2: Ladder Down
 * Lower bit: 8 provides an upper trap
 * 3: Ladder Up and Down
 * Lower bit: 8 provides an upper trap
 * 4: Chest
 * Lower bits:
 * 0: normal chest
 * 1, 2: trapped
 * 4: poisonned
 * 5: Fountain
 * Lower bits:
 * 0: cure poison
 * 1: heal
 * 2: poison
 * anything else: bad taste, damage
 * 6: Trap
 * Lower bits:
 * 0: lower trap visible
 * 1: bomb trap
 * 2: invisible trap
 * 8: upper trap visible
 * 7: Open chest
 * 8: Energy fields
 * Lower 3 bits:
 * 0: Poison
 * 1: Sleep
 * 2: Fire
 * 3: Energy
 * A: Rooms but doesn't seem to work when loading them
 * B: Wall
 * Lower values:
 * 1: Text
 * C: Secondary wall
 * D: Secret door
 * E: Normal door
 * F: Room

Note :
 * 4th lower bit says if there is an upper trap or not

TOWNE.DAT
This data file contains the cities of Ultima 5:


 * Moonglow




 * Britain




 * Jhelom




 * Yew




 * Minoc




 * Trinsic




 * Skara Brae




 * New Magincia



DWELLING.DAT
The dwelling data contains the various dwellings of the game.


 * Fogsbane




 * Stormcrom




 * Greyhaven




 * Waveguide




 * Iolo's hut




 * Spektran




 * Sin Vraal's hut




 * Grendel's hut



KEEP.DAT
The keep data contains the various keeps of the game.


 * Ararat




 * Bordermarch




 * Farthing




 * Windemere




 * Stonegate




 * The Lycaeum




 * Empath Abbey




 * The Serpent's Hold



LOOK2.DAT
This file contains the "look" descriptions for the 0x200 tiles. Each description is a zero-terminated ASCII string. Some descriptions consist only of an asterisk, which the game displays as a diamond. The offsets are relative to the beginning of the file.

look2.dat = set(0x200) of offset16, set(0x200) of ascii_string

offset16 = uint16 ascii_string = set of ascii_char, terminator ascii_char = uint8 terminator = (uint8) 0

MISCMAPS.DAT
This file contains:


 * 1) 4x Cutscene Screens (11x11 tiles)
 * 2) 4x Intro Screens (19x4 tiles)
 * 3) Intro Script Data

Intro Maps
Notes:
 * The intro maps are stored in the order in which they appear during the introduction:
 * 1) "The Summoning"
 * 2) "The Journey"
 * 3) "The Arrival"
 * 4) "The Welcoming"


 * the script data controls the movement of NPC's in the introduction
 * TODO: battle screen descriptions, script format

SIGNS.DAT
This file contains the set of 33 (0x21) sign groups in this game. First the 16-bit offsets to each sign are stored; after that comes the data for each sign. Each offset represents 0 or more sign groups for a single location, with differing XY coordinates and floors.

signs.dat = set(0x21) of offset16, set(0x21) of sign_group

offset16 = uint16 sign_group = set of sign_data

sign_data = header4, set of sign_char, terminator

header4 = location, coord_Z, coord_X, coord_Y location, coord_Z, coord_X, coord_Y = uint8 sign_char = uint8 terminator = (uint8)

Notes:


 * offset16[i] points to the signs in location i. For locations without signs, the corresponding offset16 is 0. If you remove the zero values, you'll find that the remaining offset16's are sorted in ascending order.


 * each sign starts with a 4-byte header, specifying location (shown below), z, x and y coordinates.


 * the data between your current offset and proceeding offset will contain 1..N signs (sign group) separated by a single NULL (0) byte


 * in some of the eight virtue towns, there are signs that refer to the laws - if there are two then one is defined by only a "\n", the second definition contains the strings.


 * The game may not support signs in dungeons.

0 <= sign_char <= 0x7F --> runes.ch[sign_char] 0x80 <= sign_char <= 0xFF --> ibm.ch[sign_char - 0x80]
 * Signs can contain characters from both ibm.ch and runes.ch:


 * If you want to review the plaintext string of a sign, simply subtract 128 (0x80) from any character > 127 (0x7F)


 * The signs each have the lines denoted as lower case alphabet characters.

UNDER.DAT
The Underworld map

Its size is 256x256 tiles.

It is divided into 256 chunks. Each chunk has a size of 16x16 tiles, and each tile is stored as a uint8 (in Ultima 4, the chunk size was 32x32 tiles). Unlike BRIT.DAT, it is not compressed (no chunks were left out).

The chunks are stored from west to east, north to south, i.e. the first chunk in the map is the one in the northwest corner. The tiles in a chunk are also stored from west to east, north to south.

= *.HCS =

Font files with 128 characters each.

ibm.hcs contains the Latin characters and a number of symbols.

runes.hcs contains the Britannian runes and the remaining symbols.

File format:

Each character is 16x12 pixels, 1 bit per pixel (0 = black, 1 = white). I don't know which palette index is associated with a 0 or 1. Each byte represents 8 pixels. The most significant bit represents the leftmost pixel, and the least significant bit represents the rightmost pixel.

= *.NPC =

Each file contains 4608 bytes.

There are of course : - CASTLE.NPC - DWELLING.NPC - KEEP.NPC - TOWNE.NPC

CASTLE.NPC
Todo.

DWELLING.NPC
Todo.

KEEP.NPC
Todo.

TOWNE.NPC
It is divided into 8 parts.

These files contain information about NPC's.

The general structure of the whole file is : struct NPC_File { NPC_Info info[8]; // each NPC file has information for 8 maps }; For each city, we have an information entry for the Npcs of the map:

struct NPC_Info { NPC_Schedule schedule[32]; uint8 type[32]; // merchant, guard, etc.  uint8 dialog_number[32]; };

The dialog number gives the entry index to the *.TLK file.

Finally, the schedule says how the Npc moves around in the city and especially when: struct NPC_Schedule { uint8 AI_types[3]; uint8 x_coordinates[3]; uint8 y_coordinates[3]; sint8 z_coordinates[3]; uint8 times[4]; };

Notes:

1) All maps can hold a maximum of 31 (not 32) NPC's. In every map, schedule[0], type[0] and dialog_number[0] are not used. However, type[0] is sometimes 0 and sometimes 0x1C, so perhaps it has some unknown purpose.

2) Each NPC_Schedule contains information about 3 locations that the NPC will go to at different times of day. The x and y coordinates are between 0 and 31, because each map has a size of 32x32 tiles. The z coordinates represent the level, relative to level 0. 0xFF would make the NPC go to the level below level 0, while 0x1 would make the NPC go to the level above level 0.

The times are given in hours, so they range from 0 to 23.

times[0] --> NPC goes to location 0

times[1] --> NPC goes to location 1

times[2] --> NPC goes to location 2

times[3] --> NPC goes to location 1

Values for the dialog_number
= *.OVL =

Code or data overlays.

DATA.OVL
Here is the data layout for this file:

 Mapping of Data.ovl

The old list is here for the moment:

offset     length      purpose // monster flags 0x154C     0x30*2      flags that define the special abilities of                           monsters during combat; 32 bits per monster 0x0020 = undead (affected by An Xen Corp) todo: - passes through walls (ghost, shadowlord) - can become invisible (wisp, ghost, shadowlord) - can teleport (wisp, shadowlord) - can't move (reaper, mimic) - able to camouflage itself - may divide when hit (slime, gargoyle) // moon phases 0x1EEA     28*2        moon phases (28 byte pairs, one for each day of the month) // shrines and mantras 0x1F7E     8           x coordinates of shrines 0x1F86     8           y coordinates of shrines // this section contains information about hidden, non-regenerating objects (e.g. the magic axe in the dead tree in Jhelom); there are // only 0x71 such objects; the last entry in each table is 0 0x3E88     0x72        object type (tile - 0x100) 0x3EFA     0x72        object quality (e.g. potion type, number of gems) 0x3F6C     0x72        location number (see "Party Location") 0x3FDE     0x72        level 0x4050     0x72        x coordinate 0x40C2     0x72        y coordinate // dock coordinates (where puchased ships/skiffs are placed) // 0 = Jhelom // 1 = Minoc // 2 = East Brittany // 3 = Buccaneer's Den 0x4D86     0x4         x coordinate 0x4D8A     0x4         y coordinate // scan code translation table: // when the player presses a key that produces one of the scan codes in  // the first table, the game translates it to the corresponding code in   // the second table 0x541E     8           scancodes 0x5426     8           internal codes // wells 0x7252     0x32        wishing for one of these keywords at a wishing well gets you a horse

Ordered list of Armour, Weapons and Scrolls

The other OVL
The other OVL files, as far as we can tell are actually binary files containing code. The base executable actually loads these up on a need to execute basis.

= *.TLK =

These files contain conversation scripts. Here is the transcript for Ultima V. There are 4 files for each group of Maps :

The format for each of these files is:

Notes:

1) The first NPC number is 1.

2) The script_indexes are sorted by NPC number, in ascending order.

3) The script_data blocks are sorted by NPC number, in ascending order.

4) The conversations appear to be scripted, like the ones in Ultima 6.

The way this is linked together is via the Npc information since it gives us the dialogue index which links itself to the script index.

The text encoding
For each Npc, there are a certain number of '\0' terminated strings which are encodings of what is the name, job, etc. of the Npc. To decode these texts, I've taken Nodling's decoder and tweaked it a bit.

First part: fixed entries
For each Npc, it starts with a certain number of fixed entries:

- Name, Description, Greeting, Job, Bye

Second part: key words
We specify it by:

 (Therefore text only) [  [ ]...]  (Potentially anything

Third part : labels
Then it's question/answers with labels.

Todo

Generalities
For the moment, for each entry, I do :

if((c >= 160) && (c < 255)) {           c -= 128; }       else {           special = true; }

If it's a special case, then we have two cases : it's a code based symbol, it's an entry to an array of texts from DATA.OVL (the offset table starts at 0x24f8).

For the moment, I've confirmed these bindings :

If it's not a special case, then I just copy the character in the string.

= General Notes =

1) Wishing Wells

There are two wishing wells in the game: - Paws (location 0x16) - Empath Abbey (location 0x1F)

These locations are hard-coded into the game. There is no difference between horses from wishing wells and horses from vendors. It also doesn't make any difference if you wish for "horse" or a car brand.

= EGA.DRV =

See u5_ega_drv.txt

= INIT.GAM =

Initial "SAVED.GAM".

= SAVED.GAM =

Ultima 5 (PC version) SAVED.GAM

=
==================

Last updated on 6-March-2004

Please send additions, corrections and feedback to this e-Mail address:

Remove space + vowels from "marc winterrowd" and append "at yahoo dot com"

SAVED.GAM

-

This file contains the current saved game.

When you start a new game, "SAVED.GAM" is initalized from "INIT.GAM".

0x2B2      1           ? ?

0x2B3      1           ? ?

0x2B4      1           ? ?

0x2B5      1           1-6         # Party Members

0x2B6      0xF         n/a         Non-Regenerating Object Flags[1] 0x2C5      1           ? ?

0x2C6      1           ? ?

0x2C7      1           ? ?

0x2C8      1           ? ?

0x2C9      1           ? ?

0x2CA      1           ? ?

0x2CB      1           ? ?

0x2CC      1           ? ?

0x2CD      1           ? ?

0x2CE      2           1-? Current Year

0x2D0      2           0-? # of Active Monsters & Charmed PC's[2]

0x2D2      2           0-? # of Active PC's and Charmed monsters[2]

0x2D4      1           n/a         Icon  shown between stats window and the food/gold/date window(from one of the *.CH files)

0x2D5      1           0-5,0xFF=None    Active Character

0x2D6      1           n/a         Mode of Transportation (foot, ship, skiff, horse, carpet) [3]

0x2D7      1           1-13        Current Month

0x2D8      1           1-28        Current Day

0x2D9      1           0-23        Current Hour

0x2DA      1           0-23        copy of 0x2D9

0x2DB      1           0-59        Current minute

0x2DC      1           ? ?

0x2DD      1           ? ?

0x2DE      1           ? ?

0x2DF      1           0x30-0x37   Phase of Trammel [4]

0x2E0      1           0x30-0x37   Phase of Felucca [5] 0x2E1      1           0-0x10      Current Moongate Height (in pixels) [6] 0x2E2      1           0-255       Karma

0x2E3      1           ? ?

0x2E4      1           ? ?

0x2E5      1           0-0xFF      # of turns since game began of (stops at 0xFF)

0x2E6      1           0-0xE       # of Hours Until Party Heals Again via Camping [7]

0x2E7      1           ? ?

0x2E8      1           n/a         Protective Spell Duration [8]

0x2E9      1           ? ?

0x2EA      1           ? ?

0x2EB      1           0,1,0xFF    Animations During Next Frame [9]

0x2EC      1           0-4         Wind Direction (0=calm, 1=N, 2=S, 3=E, 4=W)

0x2ED      1           s.b.        Current Party Location

0x2EE      1           s.b.        Combat Backup of 0x2ED

0x2EF      1           0xFF,0-7    Z Coordinate of Party [10]

0x2F0      1           0-[7|0xFF]  X Coordinate of Party

0x2F1      1           0-[7|0xFF]  Y Coordinate of Party

0x2F2      1           0=hidden,1=visible         Crosshair Visibility (combat only, 0=hidden, 1=visible)

0x2F3      1           0-0xA       X Coord of Crosshair

0x2F4      1           0-0xA       Y Coord of Crosshair

0x2F5      1           0-0xF0      X Coord of Upper Left Chunk [11]

0x2F6      1           0-0xF0      Y Coord of Upper Left Chunk

0x2F7      1           0-? Attacker's Weapon [12]

0x2F8      1           ? ?

0x2F9      1          0=hidden,1=visible         Visibility State of Focus Rectangle & Crosshair

0x2FA      1           0-6         Exit Direction from Combat Map 0=None,1=W,2=E,3=N,4=S,5=U,6=D

0x2FB      1           s.d.        Combat Type Flags [13]

0x2FC      1           ? ?

0x2FD      1           0,1         "No More Enemies" Flag (whether or not Esc leaves combat)

0x2FE      1           0,1         Update/Animate 2D Map? [14]

0x2FF      1           0-? Current Light Intensity

0x300      1           0-0xFF      Remaining Light Spell Duration (in turns) [15]

0x301      1           0-0xF0      Remaining Torch Duration (in turns) [15]

0x302      0x20 (1 per creature)       n/a         Monster Interference Table [16]

0x322      1           0-8,0xFF    Location of the Shadowlord of Falsehood [17]

0x323      1           0-8,0xFF    Location of the Shadowlord of Hatred ([17]

0x324      1           0-8,0xFF    Location of the Shadowlord of Cowardice [17]

0x325      1           ? ?

0x326      1           0-0xFF      ordained shrine quests

bit 0 = honesty

bit 1 = compassion

bit 2 = valor

bit 3 = justice

bit 4 = sacrifice

bit 5 = honor

bit 6 = spirituality

bit 7 = humility

0x327      1           ? ?

0x328      1           0-0xFF      completed shrine quests, bits have the

same meaning as in 0x326

0x329      1           ? ?

0x32A      8           n/a         open/sealed flag for each dungeon

0 = sealed, 0x80 = open

0x332      8           n/a         destroyed/ok flag for each shrine

one byte per shrine, only bit 7 is used

bit 7 = 0 --> ok

bit 7 = 1 --> destroyed

0x33A      0xE         n/a         dungeon room cleared flags (7 dungeons,

2 bytes per dungeon)

when you kill all monsters in a dungeon

room, two things happen:

1) the bit corresponding to the dungeon

room is set to 1

2) in the dungeon map at 0x3B4, the

room's upper nibble is set to 0xA

Note: there are no flags for Doom,

because you can't leave Doom (except

by winning the game)

0x348      0x20        n/a         x coordinates of annotations

0x368      0x20        n/a         y coordinates of annotations

0x388      0x20        n/a         annotation tiles

0x3A8      1           0-0x20      number of entries in the annotation

table (see below)

0x3A9      1           s.d.        When you open a door, the game stores

the door tile in this byte. Only one

door can be open at any time.

0x3AA      1           0-0x1F      x coord of open door

0x3AB      1           0-0x1F      y coord of open door

0x3AC      1           0-FF        Number of turns the door stays open.

Decremented every turn, wraps around

to 0xFF.

0x3AD      1           0-0xFF      x coord of purchased ship (see 0x105F)

0x3AE      1           0-0xFF      y coord of purchased ship (see 0x105F)

0x3AF      1           0-4         When you hoist your ship's sails and

press an arrow key, the game stores the

direction in this byte:

0 = none

1 = west

2 = east

3 = north

4 = south

0x3B0      1           0,1         New prompt at end of current turn

0 = don't display newline and prompt

when the current turn ends

1 = display newline and prompt at

end of current turn

0x3B1      1           0-0x19      number of drunken moves until you can

move normally again; set to 0x19 when

you get drunk

0x3B2      1           ? ?

0x3B3      1           ? ?

0x3B4      0x200       n/a         map of the current dungeon

8 levels, 8x8 tiles per level

0x5B4      0x80        n/a         "NPC killed" flags, 4 bytes (32 bits,

32 NPC's) per settlement

flag = 0 --> NPC is alive

flag = 1 --> NPC has been killed

0x634      0x80        n/a         "NPC met" flags, 4 bytes (32 bits,

32 NPC's) per settlement

flag = 0 --> party hasn't met NPC yet

flag = 1 --> party has met the NPC

("met" means that the NPC has asked the

Avatar's name and received a truthful

answer)

0x6B4      0x100       n/a         monster table (has 0x20 slots)

monster format: see below

0x7B4      4           ? ?

0x7B8      0x200       n/a         NPC schedules

See *.NPC in u5tech.txt (used only in

settlements)

0x9B8      0x200       ? ?

0xBB8      0x400       n/a         Movement List Table (see below)

0xFB8      0x40        n/a         Movement List Pointers (see below)

0xFF8      0x20        n/a         NPC types

See *.NPC in u5tech.txt (used only in

settlements)

0x1018     0x44        ? ?

0x105C     1           ? copy of 0x105D?

0x105D     1           0-3         orientation in a dungeon

0 = north, 1 = east, 2 = south, 3 = west

0x105E     1           1-3         graphics for the current dungeon

1 = cave

2 = mine

3 = dungeon

0x105F     1           s.d.        when you buy a ship or skiff, the game

encodes your purchase in this byte:

0x40 = skiff

n = frigate with (n-0x80) skiffs on

board, n >= 0x80

when you leave the settlement where you

bought the vessel, the game creates it

at the coordinates stored in 0x3AD and

0x3AF

Notes: [1]one bit per object

bit = 0 --> object is still there

bit = 1 --> object has been taken

[2](combat only)

active = sleeping, good, poisoned

inactive = charmed, dead, fled

[3]                                add 0x100 to get the real tile [4] If the hour is 0-4, this byte determines where moongates take the party. [5] If the hour is 20-23, this byte determines where moongates take the party. [6] [7]This is set to 0xE when the party gets healed by camping; it is decremented every full hour.

if this byte is 0 at the end of your

camping rest, the party gets healed,

but only if you rested for 6-9 hours;

camping for 1-5 hours never heals the

party [8]0 = no protective spell is active

0xFF = spell remains active until

removed (Badge, Crown, Sceptre, Amulet)

else = turns until the spell expires

(spell type is stored in 0x2D4) [9] When time has been stopped, the game

sets this byte to 0 during every frame.

0 = skip animations during the next

frame

1 = animate tiles during the next frame

0xFF = ? [10] dungeons:

0 = level 1, 7 = level 8

settlements:

0xFF = basement, 0 = ground floor,

1 = first floor, etc.

outside:

0 = Britannia, 0xFF = Underworld [11]

[12] (combat only)

When a PC attacks a monster during

combat, the game stores the attack

method in this byte. The attack method

can be a weapon or a spell. [13] [14]0 = don't update/animate 2D map

1 = update and animate 2D map

Ignored in dungeons.

Set to 1 when you enter or leave a

settlement. I don't know if the game

ever sets it to 0. [15]

In Lor spell, Borrowed Torch = 0x64

Vas Lor spell = 0xFF

Vas Lor scroll, Ignited Torch = 0xF0 [16] 0xFF = no monster interferes with this

creature

else = index of the monster that

interferes with this creature [17] 0 = none

1 = Moonglow

2 = Britain

3 = Jhelom

4 = Yew

5 = Minoc

6 = Trinsic

7 = Skara Brae

8 = New Magincia

0xFF = destroyed [18] 1) If you set one of the bytes at 0x322-0x324 to 0xFF, that just means

the respective shadowlord won't visit the towns anymore. You'll still

encounter the shadowlord in Stonegate, unless you've also set its

"NPC killed" flag.

2) The Crown and the Sandalwood Box are treated as NPC's. When you take

them, the game sets their "NPC killed" flag.

Crown = NPC 1 in Blackthorn's castle

Sandalwood Box = NPC 31 in Lord British's castle

You can cast spells in Blackthorn's castle if you have the Crown in your

inventory. If you don't have the Crown, the game assumes that it's still

in Blackthorn's castle, and all spells you cast there will be absorbed.

3) Annotation table:

When you're in a settlement, this table contains the locations of

drawbridges.

At 8 PM, the game places water tiles (tile 3) at the locations stored in

the annotation table.

At 5 AM, the game places the tiles stored in the annotation table at the

corresponding locations.

Changing the table in SAVED.GAM has no effect, because the game restores

it from somewhere else when you Journey Onward.

4) Chunk cache:

Britannia and the Underworld are divided into chunks of 16x16 tiles.

Four of these chunks are cached in at any time; you see them when you view

a gem.

The coordinates of the upper left corner of the upper left chunk are stored

at 0x2F5 and 0x2F6. Both coordinates are divisible by 0x10.

In settlements, both coordinates are set to 0.

--- Movement Lists ---

When it's time for an NPC to move to a new location (as dictated by his schedule), the game calculates a path to the new location and stores this

path in the NPC's movement list.

The movement lists are stored at 0xBB8. There are 0x20 lists (one for each

NPC). Each list can store up to 0x10 movement commands.

The movement commands are made up of two bytes: the number of repetitions

and the direction.

struct Movement_List_Table {

Movement_List mov_lists[0x20];

}

struct Movement_List {

Movement_Command mov_commands[0x10];

}

struct Movement_Command {

uint8 repeats;

// 1 = east

// 2 = north

// 3 = west

// 4 = south

uint8 direction;

}

The table at 0xFB8 contains 0x20 word offsets (one for each NPC).

Entries can have the following values:

0xFFFF = movement list for the NPC is empty

else = offset into the NPC's movement list (must be a multiple of 2)

If the NPC hasn't reached his destination after going through the movement

commands, the game calculates a new path and stores it in the NPC's

movement list.

Notes:

1) There are no up/down movement commands. When an NPC wants to move to a

location on another level, he goes to the nearest stair/ladder and then

teleports to his destination.

--- Monster Format ---

offset     length      purpose

0          1           tile

1          1           tile

2          1           x coordinate

3          1           y coordinate

4          1           z coordinate (level)

5          1           depends on object type

6          1           depends on object type

7          1           depends on object type

Notes:

1) The monster table contains not only monsters, but also inanimate objects

(e.g. empty ships) and the party.

2) The party is always in slot 0.

3) Non-empty entries don't have to be contiguous.

Todo: empty entries, object-specific fields

--- Character record format ---

offset     length      purpose             range

0          9           character name      zero-terminated string

(length = 8+1)

9          1           gender              0xB = male, 0xC = female

0xA        1           class               'A'vatar, 'B'ard, 'F'ighter,

'M'age

0xB        1           status              'G'ood, etc.

0xC        1           strength            1-50

0xD        1           dexterity           1-50

0xE        1           intelligence        1-50

0xF        1           current mp          0-50

0x10       2           current hp          1-240

0x12       2           maximum hp          1-240

0x14       2           exp points          0-9999

0x16       1           level               1-8

0x17       1           ? ?

0x18       1           ? ?

0x19       1           helmet              0-0x2F,0xFF

0x1A       1           armor               0-0x2F,0xFF

0x1B       1           weapon or shield (left hand)    0-0x2F,0xFF

0x1C       1           weapon or shield (right hand)   0-0x2F,0xFF

0x1D       1           ring                0-0x2F,0xFF

0x1E       1           amulet              0-0x2F,0xFF

0x1F       1           inn/party           n/a

Notes:

1) If the character is staying at an inn, the byte at offset 0x1F contains

the settlement number (see "Party Location") of the inn.

If the character is in the party, it contains 0.

If the character hasn't joined the party yet, it contains 0xFF.

If the character has been permanently killed, it contains 0x7F.

--- Equipment ---

0 = Leather Helm

...

0x2F = Ankh

0xFF = none

--- Party Location ---

0x0     Britannia/Underworld

0x1     Moonglow

0x2     Britain

0x3     Jhelom

0x4     Yew

0x5     Minoc

0x6     Trinsic

0x7     Skara Brae

0x8     New Magincia

0x9     Fogsbane

0xA     Stormcrow

0xB     Greyhaven

0xC     Waveguide

0xD     Iolo's Hut

0xE     Sutek's Hut

0xF     Sin'Vraal's Hut

0x10    Grendel's Hut

0x11    Castle British

0x12    Castle Blackthorn

0x13    West Britanny

0x14    North Britanny

0x15    East Britanny

0x16    Paws

0x17    Cove

0x18    Buccaneer's Den

0x19    Ararat

0x1A    Bordermarch

0x1B    Farthing

0x1C    Windemere

0x1D    Stonegate

0x1E    The Lycaeum

0x1F    Empath Abbey

0x20    Serpent's Hold

0x21    Deceit

0x22    Despise

0x23    Destard

0x24    Wrong

0x25    Covetous

0x26    Shame

0x27    Hythloth

0x28    Doom

0xFF    combat/resting/shrine

= Sources =

Nytegard 

http://nodling.nullneuron.net/nytegard/nytegard.html

http://nodling.nullneuron.net/ultima/ultima.html

http://martin.brenner.de/ultima/u5save.html

http://www.cosy.sbg.ac.at/~lendl/ultima/ultima5/ on the Internet Archive

http://www.wi.leidenuniv.nl/~psimoons/ultima5t.htm on the Internet Archive

Sheng Long Gradilla 

Brad Hannah 