1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150
|
**** 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
* Tiles
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)
* Shape
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)
* Palette
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)
* Font
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
* String
A string is a short single line of text ending with a null char
Format:
string = set of char
* Text
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)
* Midi music
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
* Vocal speech
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,
* Drivers
This is used to store various drivers in a flex file, for example sound
cards drivers.
Format: seems to be pure machine code
-----------------------------------------------------------------------
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
The Guardian Face animation events have the following format:
Array of face animation entries
entry = time, code
time = int16 time, in milliseconds, from beginning of voice playback
code = byte
$00 End of data
$01 Set eyebrows to "angry", keep eyes unchanged (frames 1/2/3)
$02 Set eyebrows to "neutral", keep eyes unchanged (frames 4/5/6)
$03 Set eyebrows to "raised", keep eyes unchanged (frames 7/8/9)
$04 Fully open eyes, keep eyebrows unchanged (frames 1/4/7)
$05 Partially open eyes, keep eyebrows unchanged (frames 2/5/8)
$06 Fully closed eyes, keep eyebrows unchanged (frames 3/6/9)
Eye frame descriptions:
1 "Angry" eyebrows, eyes fully opened, looking ahead
2 "Angry" eyebrows, eyes half-opened, looking ahead
3 "Angry" eyebrows, eyes closed, looking ahead
4 "Neutral" eyebrows, eyes fully opened, looking ahead
5 "Neutral" eyebrows, eyes half-opened, looking ahead
6 "Neutral" eyebrows, eyes closed, looking ahead
7 "Raised" eyebrows, eyes fully opened, looking ahead
8 "Raised" eyebrows, eyes half-opened, looking ahead
9 "Raised" eyebrows, eyes closed, looking ahead
10 "Raised" eyebrows, eyes fully opened, looking down
$07 Sets eyes to frame 10, but does not change last eye frame
for the purposes of codes $01 to $06
$08 to $16 Set mouth frame to code-8. Frame 0 is background, and it
should not be used here.
Above $17 Invalid/do not use
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
17 translucent colors (0xee-0xfe) going backwards; i.e., table 0
applies to color 0xfe, 1 to color 0xfd, 2 to 0xfc,... 16 to 0xee.
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 = <you will need a usecode disassembler>
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
<local> 2 zero-based index of a local variable (the first local variables are the arguments of the function)
<jump> 2 offset inside the code segment, relative to beginning of the next opcode
<data> 2 offset of a string inside the data segment
<byte> 1 immediate 8-bits value
<int16> 2 immediate 16-bits value
<link> 2 zero-based index of a usecode function inside the links array
<function> 2 number of a usecode function
<native> 2 index of an external function native to the game engine
<flag> 2 index of a game flag
name codes description
? 00
? 01
LOOP 02 <local1> <local2> <local3> <local4> <jump>: beginning of a loop
<local3> gets each value of the <local4> array
to be verified: <local1> receive the 1-based index of the loop
to be verified: <local2> receive the number of values in the array
once the loop is over, <jump> to the end of the loop
? 03
JTRUE/JNZ 04 <jump>: pop a boolean/integer, <jump> if it's true/non-zero (need to be verified)
JFALSE/JZ 05 <jump>: pop a boolean/integer, <jump> if it's false/zero
JMP 06 <jump>: do an immediate <jump>
CMPS 07 <int16> <jump>: ? (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 <local>: pop a value inside the given <local>
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 <data>: concatenate the given string to the string register
PUSH 1D <data>: push the given string
ARRAY 1E <int16>: pop the given number of value, create an array with them and push the result
PUSH 1F <int16>: push the immediate 16-bits value
? 20
PUSH LOCAL[]21 <local>: push the value of the given <local>
TEST == 22: pop 2 integers, test if they are equal, push the boolean result
? 23
CALL 24 <link>: call the given usecode function from the links array
RET 25: return from function
GET 26 <local>: pop an integer index and push the indexed value from the <local> 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 <local>: concatenate the string from the <local> 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 <4 bytes>: ?
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 <native> <byte>: call the external function with the given number of argument on the stack. push the result on the stack
CALLI 39 <native> <byte>: 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 <flag>: push the given game flag as a boolean
POP FLAG[] 43 <flag>: pop a boolean as the given game flag
PUSH 44 <byte>: push the immediate 8-bits value
? 45 <byte>: ?
PUT 46 <local>: pop an integer index and a value, replace the indexed value in the <local> array
CALL 47 <function>: 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
|