Ultima VII Internal Formats

The information on this page was taken from the documents folder of Exult's source code. You can find the original here.


 * TECHNICAL REFERENCE AND INTERNAL FORMATS OF ULTIMA7 FILES $Revision: 1.25 $ ****

This is a list of all the U7 files I have studied while developing U7Wizard. You will find in here technical descriptions of format and content of the files. Each time you see a "?" in this file, this means I was unable to find the meaning of some data. You can help complete this technical reference by sending your remarks and discoverings to u7wizard@pulsar.eu.org

You can download the latest version of this document from the U7Wizard Official Web Site: http://www.pulsar.eu.org/wizard/ultima/u7wizard.htm "U7Wizard: The Ultimate World & Scenery Editor for the U7 game engine"

You can freely redistribute this document as long as you don't modify it. You are not allowed to make profit using the informations contained in this document.

The author cannot be held responsible for any damage or other consequences of using this document. This document is unofficial. It is the result of pure guess. No reverse-engineering of machine code was involved in this process. "Ultima 7", "The Black Gate" and "Serpent's Isle" are registered trademarks from Origin Systems, Inc.

Olivier Marcoux --- I decided to leave Olivier's notes at the top. He's done much more than myself in figuring things out and assembling information. However, I need a document for my own purposes...so, I'm making notes here as I find out more and making the odd correction --- HISTORY

1.0      First public release (Olivier, we owe you one) ...      <- You are here ?.?      An ongoing voyage of discovery. We'll get back to you

--- DATA TYPES

Simple data types used in the description of the formats: char   = unsigned 8 bits representing a character shortint= signed 8 bits byte   = unsigned 8 bits uint16 = signed 16 bits int16   = unsigned 16 bits longint = signed 32 bits

When describing the format of the data, i will use the following conventions: A = int16 [b0=.. bF=..] means that A is a int16 (b0..bF are the description of each byte) A = nn B means that A is composed by nn times B A = B, C, D  means that A is composed by B followed by C followed by D A = set of B  means that A is composed by a certain number of B A = B | C  means that A can be composed by either B or C xx        (2 hex digits) means a byte constant xxxx     (4 hex digits) means a int16 constant xxxxxxxx (8 hex digits) means a longint constant

Chunk items = shapes coming from the U7CHUNKS map file Fixed items = shapes coming from U7IFIX files Game items = shapes coming from U7IREG files

--- COLLECTION FILE FORMATS

U7 games make extensive use of Flex Files. A flex file is a collection of objects, it is composed of: -a header (128 bytes long) header = title, magic1, count, magic2, d1..d9  title   = $50 characters (optionnal, filled with 00s) magic1 = longint (seems to be always $FFFF1A00) count  = longint (number of object in table, including empty objects) magic2 = longint (seems to be always $000000CC) d1..d9 = longints (often set to 0, but sometimes used, meaning?) -a table of references reference = offset, size offset   = longint (relative to the beginning of the file) size     = longint note: empty objects are referenced as null offset and null size -the objects data (in the same order as declared in the table) the format depends on the object type

struct	flex_hdr {	char	title[80]; uint32	magic1; uint32	count; uint32	magic2; uint32	padding[9]; };

struct	flex_reference_entry {	uint32	offset; uint32	size; };

U7 games use also IFF files. (Interchange File Format) An IFF file is a collection of file associated with their type/name. It is composed of: -a header (12 bytes long) header = 'FORM', size, type size   = reversed longint (size of the file excluding the first 8 bytes) type   = 4 chars representing the type of data contained in the IFF file -the objects entries entry  = type, size, object, [even] type   = 4 chars representing the type of this object size   = reversed longint (size of the entry excluding the first 8 bytes) even   = 1 byte (set to 0) present only to get an even number of bytes (the objects found in U7 IFF files have the following format:) object = name, data name   = 8 chars (filled with 0s) data   = the data of the object

struct	IFFhdr {	char	form_magic[4]; uint32	size; char	data_type[4]; }; struct	IFFobject {	char	type[4]; uint32	size; char	even; }; struct	u7IFFobj {	char	name[8]; char	data[];	// Variable };

U7 games use also table files. A table file is a collection of object of the same type. It is composed of: -a table of reference reference = info, offset info   = 2 bytes (meaning?) offset = longint (relative to the beginning of the file) -the objects entries (in the same order as declared in the table) entry  = size, data size   = int16 (including the size int16) data   = the data of the object

U7 games use also enumeration files. An enumeration file is a short collection of data of same type/length It is composed of: enumeration = size, entries size   = byte (number of entries) entry  = depends of the file, constant length

--- COMMON OBJECTS

Here is a description of the known object type that can be found in a flex or iff file. Remember that the size of each object is given in the flex/iff entries so that you can calculate the size of the variant parts of the object

This is a set of graphic tile representing a certain type of floor that can appears on the ground in the game view. One tile is 8x8 pixel large Format: tiles = set of tile tile = 8x8 pixel pixel = byte (index inside the color palette)
 * Tiles

This is the graphic representing any item that appears on the game view except floor tiles. The graphic can include transparent area. A shape represent a type of object. One shape can have many frames representing the different appearance of this kind of object. Each frame is describe as a set of "slices" composing the non-transparent area of the graphic. A shape can be found in a flex file or as a separate .SHP file Format: shape  = size, set of offset, set of frame size   = longint (should be same as in flex table) offset = longint (one for each frame, relative to beginning of shape) frame  = rightX, leftX, leftY, rightY, set of slice (coordinates are positive distance from the hot spot of the shape) slice  = int16=0000   (if end of frame) | slength, offsetX, offsetY, scontent (a slice represents several pixels on the same line) slength = int16 [b0   =type of slice (0=standard, 1=compressed) b1..bF=length in pixel] offsetX = uint16 (relative to hot spot) offsetY = uint16 (relative to hot spot) scontent= set of pixel (if standard slice) | set of block (if compressed slice) block  = blength, bcontent blength = byte [b0   =type of block (0=standard, 1=repeated pixel) b1..b7=length in pixel] bcontent= set of pixel (if standard block) | pixel        (if repeated pixel block)
 * Shape

A palette is an array describing the RGB values for 256 color indexes This format is compatible with the bios call for setting a palette. A palette can be found in a flex file or as a separate .PAL file Format: palette = 256 rgb rgb    = red green blue red    = byte (0..3F) green  = byte (0..3F) blue   = byte (0..3F)
 * Palette

A font is simply a shape where each frame represents the graphic for each ASCII letter Format: font   = shape Note: the number of frame of a shape is not limited to 32 as     suggested by the U7CHUNKS file
 * Font

A string is a short single line of text ending with a null char Format: string   = set of char
 * String

A text is a like a text file, composed with lines of characters. The texts used in the main menu contains backslashed sequences that indicates the output format of the lines: \Px  include picture number x (frame of MAINSHP.FLX shape 14h) \C   center line \L   left-aligned line Format: text   = set of char (with 0D 0A at end of lines)
 * Text

There are 2 kind of midi music found in flex files: -MID music that share the same format as a .MID file these musics can be extracted and saved as a .MID file to be played for example with Windows Media Player -XMI music & special effects that share the same format as a .XMI file these musics can be extracted and saved as a .XMI file to be played Note: I don't know of a player for such XMI files but there must be some Origin FX screen saver uses these files The format of such file is beyond the scope of this document
 * Midi music

This contains a vocal waveform that share the same format as a .VOC file These speeches can be extracted and saved as a .VOC file to be heared for example with WinAmp The format of such file is beyond the scope of this document There's an internet FAQ on this file format. Unusually, the 'number of channels' field seems to contain bogus data, and there seems to be some crud at the beginning and end of each VOC. Trimming 32 bytes off each end eliminates the crackle that it causes,
 * Vocal speech

This is used to store various drivers in a flex file, for example sound cards drivers. Format: seems to be pure machine code
 * Drivers

--- STATIC DIRECTORY

Here is a description of the files found in the STATIC directory: (some infos here are specific to Serpent's Isle)

U7MAP:       12x12 regions region = 16x16 chunkID chunkID = int16 (0..$C00-1) U7CHUNKS:    $C00 chunks chunk  = 16x16 shapeID shapeID = int16 [b0..b9=shapeType (0..$400-1) bA..bE=shapeFrame (0..$20-1) bF=? ] SHPDIMS.DAT: $400-$96 shpdims  (one for each Game Shape) shpdims = dimY, dimX dimY   = byte [b0: obstacle in N-S direction? b1..b7: Y dimension of the shape=number of tiles covered?] dimX   = byte [b0: obstacle in W-E direction? b1..b7: X dimension of the shape=number of tiles covered?] WGTVOL.DAT:  $400 wgtvol       (one for each Game Shape & Tiles) wgtvol = weight, volume weight = byte: weight of the shape (in 0.1 stone) volume = byte: volume of the shape (stone is the unit used in the inventory window in the game) TFA.DAT:     $400 entries (one for each Game Shape & Tiles) tfa    = $400 triplet triplet = tfa1, tfa2, tfa3 tfa1   = byte [b0..1 =? b2   =animated shape b3..4 =? b5..b7=height of shape] tfa2   = byte [b0..b6=? b7   =is transparent (cannot be selected, cannot place item on it)] tfa3   = byte [b0..b2=X size of the shape - 1  (number of tiles covered) b3..b5=Y size of the shape - 1 (number of tiles covered) b6   =is a light source b7   =contains transparency colors (light,dark,blood)] SHAPES.VGA:  flex file with $464 shapes $0..$95    the Game Tiles (floor) $96..$3FF  the Game Shapes $400..$40B extra shapes (sex/skin/dress variants of Avatar) $40C..$463 empty FACES.VGA:   flex file with shapes (represents the face of the people you can speak with) there are also some full-screen drawings that appears during the game: $100      full-screen deamon from The Black Sword $125..$127 full-screen serpents $128      full-screen guardian $12C      full-screen semi-transparent serpent and some strange faces : $103,$104 ?   $106       a cat speaking ? $129      weird!? SPRITES.VGA: flex file with shapes (used to display maps or special effects during the game) PAPERDOL.VGA: flex file with shapes (parts of suits,weapons,armor,etc.. that are combined on the inventory screen to show what the character is wearing) GUMPS.VGA:   flex file with shapes (parts of the user interface of the game like bags, windows, buttons...) FONTS.VGA:   flex file with fonts TEXT.FLX:    flex file with lots of string (name of shape, quotes, cheat texts) PALETTES.FLX: flex file with palettes MAINSHP.FLX: flex file with various objects used in main menu Content for Black Gate/Forge of Virtue - palette from INTROPAL.DAT $0       shape (male face) $1       shape (female face) $2       shape (main screen)			0 $3       font $4       shape ("view introduction")		0 $5       shape ("start new game")		0 $6       shape ("view credits")		0 $7       shape ("return to menu")		0 $8       shape ("journey onward")		0 $9       font $A       shape ("sex:") $B       shape ("male/female") $C       shape ("name") $D       text (Guardian speech...) $E       text (Credits) $F       data (Guardian Face animation events) $10      text (Quotes) $11      shape ("view quotes") $12      shape ("view endgame") $13      ???  $14       shapes ("rated mp-13/origin fx/dana glover/voodoo") $15      text (lose message) $16      shape ("male") $17      shape ("female") $18      ???  $19-$1D   empty Content for Serpent Isle/Silver Seed $0       shape (the "warp" screen) $1       palette (to be used with the previous screen) $2       shape ("serpent's isle", the main menu title) $3       shape (mask for the portrait?) $4..$8   shape (menu options) $9       font  (used when the user types the name of the Avatar) $A..$C   shape (avatar options) $D       empty $E       text  (credits) $F       empty $10      text  (quotes) $11,$12  shape (additionnal main menu options) $13      shape (mouse cursor) $14      shape (pictures used in texts) $15      text  (game lost text) $16..$19 shape (additionnal avatar options) $1A      palette (to be used with previous shapes) $1B..$20 mid music $1B..$27 empty

ENDSHAPE.FLX: flex file with various objects used in intro (Black Gate only) The palettes from the FLEX file INTROPAL.DAT should be used - the palette index is specified in brackets

$0       Thorns on left $1       Thorns on right $2       Top left moongate $3       Right moongate $4       Bottom left moongate $5       Bottom right moongate $6       Cloth map (2) $7       Top right computer $8       Bottom right computer $9       Top left computer $A       Bottom left computer $B       Orb $C       Fist $D       Ultima VII $E       Butterfly $F       "Ultima VII The Black Gate The Quest Begins Christmas 1991" $10      "The Quest Begins Christmas 1991" $11      "Lord British presents..." (3) $12       Trees $15      "Something is obviously amiss" $16      "It has been a long time since your last visit to Britannia" $17      "The mystical Orb beckons you..." $18      "It has opened gateways to Britannia in the past" $19      "Behind your house lies the circle of stones" $1A      "Why is a Moongate already there?" $1B      Empty $1C      "You have but one path to the answer" $1D      Small intro screen $1E      Guardian's mouth $1F      Guardian's forehead $20      Guardian's eyes $21      Guardian "emerging 1" (frames 0-9) $22      Guardian "emerging 2" (frames 0-9) $23      Guardian "emerging 3 (final)" (frames 0-15) ENDGAME.DAT $0       FLIC with guardian coming into Black Gate $1       FLIC with Black Gate exploding $2       FLIC with flickering torches $3       Font $4       Font $5       Font $6       Font $7       Voice file $8       Voice file $9       Voice file $A       Shape file

INITGAME.DAT: flex file with archived files (these files are to be extracted to the GAMEDAT directory with their  original name. they represent the state of a new game. the format is the   same for the saved games GAMExx.U7) archived file = filename, data filename     = 13 characters giving the filename (filled with 00s) data         = the remaining is the file content SISPEECH.SPC: flex file with vocal speech ADLIBMUS.DAT: flex file with midi music (for Adlib sound card) MT32MUS.DAT: flex file with midi music (for Roland MT32 sound card) ADLIBSFX.DAT: flex file with xmi?midi? special effects (for Adlib sound card) ?? Instrument voices? What? MT32SFX.DAT: flex file with xmi?midi? special effects (for Roland MT32 sound card) ?? More questions, as above MAINMENU.TIM: flex file with instrument timbres (for the main menu) (This is used by the main menu. Format unknown?) [ NOT IN TBG ] MAINMENU.DRV: flex file with drivers (for the main menu) [ NOT IN TBG ] SNDDRVRS.DAT: flex file with drivers (for each type of supported sound cards) XFORM.TBL:   flex file with shape transform tables These are matrix used to transform the color of pixels that are seen through semi-transparent effect (glass, cloud, blood...) table    = 256 new_color (one for each possible color index) new_color = byte (the new color index to use)

There are 11 of these tables, and they're assigned to the 11 translucent colors (0xf4-0xfe) going backwards; i.e., table 0 applies to color 0xfe, 1 to color 0xfd, 2 to 0xfc,... 10 to 0xf4. When the renderer paints one of the translucent colors, it takes the pixel that's already in the frame buffer and looks it up in the appropriate xform table to get its replacement.

POINTERS.SHP: shape file (contains the mouse cursors used in the game) ENDSCORE.DAT: A RIFF file containing what _appears_ to be some kind of midi data, but not in the usual midi format. Could it be the XMI format that much of the rest of this file refers to? In most instances so far, this document incorrectly describes regular midi files as XMI files. INTRO.DAT / ENDGAME.DAT: iff file with intro/endgame animation data (contains FLIC animations, font, shape, speeches) INTROADM.DAT: MIDI file containing 6 midi tracks for the introductory sequence. I suspect this to be a flex file, and further suspect it to be for the adlib sound card. INTRORDM.DAT: The same, but I'm guessing that this is for the roland MT32. INTROSND.DAT: VOC file. The Guardian's chilling initial announcement. ADLIB.ADV:   driver for the Adlib sound card MT32MPU.ADV: driver for the Adlib sound card SBDIG.ADV:   driver for the SoundBlaster sound card SBPDIG.ADV:  driver for the SoundBlaster Pro sound card XMIDI.AD:    table file with data relative to midi music XMIDI.MT:    table file with data relative to midi instruments WIHH.DAT:    wihh entries for the game shapes table  = $400 offset offset = int16  (0 if no object associated with the shape) (the offsets are relative to the beginning of the file and point to objects:) object = 32 int16's (meaning? one per shape frame?) WEAPONS.DAT: weapons enumeration (jsf, Marzo) weapon   = type, family, ammo, damage, flags0, range, flags1, flags2, powers, 1 byte (0), usecode, strikeSFX, hitSFX, 2 bytes (0) type     = int16 (shape type) family   = int16 (if positive, consume ammo of this family if in in quiver;                     if -3, consume item quantity when ranged;                     if -2, consume item quality;                     if -1, weapon is the ammo) ammo     = int16 (if positive, shape to be used for missile when fired;                     if -3, use weapon shape for projectile;                     if -1, projectile is invisible) damage   = 1 byte, positive. flags0   = 1 byte, bit flags: b0 == lucky (easier to hit with) b1 == explodes b2 == no blocking? b3 == delete depleted weapon if family == -2 [b4..b7]: damage type 0 == normal damage, causes bleeding 1 == fire damage 2 == magic damage 3 == lightning damage, ignores armor 4 == ethereal damage, ignores armor 5 == sonic/poison damage, ignores armor 6, 7 == unnamed/unused damage types, they nevertheless work (even immunity and vulnerability flags) range    = 1 byte b0 == always hits [b1..b2]: uses 0 == hand-to-hand weapon 1 == poor thrown (seems to be +6-dist to hit) 2 == thrown (seems to be +6-dist/2 to hit) 3 == ranged (seems to be +6-dist/4 to hit; unsure about it) [b3..b7]: Strike range for uses < 3; max range for uses == 3; for 0 < uses < 3, if farther away than this, throw weapon flags1   = 1 byte b0 == returns b1 == must target object, unless if through an usecode attack (e.g., firedoom staff can attack empty tile, bow can't)         [b2..b3]: Very fast missile if nonzero, check missile speed otherwise [b4..b7]: Missile rotation: # of frames added each step flags2   = 1 byte [b0..b1]: Actor frames to use on melee [b2..b3]: Actor frames to use on ranged b4 == unknown [b5..b7]: missile speed 0 == fast 1, 2 == medium (seems to be no difference between 1 and 2) 3+ == slow (they all seem equally slow) powers   = 1 byte, bit flags (special powers), meaning: b0 == sleep b1 == charm b2 == curse b3 == poison b4 == paralyze b5 == magebane b6 == ?? (insects have this). b7 == harmless (SI) usecode  = int16 (Usecode function.  Called upon strike with event=4.) strikeSFX = int16. 1 + SFX# to play during attack. hitSFX? = int16. Doesn't seem to be used in the originals.

AMMO.DAT:    ammunitions enumeration ammunition= type, family, type2, damage, flags0, 1 byte (0), damtype, powers, 2 bytes (0) type     = int16 (shape type) family   = int16 (shape type of the base ammunition eg: lucky arrow->arrow) type2    = int16 (if -1, do not show sprite [does not override weapon setting];                     if -3, use ammo type as projectile;					 otherwise, shape to use as projectile) damage   = byte (extra damage) flags0   = byte b0 == lucky (easier to hit with; accumulates with weapon setting) b1 == always hits (OR'ed with weapon setting) b2 == returns (OR'ed with weapon setting) b3 == no blocking? [b4..b5]: drop type 0 to drop on misses 1 to drop always 2 to drop always 3 means homing (e.g., death vortex) IF also explodes b6 == Explodes/bursts (OR'ed with weapon's)         b7 == ??? damtype  = 1 byte [b0..b3]: ??? [b4-b7]: damage type, like a weapon's; used instead of weapon's		          damage type unless == 0 powers   = 1 byte, exactly like weapon's. A harmless missile may call usecode 0x7e1 on hit in SI. ARMOR.DAT:   armors enumeration armor    = type, defense, 1 byte (0), immunity, 5 bytes (0) type     = int16 (shape type) defense  = byte (points of damage removed from a successful attack;              seems to be randomized, and ignored by some damage types) immunity = 1 byte, bit flags (bit i means immune to damage type i) MONSTERS.DAT: monsters enumeration monster  = type, str, dex, int, com, ar, 1 byte(?), wp, flags, vulnerab, immunity, flags2, attack+?, equip, 2 bytes(0), sfx, 7 bytes(0) type     = int16 (shape type) str      = byte b0 == immune to sleep b1 == immune to charm [b2..b7]: strength dex      = byte b0 == immune to curse b1 == immune to paralysis [b2..b7]: dexterity int      = byte b0 == immune to poison b1 == ??? [b2..b7]: intelligence com      = byte [b0..b1] == alignment [b2..b7]: combat skill ar       = byte b0 == may split when killed (unless killed by vulnerability) b1 == can't die b2 == immune to sleep/charm/curse/paralysis b3 == Protected from death spells? [b4..b7]: armor wp       = byte [b0..b3]: reach [b4..b7]: damage (only if not using weapon???) flags    = 1 byte, bit flags b0 == flies b1 == swims b2 == walks b3 == ethereal b4 == no body b5 == ??? b6 == start invisible b7 == see invisible vulnerab = 1 byte, bit flags (bit i means double damage of damage type i)  immunity  = 1 byte, bit flags (bit i means immune to damage type i)  flags2    = 1 byte, bit flags b5 == can't yell b6 == doesn't bleed attack   = byte, partially unknown [b0..b2]: Sets initial attack mode based on chance. [b3..b7]: ??? equip    = byte, record number in EQUIP.DAT sfx      = byte, sfx to play when striking EQUIP.DAT: equipment enumeration equipment = 60 bytes (meaning?) READY.DAT: weapon ready enumeration ready    = type, ready, 6 bytes (0) type     = int16 (shape type) ready    = byte b0 == is spell [b1..b2]: zero [b3..b7]: weapon slot SCHEDULE.DAT: schedule file schedules = count, activities, sched_types count    = longint (number of activities = number of npc with schedule) activity = int16 (index of the type of schedule) sched_type= int16 (meaning?) USECODE    = usecode functions list function = number, size, dseg, cseg number   = int16 (number of the usecode function, sometimes linked to shape type) size     = int16 (size of the data & code segments) dseg     = dsize, data (data segment) dsize    = int16 (size of the data) data     = texts/data used by the codes (strings are zero-terminated) cseg     = args, locals, links, usecodes args     = int16 (number of arguments) locals   = int16 (number of local variables) links    = count, link, ..., link count    = int16 (number of link) link     = int16 (number of a usecode function that will be called by this function) usecodes =  LINKDEP1 = usecode functions link dependencies (one dependency for each usecode function number plus one, even if the function is not defined in USECODE) dependency= index, size index    = int16 (index of first pointer for this function in LINKDEP2, starting from 0) size     = int16 (size in bytes taken by all functions linked to this function, FFFF if no function) LINKDEP2 = linked functions pointer list list     = pointer, ..., pointer pointer  = longint (offset of usecode function inside the USECODE file) OCCLUDE.DAT = bit array (128 byte = $400 bit) one bit per game shape. set if the shape completely occludes the space it is covering (not sure?) U7VOICE.FLX = flex file with what we believe to be midi instrument voices. The format for each voice is uncertain U7IFIXnn   = flex file with fixed items list (nn represents the region number.  There are 16x16 lists in each file, one for each chunk in the region.   The lists contain the static items found in the given chunk.   Static items are items which can't interact with the user) list   = entries (each entry is 4 bytes long) entry  = coord, lift, shapeID coord  = byte [b0..b3=Y coord inside chunk b4..b7=X coord inside chunk] lift   = byte [b0..b3=lift level b4..b7=0] shapeID = int16 [b0..b9=shapeType (0..$400-1) bA..bE=shapeFrame (0..$20-1) bF=0]

--- GAMEDAT DIRECTORY

Here is a description of the files found in the GAMEDAT directory:

MAPCOORDS.DAT = coordinates of the avatar coords = X, Y, d1, d2, d3, d4  X       = int16 (X coordinate 0..3071) Y      = int16 (Y coordinate 0..3071) d1,d2,d3= byte (meaning?) d4     = int16 (meaning?)

IDENTITY = text file containing the name of the world one single line, followed by RC LF (0D 0A) can also be followed by EOF (1A) it "ULTIMA7", "FORGE", "SERPENT ISLE", "SILVER SEED"

RANDSEED = current value of the seed used by the random number generator seed   = longint

U7IREGnn   = same meaning as U7IFIX but for game items (different format) Each U7IREGnn file contains the game items for the region number nn. Game items are items the user can interact with: move, use etc... Note: fixed items items from 2 chunks around the Avatar position are copied in   the U7IREG files reg_items = 16x16 chk_items (one for each chunk of the region) chk_items = item, item, ..., item, 00 (or just 00 if no item for this chunk) item     = standard | extended | extra standard = 06, XY, shapeID, lift, quality extended = 0C, XY, shapeID, type, proba, data1, lift, data2, [content] extra    = 12, XY, shapeID, extradata (content is present if shapeID represents a container and type not null) content  = item, item, ..., item, 01 shapeID  = int16 [b0..b9=shapeType  (0..$400-1) bA..bE=shapeFrame (0..$20-1) bF=0] lift     = byte [b0..b3=? (inside:6, outside:0..3 - same for all items                             in region, 4 seems to be unused NPC) b4..b7=lift level] quality  = byte (quality of the item)

for outside items: XY       = X, Y  X         = byte (X coordinate inside the region) Y        = byte (Y coordinate inside the region)

for inside items: (item in content) XY       = referent of the parent container referent = int16 (offset of the data inside U7IBUF.DAT)

virtue stones have length 0xc, with: entry[4] = tilex (cx,tx) entry[5] = tiley (cy,ty) entry[6] = superchunk #.

for containers: type     = referent of the first item in the container (0000 if empty) proba    = byte (? always 00 or current region number) data1    = quality, quantity data2    = resist, flags quality  = byte (00: no key 01..F9: matching key FA..FF: trap                    for corpse, 00..01: npc# >> 7 02: can't be raised) Exult: for corpse, 00: dead NPC 01..FF: unused quantity = byte (00 or 01 or npc# & $7F if corpse can become NPC) resist   = byte (attack/lockpicking resistance points) flags    = byte [b0:invisible b1..b2:0 b3:okay to take b4:? b5:halt flag? b6:0 b7:set if human NPC is carrying lit light sources] note: key-locked containers cannot be lockpicked but can be attacked if resistance points are > 0

for actors: type     = referent of the first item in the container (0000 if empty) proba    = byte (? always 00 or current region number) data1    = attack, quantity data2    = hps, flags attack   = byte (the NPC's attack mode) quantity = byte (?) hps      = byte (actor's current health) flags    = byte [b0:invisible b1..b2:0 b3:okay to take b4:? b5:halt flag? b6:0 b7:set if human NPC is carrying lit light sources]

for barges: type     = sizeX, sizeY proba    = byte (? id of the barge?) data1    = flags, 00 data2    = 0000 sizeX    = byte (X number of tiles covered by the barge) sizeY    = byte (Y number of tiles covered by the barge) flags    = byte [b0:0 b1:currently horizontal b2:? same as b1 except for the flying carpet b3..b7:0]

for a spellbook: extradata = circle1, .., circle5, lift, circle6, .. circle9, flags circle   = byte [b0=book contains spell 1 of this circle ...                   b7=book contains spell 8 of this circle] flags    = longint  [b0..b17=? b18..b1f=bookmarked spell]

for all egg items: (if shapeID represents an Egg) type     = int16 [b0..b3=egg type b4..b6=criteria b7=nocturnal b8=once ever b9=hatched bA..bE=distance for activation bF=auto-reset] proba    = byte (probability for activation 0..100)

for none egg (0):

for monster egg (1): data1    = mode, workType mode     = byte [b0..b1:alignment b2..b7:number] workType = byte data2    = int16 (type of creature, 6 bits frame, 10 bits shape) data3    = int16 (Exult:  data3>0 => data3 is shape, data2(7:0) is frame)

for jukebox egg (2): data1    = score, flags score    = byte flags    = byte [b0:continuous b1..b7=0] data2    = 0000

for sound effects egg (3): (must be verified?) data1    = sfxNum, flags sfxNum   = byte flags    = byte [b0:continuous b1..b7=0] data2    = 0000

for voice egg (4): data1    = speechNum, 00 speechNum = byte data2    = 0000

for usecode egg (5): data1    = quality, quantity quality  = byte quantity = byte data2    = int16 (usecode function number)

for missile egg (6): (must be verified?) data1    = int16 (missile type) data2    = direction, frequency direction = byte frequency = byte

for teleport egg (7): data1    = quality, schunk quality  = byte schunk   = byte data2    = x, y (coordinates inside region) x        = byte y        = byte Note: if quality = 255, jumps to the given coordinates otherwise, jumps to the path egg (9) with the same quality

for weather egg (8): data1    = weather, duration weather  = byte (type of weather) duration = byte (number of minutes, null=continuous) data2    = 0000

for path egg (9): data1    = quality, nextQual quality  = byte nextQual = byte (next quality) data2    = 0000

for button egg (10): (must be verified?) data1    = area, 00 area     = byte  (area of effect) data2    = 0000

for intermap egg (11) (Exult only): data1    = map, schunk map      = byte schunk   = byte data2    = x, y (coordinates inside region) x        = byte y        = byte

ITEMNODE.DAT: itemnode = first_free, free_count, 0000?, chunks1, ..., chunks4, region1, .., region4, region1? first_free = referent (of first free block inside U7IBUF.DAT) free_count = int16 (number of free blocks) chunksN = 16x16 referent (of first visible shape in each chunk) regionN = byte (region number corresponding to chunksN)

U7IBUF.DAT: Cache file for objects in use (inventories, visible map shapes,...) It is composed with 8-bytes blocks that can be referenced by their offset (int16). Here is cached all NPC shape & content, all game items from cached regions (see ITEMNODE.DAT), all fixed items & chunk items from 2 chunks around the Avatar position block = next, XY, shapeID, info next   = referent (of next item, 0000 when no more item) offset = int16 XY = X, Y    (for outside items) | referent (of parent container) shapeID = int16 info = lift, quality (for standard items) | referent (of additionnal infos for extended items)

additionnal infos: block = type, proba, data1, lift, data2 (same as extended item in U7IREG)

PARTY: party = companion1, .. companion8, count, ??? companionN = referent (of Nth companion of the party in U7IBUF.DAT) count     = byte (number of companion in party) must contain str/int etc.. for the persons

U7NBUF.DAT: Cache file for NPCs in use It is composed with 105-bytes NPC definition block It contains the whole NPC list. It is build from NPC.DAT when creating a new game. The NPCs shape & inventory are in U7IBUF under the given referent. npcBlock = index, referent, status, str, dexterity, intel, combat, activity, DAM, 3-bytes?, status2, index2, 2-bytes?, exper, training, primary, secondary, oppressor, I-Vr, S-Vr, status3, byte?, target, weapon, acty?, SN, V1, V2, 29-bytes?, food, 7-bytes?, name index    = int16 (meaning? 1-based?) status   = int16 [b0: ? b1..b2: Heading direction (N,E,S,W) b3..b4: Follow/Alignment (Neutral,Good,Evil,Chaotic) b5,b6: True alignment (stored when NPC is charmed) b7: Asleep b8: Charmed b9: Cursed bA: ? (Busy?) bB: In Party bC: Paralyzed bD: Poisonned bE: Protected bF: Dead] str      = byte [b0..b4: Strength b5..b6: Skin color b7: Freeze] dexterity = byte intel    = byte [b0..b4: intelligence b5: Read b6: Tournament b7: Polymorph] combat   = byte [b0..b4: Combat skill b5..b6: ? b7: Petra] activity = byte (should be 0..31) DAM      = byte (default attack mode 0..9) status2  = int16 [b0..b4: Maximum Magic Points (for NPC#0) b0..b4: ID# b5..b7: Temperature (high 3 bits) b8..bC: Mana, Current Magic Points (for NPC#0) b8: Met b9: No Spell Casting bA: Zombie bB..bC: ? bD..bF: Temperature (low 3 bits)] index2   = byte (meaning? 0-based? face from FACES.VGA ?) exper    = longint (experience points) training = byte (training points) primary  = uint16 (primary target NPC#) secondary = uint16 (secondary target NPC#) oppressor = uint16 (oppressor NPC#) I-Vr     = vector (called "I-Vr" in cheat menu, means?) S-Vr     = vector (location where the NPC is supposed to be for his schedule) vector   = uint16, uint16 status3  = int16 [b0..b2: D/R (called "D/R" in cheat menu, means?) b3: ? b4: Fly b5: Walk b6: Swim b7: Ethereal b8: Want Primary b9: Sex (M/F) bA: Bleeding bB: In Party bC: Might bD: In Action bE: Conjured bF: Summonned] target   = XY  XY = X, Y     | referent (of target object) X        = byte (X coordinate delta to target tile) Y        = byte (Y coordinate delta to target tile) weapon   = int16 [b0..b3: 1-based index of weapon in weapons.dat b4: ? b5: Set if targetting tile b6..b7: ?] food     = byte (food level) SN       = byte (meaning?) V1       = int16 (meaning?) V2       = int16 (meaning?) name     = 16-chars (zero-terminated string)

NPC.DAT: Initial NPC definition (taken from INITGAME.DAT) npcDef   = npc1count, npc2count, npc, npc, ..., npc npc1count = number of NPC type 1 in this file npc2count = number of NPC type 2 in this file (there are npc1count+npc2count npc in the file) npc      = extended, npcBlock, [inventory] (inventory is present if type of extended item not null) extended = part after the 0C of an extended item (see U7IREG) header   = 12-bytes? npcBlock = see U7NBUF.DAT file inventory = item, item, ..., item, 00 item     = see U7IREG files

FRAMES.FLG: $400 frame_flag frame_flag = longint (-1 if not applicable) (meaning?)

--- USECODE OPCODES

List of arguments that can be found after the opcode byte: argument size description 2 zero-based index of a local variable (the first local variables are the arguments of the function) 2 offset inside the code segment, relative to beginning of the next opcode 2 offset of a string inside the data segment 1 immediate 8-bits value 2 immediate 16-bits value 2 zero-based index of a usecode function inside the links array 2 number of a usecode function 2 index of an external function native to the game engine 2 index of a game flag

name codes description ?          00 ?           01 LOOP        02     : beginning of a loop gets each value of the array to be verified: receive the 1-based index of the loop to be verified: receive the number of values in the array once the loop is over, to the end of the loop ?          03 JTRUE/JNZ   04 : pop a boolean/integer, if it's true/non-zero  (need to be verified) JFALSE/JZ  05 : pop a boolean/integer, if it's false/zero JMP        06 : do an immediate CMPS       07  : ? (need to be verified) ?          08 ADD         09: pop 2 integers, add them and push the result SUB        0A: pop 2 integers, substract 1st from 2nd and push the result DIV        0B: pop 2 integers, divide 2nd by 1st and push the result MUL        0C: pop 2 integers, multiply them and push the result MOD        0D: pop 2 integers, operate 2nd modulo 1st and push the result AND        0E: pop 2 booleans, operate a "and" and push the result OR         0F: pop 2 booleans, operate a "or" and push the result NOT        10: pop a boolean, operate a "not" and push the result ?          11 POP LOCAL[] 12 : pop a value inside the given PUSH TRUE  13: push the boolean TRUE PUSH FALSE 14: push the boolean FALSE ?          15 TEST >      16: pop 2 integers, test if 2nd is greater than 1st, push the boolean result TEST <     17: pop 2 integers, test if 2nd is less than 1st, push the boolean result TEST >=    18: pop 2 integers, test if 2nd is greater or equal to 1st, push the boolean result TEST <=    19: pop 2 integers, test if 2nd is less or equal to 1st, push the boolean result TEST !=    1A: pop 2 integers, test if they are different, push the boolean result ?          1B CONCAT     1C : concatenate the given string to the string register PUSH       1D : push the given string ARRAY      1E : pop the given number of value, create an array with them and push the result PUSH       1F : push the immediate 16-bits value ?          20 PUSH LOCAL[]21 : push the value of the given TEST ==    22: pop 2 integers, test if they are equal, push the boolean result ?          23 CALL        24 : call the given usecode function from the links array RET        25: return from function GET        26 : pop an integer index and push the indexed value from the array ?          27 ?           28 ?           29 ?           2A ?          2B ABORT2     2C: abort the function (need to be verified) POP RESULT 2D: pop a value and set it as the return value of the function SLOOP      2E: initiate a loop (always followed by the opcode 02) CONCAT     2F : concatenate the string from the to the string register TEST IN    30: pop an array and a value, test if value is inside the array, push the boolean result ?          31 : ? RET RESULT  32: push the return value of the function and return from function SAY        33: say the string register as part of current talk and empty the string register ?          34 ?           35 ?           36 ?           37 CALLIS      38  : call the external function with the given number of argument on the stack. push the result on the stack CALLI      39  : call the external function with the given number of argument on the stack. no result on the stack ?          3A ?          3B ?          3C ?          3D PUSH REF   3E: push an identifier of the game item for which the usecode function has been called EXIT       3F: abort the function and any previous call (need to be verified) ?          40: ? ?           41 PUSH FLAG[] 42 : push the given game flag as a boolean POP FLAG[] 43 : pop a boolean as the given game flag PUSH       44 : push the immediate 8-bits value ?          45 : ? PUT         46 : pop an integer index and a value, replace the indexed value in the array CALL       47 : call the given usecode function (must appear also in the links array) PUSH EVENT 48: push an integer that identify the reason why the usecode function has been called ?          49 ARRAYADD    4A: pop a value and an array, add the value at the end of the array and push the result POP EVENT  4B: pop an integer and set it as the current reason identification ?          4C through FF --- WHAT REMAINS TO BE EXPLAINED

how does the association "NPC" <-> "FACES.VGA" work some shapes have different title for each frame (eg: desk items) what does bit 15 of shapeID mean some infos about NPCs are not yet found

plus, everywhere you find a "?" in this file, there is something i was unable to figure out.

---

Olivier Marcoux u7wizard@pulsar.eu.org