Daggerfall Mod:Quest hacking guide
Contents
Important Note Before Beginning[edit]
This document was created assuming you know the basics of hex editing and programming (not much), or, for the more technically minded. If you aren't one of these, you may not wish to read further.
All field/attribute definitions are given with conventional C types, a platform-independent byte representation in parenthesis, and the field's name. Field names will always be defined with PascalCaseConvention, and the field's name should be considered similar to a #typedef. Array field definitions will include the element count within square brackets, such as "Foo[10]" to indicate field Foo has ten elements. Instances of a field, the values of a particular file or record, are noted in camelCaseConvention and are enclosed in the <var>…</var> mark-up for "variable", such as "foo[7]". Remember that arrays start with element 0. So foo[6] would actually be the 7th element of the array foo, which corresponds to field definition Foo, which was above defined to be 10 elements (valid indexes range 0-9).
Byte sequences which are SPACE separated are in file-order, such as "76 A4" is equal to the two byte sequence "0xa476". File-order notation is always expressed in hexadecimal.
The marker "(confirmed)" indicates exhaustive testing and verification has been performed, and the marker "(unconfirmed)" indicates speculation or conjecture. A "(confirmed)" mark indicates that all quest file have been looked at, including the CompUSA ones. The absence of a marker does not necessarily imply speculation; sometimes documentation lags behind development considerably. The marker "(observed)" is synonymous with the marker "(known)", and indicates that while no other values have been found in the intrinsic and CompUSA quests, it seems plausible there are other valid values which the Daggerfall Quest Subsystem can handle, but merely have not been applied in the released, official quests.
Quest Filename Format[edit]
Individual quests are composed of two different files found in the ARENA2 Daggerfall directory, the QRC and the QBN file. Each set of QRC/QBN files appear to contain only one quest, which may have different parts. The name of these files affects several aspects of the quest such as which guild the quest belongs to etc... This format is followed by all quests except for ones which begin with $, _, or S. The almost complete filename format of quest files can be found below.
- [Byte 0]
- Represents which guild the quest belongs to. See Appendix B for a complete list of guild filenames.
- [Byte 1]
- Always a '0' (0x30)
- [Byte 2]
- The PC's status relative to guild.
A | Joining Guild Quest |
B | Member's Only Quest |
C | Non-Member's Quest |
- [Byte 3]
- The minimum reputation (in ASCII representation) with the quest giver required to receive the quest, or minimum player level for certain guilds. Values can range from '0' (0x30) to '9' (0x39) although it is recommended that you not exceed '7' (0x37) for the majority of quests. A value of '5' (0x35) would indicate the quest can only be received by characters with at least 50 reputation.
- [Byte 4]
- Usually '0' (0x30). If 'X' or 'Y' (0x58 or 0x59), the quest is unavailable with ChildGuard on.
- [Byte 5]
- Method in which the quest is given to the PC.
Y | Quest is Given in Person |
L | Quest is Given by a Letter |
- [Bytes 6 and 7]
- Any valid filename characters, usually a two-digit number.
The QRC Files[edit]
Each QRC files contains all the text data needed for that quest. The format of these files appears to be completely understood. The file contains variable amounts and variable lengths of data, but the file is nothing more than a Text Record Database; there are no other data elements. Since these files are simply text record databases, modification and enhancement have proved trivial. Because individual TextRecord structures support multiple TextSubrecord structures, a great variety of text can be associated with each type of message or log entry for the quest, which offers to the player considerably variety and diversity.
To briefly summarize, the file opens with a TextRecordDatabaseHeader (at the file's start, Byte 0) which implies how many TextRecord structures the file contains. Following this is a standard TextRecordHeaderList which indicates the TextRecordId and file position for each TextRecord within the file. The remainder of the file is dedicated to the TextRecord structures.
Well-Known TextRecordId Values[edit]
There are some well-known (and indeed appear to be Magic Numbers for the Daggerfall Engine) TextRecordId values. The "get quest" button in the UI, for example, depends on the presence of a TextRecord with an ID# of 0x03e8. While there are numerous OpCodes for displaying (and controlling the display) of any defined TextRecord, these appear to be "Magic Numbers"; they are required for proper function of the engine.
textRecordId (Hex, file byte order) | Text Type | |
---|---|---|
E8 | 03 | Text seen when 'Get Quest' is Pressed |
E9 | 03 | When you turn down the quest |
EA | 03 | When you accept the quest |
EB | 03 | 'Don't Bother Me' type questor message |
EC | 03 | Guild quest complete message |
ED | 03 | Information/Rumours? |
EE | 03 | Rumours? |
F0 | 03 | Information/Rumours? |
F1 | 03 | Quest fail message at guild |
F2 | 03 | Quest Log Entry |
F3 | 03 | Message when Quest item found on body? |
10 | 04 | Message when Quest item found on ground? |
15 | 04 | Failure message when Quest time expires |
These "well-known" values are not necessarily absolute. The "Quest Log Entry" row, for example, is not absolute but merely "most often used"; any TextRecord can be recorded to the player's log via a set of OpCodes depending on various states and events (record an entry when the player meets a specific NPC, or when he acquires a specific item, etc.). More research is necessary with the set of "Magic Numbers". As it stands, the only values of which we are certain are the 0x03e8-0x03eb, 0x03ed-0x03ee, and 0x0415; the remainder seem to be defined on a quest-by-quest basis.
Text Variables[edit]
There are certain sequences of text which the Daggerfall Quest Subsystem will regard as "variables". When a TextSubrecord is displayed, these "variables" are replaced by appropriate text. In essence, they behave like mark-up (wiki, html, sprintf(), etc).
- [%string]
- Any string preceded with a '%' indicates some sort of variable is to be inserted there. '%pcf' indicates the characters full name, '%pcn' only the first name, etc... A much more complete listing of these can be found in Appendix A at the end of this document.
- [=string_]
- [==string_]
- [===string_]
- [_string_]
- [__string_]
- [___string_]
- [____string_]
- Any string preceded by and followed with a '_' or '=' indicates some sort of location/item, probably a randomly generated one. This can be a house, dungeon, castle, fort, or some gold, a magic item, etc...
- A list of all known underscore-prefixed text variables (and their associated hashes) can be found in the Text Variable Hashes article.
- The number of _'s and ='s preceding the string determines how the location type is to be displayed. This is similar to "most specific" or "smallest size" through "most general" or "largest size". There is some overlap, and one would be wise to study and experiment with the quest file sources to determine the effect on a variable-by-variable basis.
Modifier | Interpretation | Item | NPC | Location | Timer | Monster |
---|---|---|---|---|---|---|
_string_ | The name of a person, house, or business. | (to do) | Name | (to do) | Duration in minutes | Class or creature (like Spriggan) |
__string_ | The name of a City. | (to do) | Location name | (to do) | — | — |
___string_ | The name of a Place (such as dungeon name or house where someone is) | (to do) | (to do) | (to do) | — | — |
____string_ | The name of a Province | (to do) | Province name | (to do) | — | — |
=string_ | Unknown | (to do) | Sprite description | (to do) | Duration in days | Name |
==string_ | Unknown | (to do) | Faction name | (to do) | — | — |
===string_ | Unknown | (to do) | — | (to do) | — | — |
That basically concludes the description of the QRC files, which appears to be very much complete and well understood. We still need, however, a better understanding of the %string variables, including a complete listing of them all (see Appendix A and Text Variable Hashes.
Using TextRecords[edit]
Since a TextRecord is a list of one or more TextSubrecord elements, and a textSubrecord follows a well-known format, some pseudo-code for reading/manipulating TextSubrecord entries is possible.
const SEP = 0xff;
public static String[] GetSubrecords( fileStream, length ) {
Char[] chars = (Char[])fileStream.ReadBytes( length );
totalRecord = new String( chars );
return totalRecord.Split( SEP );
}
The above exemplifies one means of translating a TextRecord into an array of strings (one for each TextSubrecord). Care should be taken when reading the file. If your language of choice offers 7-bit ASCII mode, the EOL, EOP, and SEP characters might not be read correctly (they each have their 8th-bit set TRUE) while reading files in this mode. If your language of choice offers utf-8 and unicode-16 "auto-detection", the file might not be read correctly at all since the unicode Byte-Order Mark (BOM), 0xfeff, is a valid sequence in a TextRecord. It is suggested when/if creating an editor one reads the data as bytes, and translates into system-characters as per:
public static String[] GetSubrecords( fileStream, length ) {
Byte[] bytes = fileStream.ReadBytes( length );
Char[] chars = new Char[ length ];
for ( System.Int32 i = 0; i < length; i++ ) {
chars[ i ] = (Char)bytes[ i ];
}
totalRecord = new String( chars );
return totalRecord.Split( SEP );
}
This example will eliminate most languages' attempts at "translating" 8-bit bytes into 16-bit unicode chars, thus preserving our 7-bit ASCII chars (the TextSubrecord text) and the 8-bit SEP (0xff) char.
Naturally getting a TextRecord is dependent on reading the TextRecordHeader entry from the file's TextRecordHeaderList. The following code would prove functional:
public struct TextRecordHeader {
UInt16 RecordId;
UInt32 Offset;
}
public static String[] GetSubrecords( fileStream, length, textRecordHeader ) {
Int32 oldPosition = fileStream.Position;
fileStream.Seek( textRecordHeader.Offset );
Byte[] bytes = fileStream.ReadBytes( length );
Char[] chars = new Char[ length ];
for ( System.Int32 i = 0; i < length; i++ ) {
chars[ i ] = (Char)bytes[ i ];
}
totalRecord = new String( chars );
fileStream.Seek( oldPosition );
return totalRecord.Split( SEP );
}
Of course the best solution would be to use a set of Regular Expressions. And that concludes our section on reading/writing the TextRecord file elements.
The QBN Files[edit]
The QBN quest files are the heart and soul of the quest. It is they which tell Daggerfall what kind of quest, quest rewards, location, monsters, items, etc... Unfortunately, discovering the complete format of the QBN file has proved difficult since this file is completely numeric Hex data. The method involves hacking a byte, running Daggerfall to get the quest, and then seeing what, if anything, has changed; definitely a very slow method but significant progress has been made.
QBN Header[edit]
Fortunately there is a regular format to these binary files. Each contains a header preamble, and then a series of "sections". Each section itself contains one or more records, and the format of these records is mostly understood.
- [Bytes 0-1] unsigned short (UInt16) QuestId
- Reserved, set to 0.
- [Bytes 2-3] unsigned short (UInt16) FactionId
- Reserved, set to 0.
- [Bytes 4-5] unsigned short (UInt16) ResourceId
- Reserved, set to 0.
- [Bytes 6-14] unsigned char[9] (UInt8[9]) ResourceFilename
- Reserved, set to 0.
- [Byte 15] unsigned char (UInt8) HasDebugInfo
- Non-zero value indicates the presence Text Variable Section, at the end of the file (confirmed). Usually 0.
- [Bytes 16-35] unsigned short[10] (UInt16[10]) SectionRecordCount[10]
- This is a list of ten ushorts (20 bytes total length), each indicating the count of records contained in the corresponding section.
- That is to say, sectionRecordCount[4] indicates how many records are contained in the fifth Section (remember: 0 is the root ordinal in counting, not 1).
- If sectionRecordCount[x] is 0x0000, then Section[x] is empty (no entries), and the corresponding sectionOffset[x] can be safely ignored.
- Valid value range for a sectionRecordCount[x] element is 0x0000-0x00fd, due to the way the various OpCodes interpret Section elements.
- [Bytes 36-57] unsigned short[11] (UInt16[11]) SectionOffset[11]
- This is a list of 11 ushorts (22 bytes total length), indicating the offsets (from the start of the file) to each Section.
- As with the SectionRecordCount list, this array is associative by index (the sectionOffset[5] reports the offset to the sixth Section).
- If sectionRecordCount[x] is 0, then sectionOffset[x] may be ignored.
- Only 7 QBN files contain a valid offset for the 11th section (sectionOffset[10]).
- [Bytes 58-59] unsigned short (UInt16) Null2
- Always 0x0000 (confirmed).
The indexes used in the above SectionOffset and SectionRecordCount arrays are always in the following order:
Ordinal Index | Section Type |
---|---|
0 | Items |
1 | Unimplemented |
2 | Unimplemented |
3 | Npcs |
4 | Locations |
5 | Unimplemented |
6 | Timers |
7 | Mobs |
8 | OpCodes |
9 | States |
10 | Text Variables (optional) |
Therefore, sectionRecordCount[6] will report the count of records in the Timers Section, and sectionOffset[3] will report the offset (from the start of the file) to the start of the Npcs Section.
The file up to this point is always the same in structure. What follows depends on the sectionRecordCount[x] and sectionOffset[x] values, because each Section is of variable length and some may even be empty (not all quests have an Items Section, for example).
QBN Section Descriptions[edit]
Each Section's record size varies, but the list below reports the size (in bytes) for each Record of each Section.
- Items Section
- 0x13 Bytes per Record
- Section 1
- 0x5e Bytes per Record
- Section 2
- 0x22 Bytes per Record
- NPCs Section
- 0x14 Bytes per Record
- Locations Section
- 0x18 Bytes per Record
- Section 5
- 0x10 Bytes per Record
- Timers Section
- 0x21 Bytes per Record
- Mobs Section
- 0x0e Bytes per Record
- OpCodes Section
- 0x57 Bytes per Record
- States Section
- 0x08 Bytes per Record
- Text Variables Section
- 0x1b Bytes per Record
Sections 1, 2, and 5 always have a sectionRecordCount of 0 (confirmed). The 11th section, Text Variables Section, is contained in only 7 QBN files. There is no associated sectionRecordCount for the Text Variables Section (a standard "C-Style" array) and that Section is always the last Section in the file (by sectionOffset) and continues to the end of the file (EOF), terminated by the usual null string element.
Items Section[edit]
The Items Section (starting at sectionOffset[0]) describes each of the items specific to the quest (deliverable inventory items such as "drugs", or the infamous "finger", etc). There are sectionRecordCount[0] records in this section, each 0x13 bytes long. Thus, the total length for the Items Section is ( 19 × sectionRecordCount[0] ) bytes long.
- [sectionOffset[0] - 0x13 Bytes Long per record]
-
- [Byte 0-1] signed short (Int16) ItemIndex
- This is the index number for the item. Some OpCodes will add or remove items from the character's inventory, or "drop" them at certain locations.
- Valid value range is 0x0000-00fd (0 through 253). (confirmed)
- [Byte 2] unsigned byte (UInt8) Reward
- This appears to be an enumeration, since the values below appear to be the only valid values.
- While this is related to the base type of reward (such as being paid X gold for successfully completing a quest), each item record must have a value even if it is not a reward, such as items the player must recover from a dungeon or deliver to another NPC.
-
-
Well-Known Reward values Byte 2 Interpreted as 00 Artifact Reward 01 Item Reward 02 Gold Reward 03 Unknown (Appears only in A0C01Y09)
-
-
- [Bytes 3-4] unsigned long (UInt16) ItemCategory
- This value defines the gross category of the item, such as Plant Ingredients, or Drugs.
- Valid values are a valid Daggerfall Item Category number ( 0x0000-0x001c ) and 0xffff.
- [Bytes 5-6] unsigned short (UInt16) ItemCategoryIndex
- Valid values are a valid Daggerfall Item Category Index number and 0xffff.
- This value specifies exactly which type of item within the above category, such as Aloe from the Plant Ingredients.
- There is no means to specify the material component for any items (if the item can be made from differing materials, such as Iron or Silver). The material of manufacture is determined randomly by the character's level.
- If itemCategory is 0xffff and itemCategoryIndex is 0x0000, then this is interpreted as "No Reward", such as the various "Kill the rats" Fighters' Guild quests.
- If itemCategory is 0xffff and itemCategoryIndex is 0xffff, then this is interpreted as a random reward. In this mode the reward field determines the type of item.
- reward is 0x02
- A positive random amount of gold.
- reward is 0x01
- A single item, selected at random.
- reward is 0x00
- A single artifact, selected at random.
- [Bytes 7-10] unsigned long (UInt32) TextVariableHash
- This is the hash value of the variable used in the QRC section.
- If the variable "_reward_" should refer to this item, one would supply the value of 0x00001b14.
- See the list of list of Text Variable Hashes for the list of known predefined variable hash values.
- [Bytes 11-14] unsigned long (UInt32) Null
- Always 0x00000000.
- [Bytes 15-16] unsigned short (UInt16) TextRecordId1
- A valid textRecordId from the associated quest's QRC file, or 0x0000.
- If 0x0000, then there is no TextRecord directly associated with this item (although it may still be referenced by the various "display text" OpCodes).
- If a valid textRecordId1, this message is displayed when the item is received.
- [Bytes 17-18] unsigned short (UInt16) TextRecordId2
- A valid textRecordId from the associated quest's QRC file, or 0x0000.
- If 0x0000, then there is no TextRecord directly associated with this item (although it may still be referenced by the various "display text" OpCodes).
- If a valid textRecordId1, this message is displayed when the item is "used" or "examined" from within the character's inventory screen. This is commonly used for letters, but could be used for anything (such as adding quest-flavour text to mundane quest items).
NPCs Section[edit]
This Section describes all the NPCs (flats) required by the quest, such as those met in Dungeons or Taverns or Temples and so-forth. Each of the sectionRecordCount[5] elements is 0x14 bytes long.
- [sectionOffset[3] - 0x14 Bytes per record]
-
- [Bytes 0-1] signed short (Int16) NpcIndexNumber (confirmed)
- This index number uniquely identifies the NPC within the quest.
- OpCodes which refer to an NPC will specify which NPC by npcIndexNumber, just as items are identified/accessed/associated by their index number.
- Valid value range is 0x0000-00fd (0 through 253). (confirmed)
- [Byte 2] unsigned char (UInt8) Gender
- Valid values may be 0x00ff, 0x01, 0x00, or 0x8c.
- If 0xff, the gender of the NPC will be randomly determined. (confirmed)
- If 0x01, the gender of the NPC will be Female. (confirmed)
- If 0x00, the gender of the NPC will be Male. (confirmed)
- Value 0x008c only appears in quest C0B10Y07, and is not understood. (unknown)
- [Byte 3] unsigned char (UInt8) FacePictureIndex
- This is the index number for the image of the NPC's face in dialog/escort.
- It is not completely understood from where the image will be retrieved (FACES.CIF, or TFAC00I0.RCI, or some other source).
- It is also not known if the data specified in FLATS.CFG and FACTION.TXT have an effect.
- [Bytes 4-5] unsigned short (UInt16) Unknown1
- Some purposes of this field are unknown, but observed values are indicative of an enumeration.
- Observed values
-
- 0x0000, 0x0003, 0x0009, 0x000a-0x000c, 0x000e-0x0012, 0x0014, 0x0015, 0xfff8-0xfffd, 0xffff
- (unknown)
- 0xfffe
- The Npc is a Daedra Prince, specified by the FactionIndex field.
- [Bytes 6-7] unsigned short (UInt16) FactionIndex (confirmed)
- A valid faction number (see FACTION.TXT) or 0x0000.
- A value of 0x0000 implies this NPC is not affiliated with any particular faction.
- A valid faction number indicates to which faction this NPC is loyal.
- Also corresponds to values found in the SAVEVARS.DAT in each save game directory.
- If unknown1 is 0xfffe, then factionIndex determines which Daedra Prince. (confirmed)
- unknown1 is 0xfffe and factionIndex is 0x0000
- The Npc is a random Prince.
- unknown1 is 0xfffe and factionIndex is a valid Daedra faction number
- The Npc is the Daedra specified by factionIndex
- [Bytes 8-11] unsigned long (UInt32) TextVariableHash (confirmed)
- This is the hash of the text variable to whom this NPC refers.
- Ex: If this NPC should be associated with the variable "_patsy_", then one would specify 0x00000d37
- See the list of list of Text Variable Hashes for the list of known predefined variable hash values.
- [Bytes 12-15] unsigned long (UInt32) Null
- Always 0x00000000 (confirmed).
- [Bytes 16-17] unsigned short (UInt16) TextRecordId1 (confirmed)
- A valid textRecordId from the quest's QRC file, or 0x0000.
- If a valid textRecordId, this Npc will have an entry in the "Tell Me About" conversation topic list.
- [Bytes 18-19] unsigned short (UInt16) TextRecordId2 (confirmed)
- A valid textRecordId from the quest's QRC file, or 0x0000.
- If a valid textRecordId, this Npc will have an entry in the "Tell Me About" conversation topic list.
Locations Section[edit]
This Section describes all the locations which are required for the quest. This includes everything from Taverns and Houses inside towns (same or different), to Dungeons and Shrines and Covens.
- [sectionOffset[4] - 0x18 Bytes per record]
-
- [Bytes 0-1] unsigned short (UInt16) LocationIndex (confirmed)
- This is the index number of the location, by which other sections of the file will refer to this Location.
- Valid value range is 0x00-fd (0 through 253). (confirmed)
- [Byte 2] unsigned char (UInt8) Flags
- Reserved, set to 0.
- [Byte 3] unsigned char (UInt8) GeneralLocation
- This field specifies the overland map locale for the Location record.
- Valid (observed) values are from 0x00 through 0x02.
- If 0x02, this location is outside of the current town (a Dungeon, or another Town).
- If 0x01, this location is inside the current town (such as found in the "Kill the monster in the house" quest).
- If the value is 0x00, then this location is a specific (not pseudo-random) Location (the storyline quests follow this pattern).
- [Bytes 4-5] unsigned short (UInt16) FineLocation
- This field is related to the previous, where this determines the Location within the town map.
- If the value is 0x0000, then the location is a building within a town.
- If the value is 0x0001, then the location is a Dungeon.
- If the GeneralLocation is 0, this is the higher 16 bits of the location's ObjectID.
- Only the storyline quests have values within the range 0xc3-ff. (confirmed)
- [Bytes 6-7] short (Int16) LocationType
- Building or dungeon type (-1 means random).
- If the GeneralLocation is 0, this is the lower 16 bits of the location's ObjectID.
- [Bytes 8-9] short (Int16) DoorSelector
- If the value is -1, use any suitable location.
- If the value is 0, consider only the doors having a 0x4000 flag set.
- If the value is 1, consider only the doors having a 0x1000 flag set.
- [Bytes 10-11] unsigned short (UInt16) Unknown2
- An unknown value.
- [Bytes 12-15] (long) TextVariableHash (confirmed)
- This is the hash of the text variable with which this location should be associated.
- Ex: If one wanted to associate the text variable "_mondun_" with this location, one would specify 0x00001ae8 for this value.
- See the Text Variable Hashes article for the known values.
- [Bytes 16-19] unsigned long (UInt32) ObjPtr
- Reserved, set to 0.
- [Bytes 20-21] unsigned short (UInt16) TextRecordId1 (confirmed)
- A valid textRecordId or 0x0000.
- If a valid textRecordId this location will be available in the "Tell Me About" conversation dialog. (confirmed)
- [Bytes 22-23] unsigned short (UInt16) TextRecordId2 (confirmed)
- A valid textRecordId or 0x0000.
- If a valid textRecordId, then this location will be available in the "Tell Me About" conversation dialog. (confirmed)
Timers Section[edit]
This Section deals with all the timers, intervals, and durations in the quest (such as "you have 22 days to return with the item"). This Section is also the least understood.
- [sectionOffset[6] - 33 Bytes per record]
-
- [Bytes 0-1] signed short (Int16) TimerIndex
- This is the index number by which other sections (particularly the OpCodes) may access the timer (starting/stopping it).
- Valid value range is 0x0000-00fd (0 through 253). (confirmed)
- [Bytes 2-3] unsigned short (UInt16) Flags
- Various flags controlling the timer arguments and its current state.
- [Byte 4] unsigned char (UInt8) Type
- Type of the timer. Possible values:
0 | Random duration |
---|---|
1 | Fixed duration |
2 | One location or NPC |
3 | Two locations/NPCs, only one dungeon (gives extra week in some circumstances) |
4 | Same as 2? |
5 | Two locations/NPCs, both are dungeons (gives up to 2 extra weeks in some circumstances) |
-
- [Bytes 5-8] signed long (Int32) Minimum
- Minimum time value in game minutes.
- [Bytes 9-12] signed long (Int32) Maximum
- Maximum time limit as above (or 0).
- [Bytes 13-16] unsigned long (UInt32) Started
- Reserved. Set to 0.
- [Bytes 17-20] unsigned long (UInt32) Duration
- Reserved. Set to 0.
- [Bytes 21-24] (long) (Int32) Link1
- The 1st NPC or a location Id associated with this timer
- [Bytes 25-28] (long) (Int32) Link2
- The 2nd NPC or a location associated with this timer
- [Bytes 29-32] unsigned long (UInt32) TextVariableHash
- The name of the state this timer controls.
- Ex: To associate the state "traveltime" with this timer, one would specify a value of 0x0001c1e3 for this field.
- See the Text Variable Hash article for full list of Text Variables and their associated hashes.
A great deal of research is still required to accurately use this Section when authoring quests, but this is the last remaining section of any great mystery.
Mobs Section[edit]
This section defines all the quest-specific mobs found in the quest. First, a "mob" is a "mobile", which comes from the MUD community, which basically means any of the computer controlled monsters. Secondarily, this doesn't impact the random monsters found in a dungeon (that is determined by the player's level); it only relates to quest-specific mobs (such as "assassinate the knight" or "kill the rats" quests).
- [sectionOffset[7] - 0x0e Bytes per record]
-
- [Byte 0] unsigned char (UInt8) MobIndex (confirmed)
- The value of this index is how other sections will refer to the mobile defined by this record, analogous to ItemIndex or NpcIndex.
- Valid value range is 0x00-fd (0 through 253). (confirmed)
- [Bytes 1-2] unsigned short (UInt16) Null1
- Always 0x0000 (confirmed)
- [Byte 3] unsigned char (UInt8) MobType (confirmed)
- This value specifies the type of mob (such as a rat or a monk).
- Valid values are 0x00-0x28, 0x80-0x8d, which are Daggerfall Mobile Type values.
- [Bytes 4-5] unsigned short (UInt16) MobCount (confirmed)
- The count of mobiles defined for this index.
- Valid value range is 0x01-06.
- [Bytes 6-9] unsigned long (UInt32) TextVariableHash (confirmed)
- This is the hash of the text variable with which this location should be associated.
- Ex: If one wanted to associate the text variable "_knight_" with this Mob record, one would specify 0x00001a68 for this value.
- See the Text Variable Hashes article for the known values.
- [Bytes 10-13] unsigned long (UInt32) Null2
- Always 0x00000000 (confirmed)
OpCodes Section[edit]
This Section actually defines a set of OpCodes which the Daggerfall Quest Subsystem executes in a manner very similar to a Virtual Machine. While this is not as advanced as the Java Virtual Machine, or the .Net Runtime Engine, it has proved to be quite extensible and fairly complete for all quest needs. In simplest terms, it would seem the Daggerfall Quest Subsystem creates an array of function pointers "under the hood", and use the OpCode as the index of the function to execute, passing a pointer to the record (each 0x57 bytes long) as an argument. There are several dozen understood OpCodes, all of which are used by at least one quest within the game's intrinsic quests or CompUSA quests.
- [sectionOffset[8] - 0x57 Bytes per record]
- See the special section for complete documentation on this Section. As the "brain" of the quest, it tells the game things like what Mobs to add, when to add log entries, so-on and so-forth.
States Section[edit]
State records describe state/flag variables used by the Daggerfall Quest Subsystem for tracking events throughout the quest. For example, slaying a Mob might trigger one state variable to be set TRUE, or meeting an NPC might reset another state variable to FALSE. Some OpCodes even offer rather complex if/else decision branching based on one or more state variable values. Many of the Dark Brotherhood and Vampire quests, for example, make use of around 20 different state variables to offer some of the richest and complex quests.
This entire Section has been confirmed.
- [sectionOffset[9] - 0x08 Bytes per record]
-
- [Bytes 0-1] signed short (Int16) FlagIndex
- This is the index number of the quest's state variable, and it is by this index number the OpCodes section may refer to a state.
- Valid value range is 0x0000-00fd (0 through 253). (confirmed)
- [Byte 2] unsigned byte (UInt8) IsGlobal
- This value indicates if this quest variable is accessible outside the current quest scope (from another quest).
- This value may be treated as a boolean (0 would be FALSE, and any other value TRUE).
- This is usually 0 except for storyline quests, where such permanency of state is necessary to offer a continuous story.
- [Byte 3] unsigned byte (UInt8) GlobalIndex
- If isGlobal is TRUE, this value is the index of the Global state variable to which this quest state variable should be linked.
- [Bytes 4-7] unsigned long (UInt32) TextVariableHash
- This is the hash of the text variable with which this state should be associated.
- Ex: If this NPC should be associated with the variable "_oneday_", then one would specify 0x00001ab3 for this field.
- See the list of list of Text Variable Hashes for the list of known predefined variable hash values.
Text Variables Section[edit]
This (optional) Section is only included in 7 files: 80C00Y00.QBN, A0C00Y04.QBN, A0C00Y06.QBN, N0C00Y01.QBN, O0B10Y00.QBN, P0B1XL08.QBN, and R0C40Y23.QBN. This Section is a list of several variable names. Since these define TextVariable objects, valid character range is 0x30-39 and 0x61-7a (TextVariables are always lower-case). If present, this Section always exists at the end of the QBN file and continues through to the file's end (EOF). A variable name beginning with the null-terminator token indicates no more strings follow.
- [sectionOffset[10] - 0x1b Bytes per record]
-
- [Bytes 0-19] 7-bit ASCII string TextVariable
- This is a standard "C-style" string, where the string is represented as a character array (char[ 20 ]). The end of the string is identified by the null-termination token (0x00).
- [Byte 20] unsigned char (UInt8) SectionId
- [Bytes 21-22] unsigned short (UInt16) RecordId
- The record which is labeled with this variable.
- [Bytes 23-26] unsigned int (UInt32) RecordPtr
- Reserved; set to 0.
- When a QBN file has a Text Variable Section, the table contains all TextVariables whose hash values are used in file's other Section records.
- See the Text Variable Hashes article for the algorithm to compute a TextVariableHash from a TextVariable.
Credits[edit]
I could not have done all this without much help from the many other Daggerfall addicts out there (list is in no particular order).
- Peggy Hanks - df4@juno.com
- For donating much new information for the quest file formats.
- Lord Phoenix - gozer@esoterica.pt
- For donating information on the files formats as well.
- Michael Schneider - michael@cybermagician.com
- Maintains a Quest Hacking guide on Daggerweb and has also created a quest editor for Windows, and of course has provided much new information.
- Glen Wright - glen@skatter.usask.ca
- Provided some more very useful information on the structure of the QBN files.
- Skeleton Man
- Has the most experience in actually editing and creating quests.
- Thomas Hautesserres - hautesse@cert.fr
- Author of the fabulous WinQEdit which has helped deciphering QBN format a great deal.
If I've forgotten anyone I can assure you it's not on purpose. Just e-mail me and we'll clear things up in a jiffy (anyone know how long that is perchance?). If you'd like to help out in some way, feel free to make it known to me as I'm sure I could find something you could do. Just e-mail me at aj589@freenet.carleton.ca. I'll take suggestions for the quest editor, comments on this document, and of course any data on the quest formats.
Quest Related Programs[edit]
Many useful files can be found in the Daggerfall Files article. At the moment there are only two Daggerfall quest editors, but they are all you really need.
- WINQEdit
- A great QBN editor for windows. Requires knowledge of hex-editing and the quest format. Can be downloaded here.
- DMPQuest
- A great QRC editor for windows. Easy to use since it does not require any hex-editing. Can be downloaded here.
Appendixes[edit]
Appendix A[edit]
Listing of %string Variables==
Variable | Description | Variable | Description | Variable | Description | ||
---|---|---|---|---|---|---|---|
%1am | 1st + Magnitude | %fx2 | Another faction in news | %olf | What happened to %ol1 | ||
%1bm | 1st base Magnitude | %g | He/She etc... | %oth | An oath (listed in TEXT.RSC) | ||
%1com | Greeting (?) | %g1 | He/She ??? | %pcf | Character's first name | ||
%1hn | ? | %g2 | Him/Her etc... | %pcn | Character's full name | ||
%2am | 2nd + Magnitude | %g2self | Himself/Herself etc... | %pct | Character's title in guild | ||
%2bm | 2nd Base Magnitude | %g3 | His/Hers/Theirs etc... | %pdg | Days in jail | ||
%2com | ? | %gii | Amount of gold in hand | %pen | Prison sentence | ||
%2hn | ? | %god | Some god (listed in TEXT.RSC) | %per | Amount of Personality | ||
%3hn | ? | %gtp | Fine | %plq | Place of something in log. | ||
%a | Cost of something. | %hea | HP Modifier | %pnq | Person of something in log | ||
%ach | + Chance per | %hmd | Healing rate modifier | %pqn | Potential Quest Giver | ||
%adr | + Duration per | %hnr | Your title? | %pqp | Potential Quest Giver's Location | ||
%agi | Amount of Agility | %hnt | Direction of location. | %ptm | An enemy of the current region (?) | ||
%ark | Considered for stat | %hol | Holiday | %q1 to %q5 | Effects of questions answered in bio. | ||
%arm | Armor | %hrg | ? | %qdt | Quest date in log | ||
%ba | Book Author | %hs | Holding Soul type | %qot | The log comment | ||
%bch | Base chance | %htwn | ? | %qua | Condition | ||
%bdr | Base Duration | %int | Amount of Intelligence | %r1 | Commoners rep | ||
%bt | Book title | %it | Item | %r2 | Merchants rep | ||
%clc | Per level (Chance) | %jok | A joke | %r3 | Scholars rep | ||
%cld | Per level (Duration) | %key | A location (?) | %r4 | Nobilities rep | ||
%clm | Per level (Magnitude) | %key2 | Another location | %r5 | Underworld rep | ||
%cn | Current City | %kg | Weight of items | %ra | Player's race | ||
%cpn | Current shop name | %kno | Appears to be a guild name | %reg | Region | ||
%cri | Accused crime | %lev | Rank in guild that you are in. | %rn | Regent's Name | ||
%crn | Current Region | %ln | Random lastname | %rt | Regent's Title | ||
%dae | A daedra | %loc | Location marked on map { | %spc | Current Spell Points}; | ||
%dam | Damage modifier | %lt1 | Title of %fl1 | %scn | Total Spell Points | ||
%dat | Date | %ltn | In the eyes of the law you are....... | %ski | Skill | ||
%di | Direction | %luc | Luck | %spd | Speed | ||
%dip | ? | %mad | Resistance | %str | Amount of strength | ||
%dng | Dungeon | %map | A location on the main map { | %t | Regent's Title}; | ||
%dts | Daedra | %mat | Material | %tcn | Travel to city | ||
%enc | Encumbrance | %mit | Item | %thd | Combat odds | ||
%end | Amount of Endurance | %mn | Random First(?) name (Male?) | %tim | Time | ||
%fcn | Another city | %mn2 | Same as %mn (?) | %vam | Vampire Name | ||
%fl1 | Lord of %fx1 | %mod | Modification | %vcn | Vampire's Clan | ||
%fl2 | Lord of %fx2 | %mpw | Magic powers | %wdm | Weapon damage | ||
%fn | Random first(?) name (Female?) | %n | A random person/Person you are talking to. | %wep | Weapon | ||
%fn2 | Same as %mn2 (?) | %nam | Came up empty for me | %wil | Amount of Willpower | ||
%fon | Name of Knightly Order/Guild/Temple (?) | %nrn | Noble of the current region | %wpn | Poison (?) | ||
%fx1 | A faction in news | %ol1 | Old lord of %fx1 | %wth | Worth |
Many thanks to James Dean (james@rygannon.force9.co.uk) for donating many of the string variables and descriptions.
Appendix B[edit]
Listing of Guild Quest Filenames
Below is a complete list of all types of quest file names contributed by several people. The character under the Filename heading represents the first character of a QRC or QBN file.
Filename | Guild / Group | Filename | Guild / Group | |
---|---|---|---|---|
_ | Starting quests (Tutor and Initial) | H | The Benevolence of Mara | |
$ | Vampire/Lycanthropy Cure Quests | I | The Temple of Stendarr | |
0 | School of Julianos | J | The Resolution of Zenithar | |
1 | Meridia | K | Merchant Quests | |
2 | Molag Bal | L | Dark Brotherhood | |
3 | Namira | M | Fighters Guild | |
4 | Nocturnal | N | Mage Guild | |
5 | Peryite | O | Thieves Guild | |
6 | Sheogorath | P | Vampire's Quests | |
7 | Sanguine | Q | Coven Quests | |
8 | Malacath | R | Royalty Quests | |
9 | Vaermina | S | Main Quests | |
A | Commoner's Quests ??? | T | Lady Azura, Crimson Gate (Daedra Lady) | |
B | Knightly Order | U | Prince Boethiah's Quest (Daedra Prince) | |
C | Temple quests | V | Clavicus Vile's Quest (Daedra Lord) | |
D | The Akatosh Chantry | W | Hermaeus Mora's quest (Daedra Lord) | |
E | The Temple of Arkay | X | Hircine's Quest (Daedra Lord) | |
F | The House of Dibella | Y | Mehrune Dagon's Quest (Daedra Lord) | |
G | The Kynaran Order | Z | Mephala's Quest (Daedra Lord) |