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 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 |