Ultima III internal formats
Note: This file was originally found here.
SHAPES.ULT[edit]
This file contains 0x50 tiles. All tiles are 16*16 pixels^2. Each tile in stored in CGA-compatible format. The first 0x20 bytes of each tile contain the first bit plane, the following 0x20 bytes contain the second bit plane.
| Offset | Length | Purpose |
|---|---|---|
| 0x0 | 0x40 | tile 0x0 |
| 0x40 | 0x40 | tile 0x1 |
| 0x80 | 0x40 | tile 0x2 |
| ... | ... | ... |
| 0x13C0 | 0x40 | tile 0x4f |
- The Ultima III monochrome, CGA, and EGA tileset
The CGA tileset uses the following structure which is a linear format with interlacing:
- The tile data is organized into two 32-byte planes:
- Plane 0: Contains even rows (0, 2, 4, 6, 8, 10, 12, 14)
- Plane 1: Contains odd rows (1, 3, 5, 7, 9, 11, 13, 15)
- Each plane uses CGA linear encoding where one byte encodes 4 pixels using 2 bits per pixel:
- Bits 7-6: First pixel
- Bits 5-4: Second pixel
- Bits 3-2: Third pixel
- Bits 1-0: Fourth pixel
- Each row consists of 16 pixels encoded in 4 bytes. The 2-bit values map to the CGA palette:
- 00: Black
- 01: Cyan
- 10: Magenta
- 11: White
The index within this character set is also used to index the XP table. The table repeats every 16 monsters (with 2 rows never used), which explains why such difficult monsters like Floors or Grasses only get you a few XP. (It's not possible to actually kill Lord British or Exodus, but it's fun to see what their XP would be if you could.)
| Water | Merchant | 1 |
| Grass | Jester | 2 |
| Brush | Guard | 15 |
| Lord British | 20 | |
| Fighter | 8 | |
| Cleric | 6 | |
| Wizard | 10 | |
| Thief | 5 | |
| Floor | Orc | 3 |
| Chest | Skeleton | 4 |
| Horse | Giant | 6 |
| Daemon | 8 | |
| Pincher | 10 | |
| Serpent | Dragon | 15 |
| Man-O-War | Balron | 20 |
| Ranger | Exodus | 5 |
CHARSET.ULT[edit]
This file contains the 0x80 characters that make up the U3 font. All characters are 8*8 pixels^2. Each character in stored in CGA-compatible format. The first 0x8 bytes of each character contain the first bit plane, the following 0x8 bytes contain the second bit plane.
| Offset | Length | Purpose |
|---|---|---|
| 0x0 | 0x10 | character 0x0 |
| 0x10 | 0x10 | character 0x1 |
| ... | ... | ... |
| 0x7F0 | 0x10 | character 0x7F |
SOSARIA.ULT[edit]
This file contains the world map and information about the game world state.
| Offset | Length | Purpose |
|---|---|---|
| 0x0 | 0x1000 | 64*64 world map |
| 0x1000 | 0x180 | not used |
| 0x1180 | 0x20 | tile number of monster 1-32; divide by 4 to get real tile number |
| 0x11A0 | 0x20 | tile number of floor under monster 1-32; divide by 4 to get real tile number |
| 0x11C0 | 0x20 | x coordinate of monster 1-32 |
| 0x11E0 | 0x20 | y coordinate of monster 1-32 |
| 0x1200 | 0x20 | movement flag of monster 1-32 |
| 0x1220 | 0x1 | x coordinate of whirlpool |
| 0x1221 | 0x1 | y coordinate of whirlpool |
| 0x1222 | 0x1 | signed byte to add to x coordinate of whirlpool; possible values: 0, 1, 0xFF |
| 0x1223 | 0x1 | signed byte to add to y coordinate of whirlpool; possible values: 0, 1, 0xFF |
| 0x1224 | 0x1 | current phase of left moon; ranges from 0-7 |
| 0x1225 | 0x1 | current phase of right moon; ranges from 0-7 |
| 0x1226 | 0x1 | current sub-phase of left moon; ranges from 0-0xB |
| 0x1227 | 0x1 | current sub-phase of right moon; ranges from 0-3 |
Tiles[edit]
| Tile # | Notes |
|---|---|
| 0x00 | Water |
| 0x04 | Grass |
| 0x08 | Brush |
| 0x0C | Forest |
| 0x10 | Mountains |
| 0x14 | Dungeon |
| 0x18 | Towne |
| 0x1C | Castle |
| 0x20 | Floor |
| 0x24 | Chest |
| 0x28 | Horse |
| 0x2C | Frigate |
| 0x30 | Whirlpool |
| 0x34 | Serpent |
| 0x38 | Man-o-War |
| 0x3C | Pirate |
| 0x40 | Merchant |
| 0x44 | Jester |
| 0x48 | Guard |
| 0x4C | Lord British |
| 0x50 | Fighter |
| 0x54 | Cleric |
| 0x58 | Wizard |
| 0x5C | Thief |
| 0x60 | Orc |
| 0x64 | Skeleton |
| 0x68 | Giant |
| 0x6C | Daemon |
| 0x70 | Pincher |
| 0x74 | Dragon |
| 0x78 | Balron |
| 0x7C | Exodus |
| 0x80 | Force Field |
| 0x84 | Lava |
| 0x88 | Moon Gate |
| 0x8C | Wall |
| 0x90 | Void |
| 0x94 | Wall (Space) |
| 0x98 | A |
| 0x9C | B |
| 0xA0 | C |
| 0xA4 | D |
| 0xA8 | E |
| 0xAC | F |
| 0xB0 | G |
| 0xB4 | H |
| 0xB8 | I |
| 0xBC | V |
| 0xC0 | Y |
| 0xC4 | L |
| 0xC8 | M |
| 0xCC | N |
| 0xD0 | O |
| 0xD4 | P |
| 0xD8 | W |
| 0xDC | R |
| 0xE0 | S |
| 0xE4 | T |
| 0xE8 | Snake (tail) |
| 0xEC | Snake (head) |
| 0xF0 | Magic |
| 0xF4 | Fire |
| 0xF8 | Shrine |
| 0xFC | Ranger |
Monsters, horses, ships, chests[edit]
U3 stores the location of these objects by writing them directly into the map.
Monsters[edit]
Monsters are stored as basetile_number*4. The map tile under the monster is stored in the table at 0x11A0.
Horses[edit]
Horses are stored as 0x28 == horse_tile*4. The tile under the horse can only be 0x1, because you can only dismount a horse on a grass tile.
Ships[edit]
Ships are stored as 0x2C == ship_tile*4. The tile under the ship can only be 0x0.
Chests[edit]
The map tile under the chest is encoded in the lowest 2 bits of the tile: 0x24 == chest_tile*4 + 0 --> chest on bricks (tile 0x8) 0x25 == chest_tile*4 + 1 --> chest on grass (tile 0x1) 0x26 == chest_tile*4 + 2 --> chest on forest (tile 0x2) 0x27 == chest_tile*4 + 3 --> chest on deep forest (tile 0x3) Monsters can't walk onto chests, so there can only be one chest per map square.
Moon phases and sub-phases[edit]
Each turn, both sub-phases are decremented. When a sub-phase reaches -1, it is reset to 0xB/3, and the corresponding phase is incremented. The phases are the numbers displayed on the upper border of the game world window.
EXODUS.BIN[edit]
This is the file executable.
| Offset | Length (in bytes) | Purpose |
|---|---|---|
| 0x0 | 5600 | ??? |
| 0x015e1 | 4 | x/y coordinates of castles. LCB and Castle Death (two bytes, one for x and one for y) |
| 0x015e5 | 20 | x/y coordinates of towns (two bytes, one for x and one for y) |
| 0x015f9 | 14 | x/y coordinates of dungeons (two bytes, one for x and one for y) |
| 0x184d | 8 | moongate x-coordinates (one byte for each moongate) |
| 0x1855 | 8 | moongate y-coordinates (one byte for each moongate) |
| 0x6566 | 335 | list of null terminated strings for the (L)ook command |
| 0x7445 | 2 | xy-coordinates for Exotic Weapons (1 byte for x and one byte for y) |
| 0x7450 | 2 | xy-coordinates for Exotic Armour (1 byte for x and one byte for y) |
DEMO.ULT[edit]
This file contains the 19*6 map displayed in the main menu. Each byte represents a tile.
MOVES.ULT[edit]
This file contains the animation script for the main menu map.
| Offset | Length | Purpose |
|---|---|---|
| 0x0 | 0x200 | command table |
| 0x200 | 0x200 | data table |
ROSTER.ULT[edit]
This file stores the infomation about all the characters the player has created.
| Offset | Length | Purpose |
|---|---|---|
| 0x0 | 0x40 | character record 1 |
| 0x40 | 0x40 | character record 2 |
| ... | ... | ... |
| 0x4C0 | 0x40 | character record 20 |
Character record format[edit]
NOTE: BCD = binary coded decimal (two digits per byte).
| Offset | Length | Purpose |
|---|---|---|
| 0x0 | 0xA | character name; ASCII string, 0-terminated. Padded with 0's. |
| 0xA | 0x4 | ? |
| 0xE | 0x1 | marks and cards; bit 0-7 = love, sol, moon, death, force, fire, snake, kings |
| 0xF | 0x1 | number of torches; BCD |
| 0x10 | 0x1 | 0x0 = character is not in party, 0xFF = character is in party |
| 0x11 | 0x1 | status; ASCII character: G,P,D,A |
| 0x12 | 0x1 | strength; BCD |
| 0x13 | 0x1 | dexterity; BCD |
| 0x14 | 0x1 | intelligence; BCD |
| 0x15 | 0x1 | wisdom; BCD |
| 0x16 | 0x1 | race; ASCII character: E,D,F,H,B |
| 0x17 | 0x1 | class; ASCII character: W,R,T,I,A,D,F,L,C,B |
| 0x18 | 0x1 | gender; ASCII character: M,F,O |
| 0x19 | 0x1 | current magic points; BCD |
| 0x1A | 0x2 | current hit points; BCD |
| 0x1C | 0x2 | maximum hit points; BCD |
| 0x1E | 0x2 | experience points; BCD |
| 0x20 | 0x1 | sub-morsels; BCD |
| 0x21 | 0x2 | food; BCD |
| 0x23 | 0x2 | gold; BCD |
| 0x25 | 0x1 | gems; BCD |
| 0x26 | 0x1 | keys; BCD |
| 0x27 | 0x1 | powders; BCD |
| 0x28 | 0x1 | currently worn armor |
| 0x29 | 0x7 | how many of each armor type; BCD |
| 0x30 | 0x1 | currently readied weapon |
| 0x31 | 0xF | how many of each weapon type; BCD |
Sub-morsels[edit]
This field contains 2 digits (those behind the decimal point) of the food field. On the surface, the game subtracts 10 from this field during every turn (remember that it contains a BCD). Everywhere else, the game subtracts 10 every 4 turns.
Weapon types[edit]
| Offset | Letter | Name |
|---|---|---|
| 0x31 | B | Dagger |
| 0x32 | C | Mace |
| 0x33 | D | Sling |
| 0x34 | E | Axe |
| 0x35 | F | Bow |
| 0x36 | G | Sword |
| 0x37 | H | 2H Sword |
| 0x38 | I | +2 Axe |
| 0x39 | J | +2 Bow |
| 0x3A | K | +2 Sword |
| 0x3B | L | Gloves |
| 0x3C | M | +4 Axe |
| 0x3D | N | +4 Bow |
| 0x3E | O | +4 Sword |
| 0x3F | P | Exotic Weapon |
Armor types[edit]
| Offset | Letter | Name |
|---|---|---|
| 0x29 | B | Cloth |
| 0x2A | C | Leather |
| 0x2B | D | Chain |
| 0x2C | E | Plate |
| 0x2D | F | +2 Chain |
| 0x2E | G | +2 Plate |
| 0x2F | H | Exotic Armor |
PARTY.ULT[edit]
This file contains information about the current party. BCD = binary coded decimal (two digits per byte).
| Offset | Length | Purpose |
|---|---|---|
| 0x0 | 0x1 | mode of transportation; 0xA = horse, 0xB = ship, 0x3F = on foot |
| 0x1 | 0x1 | ? |
| 0x2 | 0x1 | party location |
| 0x3 | 0x4 | number of moves; BCD |
| 0x7 | 0x1 | number of characters in the party |
| 0x8 | 0x1 | x coordinate of party (on Sosarian map) |
| 0x9 | 0x1 | y coordinate of party (on Sosarian map) |
| 0xA | 0x4 | number of PC in slot 1-4 (party order) |
| 0xE | 0x4 | ? |
| 0x12 | 0x40 | character record for PC 1 |
| 0x52 | 0x40 | character record for PC 2 |
| 0x92 | 0x40 | character record for PC 3 |
| 0xD2 | 0x40 | character record for PC 4 |
Party location[edit]
| Value | Location |
|---|---|
| 0 | Sosaria |
| 1 | dungeon |
| 2 | towne |
| 3 | castle (LB's Castle, Castle Death) |
| 4 | shrine, fountain, mark |
| 0x80 | combat |
| 0xF0 | talking to merchant |
| 0xFF | Ambrosia |
Note: You can only save your game in Sosaria, so the party location field in the file is always 0.
.ULT files (town maps)[edit]
These files contain the map and "dialog" for each town. There are 32 NPC's in every town. (?)
| BRITISH.ULT | Lord British's castle |
| DAWN.ULT | Dawn |
| DEATH.ULT | Death Gulch |
| DEVIL.ULT | Devil Guard |
| EXODUS.ULT | Exodus (Castle Death) |
| FAWN.ULT | Fawn |
| GREY.ULT | Grey |
| LCB.ULT | Britain |
| MONTOR_E.ULT | Montor East |
| MONTOR_W.ULT | Montor West |
| MOON.ULT | Moon |
| YEW.ULT | Yew |
Map format[edit]
| Offset | Length | Purpose |
|---|---|---|
| 0x0 | 0x1000 | 64*64 town map |
| 0x1000 | n*0x2 | sign text offsets; n <= 8, add 0x1000 to get the real offset |
| 0x1000+n*0x2 | variable | sign texts and dialog; all texts are 0-terminated ASCII strings |
| 0x1180 | 0x20 | tile number for each NPC; divide by 4 to get real tile number |
| 0x11A0 | 0x20 | tile number of floor under each NPC; divide by 4 to get real tile number |
| 0x11C0 | 0x20 | starting x coordinate of each NPC |
| 0x11E0 | 0x20 | starting y coordinate of each NPC |
| 0x1200 | 0x20 | movement flag + dialog number |
| 0x1220 | 0x8 | not used |
Movement flag + dialog number[edit]
(byte >> 4) == 0 --> NPC walks around
(byte >> 4) == 4 --> NPC doesn't move
(byte >> 4) == 8 --> NPC is a merchant
(byte >> 4) == 0xC --> NPC attacks party
(byte % 0xF) == sentence number
Still unknown: 0x85, 0x86 (?)
.ULT files (dungeon maps)[edit]
These files contain the dungeon maps and sign texts.
| DARDIN.ULT | Dardin's Pit |
| FIRE.ULT | Dungeon of Fire |
| M.ULT | Dungeon of Doom |
| MINE.ULT | Mines of Morinia |
| P.ULT | Dungeon of the Snake |
| PERINIAN.ULT | Perinian Depths |
| TIME.ULT | Dungeon of Time |
Map format[edit]
| Offset | Length | Purpose |
|---|---|---|
| 0x0 | 0x100 | 16*16 map of level 1 |
| 0x100 | 0x100 | 16*16 map of level 2 |
| ... | ... | ... |
| 0x700 | 0x100 | 16*16 map of level 8 |
| 0x800 | n*0x2 | sign text offsets; n = 8, add 0x800 to get the real offset |
| 0x810 | 0x80 | sign texts; every sign text is a null-terminated ASCII string |
There is at most one sign per dungeon level. To find out the text for a sign, use (dungeon_level-1) as an index into the table at 0x800.
.ULT files (conflict maps)[edit]
These files contain the 11*11 conflict maps.
CNFLCT_A.ULT
CNFLCT_B.ULT
CNFLCT_C.ULT
CNFLCT_F.ULT
CNFLCT_G.ULT grasslands
CNFLCT_M.ULT
CNFLCT_Q.ULT
CNFLCT_R.ULT
CNFLCT_S.ULT
Map format[edit]
| Offset | Length | Purpose |
|---|---|---|
| 0x0 | 0x79 | 11*11 map; each byte represents one tile |
| 0x79 | 0x7 | ? |
| 0x80 | 0x8 | starting x coordinates for monsters 1-8 |
| 0x88 | 0x8 | starting y coordinates for monsters 1-8 |
| 0x90 | 0x8 | not used |
| 0x98 | 0x8 | not used |
| 0xA0 | 0x4 | starting x coordinates for PC's 1-4 |
| 0xA4 | 0x4 | starting y coordinates for PC's 1-4 |
| 0xA8 | 0x4 | not used |
| 0xAC | 0x4 | not used |
U3 uses the 8 bytes at offset 0x90 (in memory) to store the tiles under monster 1-8. U3 uses the 8 bytes at offset 0x98 (in memory) to store the hit points of monster 1-8. U3 uses the 4 bytes at offset 0xA8 (in memory) to store the tiles under PC 1-4. U3 uses the 4 bytes at offset 0xAC (in memory) to store the base tiles for PC 1-4. U3 doesn't use the values stored at these locations in the conflict files.
.IMG files[edit]
Each of these files contains an 11*11 tile image. Each byte represents a tile number.
| BRAND.IMG | Brand |
| FOUNTAIN.IMG | Fountain |
| SHRINE.IMG | Ambrosian shrine |
| TIME.IMG | Time Lord |
Further Notes[edit]
I'm pretty sure that U3 doesn't save the wind direction.
External Links[edit]
- Original copy of this article
- Ultimatrix website
- Decoding of the roster file format
- Transcript of the NPC dialogue
| Technical Details | |
|---|---|
| Game | Ultima III ☥ Ultima IV ☥ Ultima V ☥ Ultima VI ☥ Ultima VII ☥ Ultima VIII ☥ Ultima IX ☥ Ultima Underworld |
