├── .gitignore
├── README.md
├── specs
├── bioware
│ ├── 2DA_Format.pdf
│ ├── AreaFile_Format.pdf
│ ├── CommonGFFStructs.pdf
│ ├── Conversation_Format.pdf
│ ├── Creature_Format.pdf
│ ├── DoorPlaceableGFF.pdf
│ ├── ERF_Format.pdf
│ ├── Encounter_Format.pdf
│ ├── Faction_Format.pdf
│ ├── GFF_Format.pdf
│ ├── IFO_Format.pdf
│ ├── Item_Format.pdf
│ ├── Journal_Format.pdf
│ ├── KeyBIF_Format.pdf
│ ├── LocalizedStrings_Format.pdf
│ ├── PaletteITP_Format.pdf
│ ├── README
│ ├── SSF_Format.pdf
│ ├── SoundObject_Format.pdf
│ ├── Store_Format.pdf
│ ├── TalkTable_Format.pdf
│ ├── Trigger_Format.pdf
│ └── Waypoint_Format.pdf
├── foxpro
│ ├── README.md
│ ├── cdx.pdf
│ ├── dbf.pdf
│ ├── fpt.pdf
│ ├── idx.pdf
│ ├── idx2.pdf
│ ├── macro.pdf
│ └── table.pdf
├── gmax_nwn_mdl_0.3b.ms
├── kotor_mdl.html
├── nds
│ ├── articulation.htm
│ └── sdat.html
├── torlack
│ ├── README
│ ├── basics.html
│ ├── bif.html
│ ├── binmdl.html
│ ├── itp.html
│ ├── key.html
│ ├── mod.html
│ ├── ncs.html
│ └── plt.html
└── trn
│ ├── trn.aswm.md
│ ├── trn.aswm.path_tables.svg
│ ├── trn.md
│ └── trn_tanita.pdf
└── templates
├── COPYING
├── JadeMDL.bt
├── NWN1MDL.bt
├── NWN2GR2.bt
└── NWN2MDB.bt
/.gitignore:
--------------------------------------------------------------------------------
1 | # Editor temp files
2 | .sw[po]
3 | .*.sw[po]
4 | *~
5 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | xoreos-docs README
2 | ==================
3 |
4 | A small repository containing documentation to help with the reverse-
5 | engineering of BioWare's Aurora engine games. xoreos-docs is part of the
6 | xoreos project; please see the [xoreos website](https://xoreos.org/) and
7 | its [GitHub repositories](https://github.com/xoreos) for details.
8 |
9 | specs
10 | -----
11 |
12 | At the moment, this directory contains file format specifications, both
13 | official and as figured out by the modding community.
14 |
15 | * bioware: The official BioWare NWN docs, from the now defunct
16 | nwn.bioware.com site
17 | * torlack: Tim Smith (Torlack)'s notes of several formats, from his
18 | now defunct website
19 | * trn: Specs for the NWN2 terrain and walkmesh format
20 | * foxpro: Specs for the FoxPro database format
21 | * gmax\_nwn\_mdl\_0.3b.ms: GMax import script for NWN models, by
22 | Wayland Reid
23 | * kotor\_mdl.html: Partial KotOR model specs
24 | * nds\_sdat.html: Partial Nintendo DS Nitro Composer File (\*.sdat)
25 | specs, by kiwi.ds
26 |
27 | templates
28 | ---------
29 |
30 | This directory contains binary template files for [010
31 | Editor](http://www.sweetscape.com/010editor/). They describe the
32 | structure of binary files.
33 |
34 | * JadeMDL.bt: Jade Empire's MDL model file
35 | * NWN1MDL.bt: Neverwinter Nights' MDL model file
36 | * NWN2MDB.bt: Neverwinter Nights 2's MDB/TRN/TRX model file
37 | * NWN2GR2.bt: Neverwinter Nights 2's Granny2 animation file
38 |
39 | These template files are released under the terms of the CC0 1.0 Universal
40 | Public Domain Dedication. For details, see the file COPYING in the templates
41 | directory, or
This spec is far from completion. And it may contains error. Use it at your own risk. 8 |
23 June 2007 SBNK update 9 |
20 June 2007 SSEQ Events 10 |
6 June 2007 SBNK + general update 11 |
23 May 2007 First published 12 |
For enquiries please contact me at "kiwi.ds AT gmail.com" 13 | 14 |
Crystal - the author of CrystalTile2.exe
17 |loveemu - the author of sseq2mid.exe, swave2wave.exe & strm2wave.exe
18 |Nintendon - the author of ndssndext.exe
19 |DJ Bouche - the author of sdattool.exe
20 |VGMTrans - the author of VGMTrans.exe
21 | 22 | 23 | 24 |
25 | Tables of Contents26 | |
0. Introduction 28 |
1. SDAT File Format 33 |
52 | 0. Introduction53 | |
"The DS SDK has all the tools in it to convert MIDI files to the DS format, and has text file templates to define the soundbanks." CptPiard from VGMix
56 | 57 |The Nitro Composer packs various types of sound files in a single file (*.sdat) for use in DS games. Not all games involve the Nitro Composer. But it seems that it is very popular for creation of DS game music.
58 | 59 |Inside the SDAT you will find: SSEQ (Sequence), SSAR (Sequence Archive), SBNK (Sound Bank), SWAR (Wave Archive), STRM (Stream).
60 | 61 |SSAR is a collection of SSEQ, while SWAR is a collection of SWAV.
62 | 63 |67 | s8 1 byte // signed char 68 | u8 1 byte // unsigned char 69 | s16 2 byte // signed short 70 | u16 2 byte // unsigned short 71 | s32 4 byte // signed long 72 | u32 4 byte // unsigned long 73 |74 | 75 |
79 | typedef struct tagNdsStdFile { 80 | s8 type[4]; // i.e. 'SDAT' or 'SBNK' etc... 81 | u32 magic; // 0x0100feff or 0x0100fffe 82 | u32 nFileSize; // Size of this file ( include this structure ) 83 | u16 nSize; // Size of this structure ( always 16 ) 84 | u16 nBlock; // Number of Blocks 85 | } NDSSTDF; 86 |87 | 88 |
The magic value can be 0x0002feff or 0x0001feff for non sound-related files.
89 | 90 | 91 |
92 | 1. SDAT File Format93 | |
98 | -------------------------------- 99 | | Header | 100 | -------------------------------- 101 | | Symbol Block | 102 | -------------------------------- 103 | | Info Block | 104 | -------------------------------- 105 | | File Allocation Table (FAT) | 106 | -------------------------------- 107 | | File Block | 108 | -------------------------------- 109 |110 | 111 |
115 | typedef struct tagSDATHeader 116 | { 117 | struct tagNdsStdFile { 118 | s8 type[4]; // 'SDAT' 119 | u32 magic; // 0x0100feff 120 | u32 nFileSize; 121 | u16 nSize; 122 | u16 nBlock; // usually 4, but some have 3 only ( Symbol Block omitted ) 123 | } file; 124 | u32 nSymbOffset; // offset of Symbol Block = 0x40 125 | u32 nSymbSize; // size of Symbol Block 126 | u32 nInfoOffset; // offset of Info Block 127 | u32 nInfoSize; // size of Info Block 128 | u32 nFatOffset; // offset of FAT 129 | u32 nFatSize; // size of FAT 130 | u32 nFileOffset; // offset of File Block 131 | u32 nFileSize; // size of File Block 132 | u8 reserved[16]; // unused, 0s 133 | } SDATHEADER; 134 |135 | 136 |
142 | typedef struct tagSDATSymbol 143 | { 144 | char type[4]; // 'SYMB' 145 | u32 nSize; // size of this Symbol Block 146 | u32 nRecOffset[8]; // offset of Records (note below) 147 | u8 reserved[24]; // unused, 0s 148 | } SDATSYMB; 149 |150 | 151 | 152 | 153 |
Record No. | Record Name | Description | 159 |
0 | SEQ | Sequence (for music) | 161 |
1 | SEQARC | Sequence Archive (for sound effect) | 162 |
2 | BANK | Sound Bank | 163 |
3 | WAVEARC | Wave Archive | 164 |
4 | PLAYER* | Player (Group-related) | 165 |
5 | GROUP | Group of SEQ/SEQARC/BANK/WAVEARC | 166 |
6 | PLAYER2* | Player2 (Stream-related) | 167 |
7 | STRM | Stream | 168 |
* Records 4 and 5 do not appear in SMAP file. A SMAP File is generated by the Nitro Composer listing all sound files in the SDAT file. An example can be found from <<Zoids Saga DS - Legend of Arcadia>> |
174 | typedef struct tagSDATSymbolRec 175 | { 176 | u32 nCount; // No of entries in this record 177 | u32 nEntryOffset[1]; // Array of offsets of each entry 178 | } SDATSYMBREC; 179 |180 | 181 | For Record 1 (SEQARC), it is a group which contains sub-records. The sub-record is of the same structure as SDATSYMBREC (above). Record 1 has the following structure: 182 | 183 |
184 | typedef struct tagSDATSymbolRec2 185 | { 186 | u32 nCount; // No of entries in this record 187 | struct { 188 | u32 nEntryOffset; // Offset of this Group's symbol 189 | u32 nSubRecOffset; // Offset of the sub-record 190 | } Group[1]; // Array of offsets of each entry 191 | } SDATSYMBREC2; 192 |193 | 194 | Below is an example to access these records: 195 | 196 |
197 | SDATSYMB *symb; 198 | int i, j; 199 | char *szSymbol; 200 | ... 201 | // access record 0 'SSEQ' 202 | SDATSYMBREC *symb_rec = (SDATSYMBREC *) ( (u8 *)symb + symb->RecOffset[0] ); 203 | 204 | for ( i = 0; i < symb_rec->nCount; i++ ) 205 | { 206 | // print out the symbol 207 | szSymbol = (char *) ( (u8 *)symb + symb_rec->nEntryOffset[i] ); 208 | printf( "%s\n", szSymbol ); 209 | } 210 | ... 211 | 212 | // access record 1 'SSAR' 213 | SDATSYMBREC2 symb_rec2 = (SDATSYMBREC *)( (u8 *)symb + symb->RecOffset[1] ); 214 | 215 | for ( i = 0; i < symb_rec2->nCount; i++ ) 216 | { 217 | szSymbol = (char *) ( (u8 *)symb + symb_rec2->Group[ i ].nEntryOffset ); 218 | printf( "%s\n", szSymbol ); 219 | 220 | SDATSYMBREC *symb_subrec = (SDATSYMBREC *) ( (u8 *)symb + symb_rec2->Group[i].nSubRecOffset ); 221 | for ( j = 0; j < symb_subrec->nCount; j++ ) 222 | { 223 | // print out sub record's symbols 224 | szSymbol = (char *) ( (u8 *)symb + symb_subrec->nEntryOffset[i] ); 225 | printf( "%s\n", szSymbol ); 226 | } 227 | } 228 |229 | 230 |
240 | typedef struct tagSDATInfo 241 | { 242 | char type[4]; // 'INFO' 243 | u32 nSize; // size of this Info Block 244 | u32 nRecOffset[8]; // offset of a Record 245 | u8 reserved[24]; // unused, 0s 246 | } SDATINFO; 247 |248 | 249 |
253 | typedef struct tagSDATInfoRec 254 | { 255 | u32 nCount; // No of entries in this record 256 | u32 nEntryOffset[1]; // array of offsets of each entry 257 | } SDATINFOREC; 258 |259 | 260 | 261 |
Record 0 "SEQ" - The Info Entry for SEQ contains playback information.
264 |265 | typedef struct tagSDATInfoSseq 266 | { 267 | u16 fileID; // for accessing this file 268 | u16 unknown; 269 | u16 bnk; // Associated BANK 270 | u8 vol; // Volume 271 | u8 cpr; 272 | u8 ppr; 273 | u8 ply; 274 | u8 unknown[2]; 275 | } SDATINFOSSEQ; 276 |277 | 278 |
282 | typedef struct tagSDATInfoSsar 283 | { 284 | u16 fileID; 285 | u16 unknown; 286 | } SDATINFOSSAR; 287 |288 | 289 | Remarks: no info is available for SEQARC files. The info of each archived SEQ is stored in that SEQARC file. 290 | 291 |
295 | typdef struct tagSDATInfoBank 296 | { 297 | u16 fileID; 298 | u16 unknown; 299 | u16 wa[4]; // Associated WAVEARC. 0xffff if not in use 300 | } 301 |302 | 303 | Remarks: Each bank can links to up to 4 WAVEARC files. The wa[4] stores the WAVEARC entry number. 304 | 305 |
309 | typedef struct tagSDATInfoSwar 310 | { 311 | u16 fileID; 312 | u16 unknown; 313 | } SDATINFOSwar; 314 |315 | 316 | Remarks: This is not a new structure. It is the same as SDATINFOSSAR above for Record 1. 317 | 318 |
322 | typedef struct tagSDATInfoPlayer 323 | { 324 | u8 unknown; 325 | u8 padding[3]; 326 | u32 unknown2; 327 | } SDATINFOPlayer; 328 |329 | 330 | Remarks: None 331 | 332 |
336 | typedef struct tagSDATInfoPlayer 337 | { 338 | u32 nCount; // number of sub-records 339 | struct { // array of Group 340 | u32 type; 341 | u32 nEntry; 342 | } Group[1]; 343 | } SDATINFOPlayer; 344 |345 | 346 |
Remarks: SDATINFOPlayer::Group::type can be one of the following values. nEntry is the entry number in the relevant Record (SEQ/SEQARC/BANK/WAVEARC).
347 | 348 |Value | Type | 351 |
0x0700 | SEQ |
0x0803 | SEQARC |
0x0601 | BANK |
0x0402 | WAVEARC |
363 | typedef struct SDATInfoPlayer2 364 | { 365 | u8 nCount; 366 | u8 v[16]; // 0xff if not in use 367 | u8 reserved[7]; // padding, 0s 368 | } SDATINFOPLAYER2; 369 |370 | 371 | Remarks: The use is unknown. The first byte states how many of the v[16] is used (non 0xff). 372 | 373 |
377 | typedef struct SDATInfoStrm 378 | { 379 | u16 fileID; // for accessing the file 380 | u16 unknown; 381 | u8 vol; // volume 382 | u8 pri; 383 | u8 ply; 384 | u8 reserved[5]; 385 | } SDATINFOSTRM; 386 |387 | 388 | Remarks: 'ply' means play?, 'pri' means priority? 389 | 390 |
394 | typedef struct tagSDATFAT 395 | { 396 | char type[4]; // 'FAT ' 397 | u32 nSize; // size of the FAT 398 | u32 nCount; // Number of FAT records 399 | SDATFATREC Rec[1]; // Arrays of FAT records 400 | } SDATFAT; 401 |402 | 403 |
407 | typedef struct tagSDATFATREC 408 | { 409 | u32 nOffset; // offset of the sound file 410 | u32 nSize; // size of the Sound file 411 | u32 reserved[2]; // always 0s, for storing data in runtime. 412 | } SDATFATREC; 413 |414 | 415 |
419 | typedef struct tagSDATFILE 420 | { 421 | char type[4]; // 'FILE' 422 | u32 nSize; // size of this block 423 | u32 nCount; // Mumber of sound files 424 | u32 reserved; // always 0 425 | } SDATFILE; 426 |427 | 428 | 429 | 430 | 431 | 432 |
433 | 2. SSEQ File Format434 | |
SSEQ stands for "Sound Sequence". It is a converted MIDI sequence. Linked to a BANK for instruments.
437 | 438 | 439 |440 | typedef struct tagSseq 441 | { 442 | struct tagNdsStdFile { 443 | char type[4]; // 'SSEQ' 444 | u32 magic; // 0x0100feff 445 | u32 nFileSize; // Size of this SSEQ file 446 | u16 nSize; // Size of this structure = 16 447 | u16 nBlock; // Number of Blocks = 1 448 | } file; 449 | struct { 450 | char type[4]; // 'DATA' 451 | u32 nSize; // Size of this structure = nFileSize - 16 452 | u32 nDataOffset; // Offset of the sequence data = 0x1c 453 | u8 data[1]; // Arrays of sequence data 454 | } data; 455 | } SSEQ; 456 |457 | 458 |
NB. For the details of the SSEQ file, please refer to loveemu's sseq2mid
459 | 460 |The design of SSEQ is more programming-oriented while MIDI is hardware-oriented. In MIDI, to produce a sound, a Note-On event is sent to the midi-instrument and then after a certain time, a Note-Off is sent to stop the sound (though it is also acceptable to send a Note-On message with 0 velocity). 462 | In SSEQ, a sound is produced by one event only which carries with data such as note, velocity and duration. So the SSEQ-sequencer knows exactly what and how to play and when to stop.
463 | 464 |A SSEQ can have at maximum 16 tracks, notes in the range of 0..127 (middle C is 60). Each quartet note has a fixed tick length of 48. Tempo in the range of 1..240 BPM (Default is 120). The SSEQ will not be played correctly if tempo higher than 240.
465 |The SEQ player uses Arm7's Timer1 for timing. The Arm7's 4 Timers runs at 33MHz (approximately 2^25). The SEQ player sets Timer1 reload value to 2728, prescaler to F/64. So on about every 0.0052 sec (64 * 2728 / 33MHz) the SEQ Player will be notified ( 1 cycle ). As a quartet note has fixed tick value of 48, the highest tempo that SEQ Player can handle is 240 BPM ( 60 / (0.0052 * 48) ).
466 |During each cycle, the SEQ player adds the tempo value to a variable. Then it checks if the value exceeds 240. If it does, the SEQ player subtracts 240 from the variable, and process the SSEQ file. Using this method, the playback is not very precise but the difference is too small to be noticed.
467 |Take an example with tempo = 160 BPM, the SSEQ file is processed twice in 3 notifications.
468 |cycle | variable | action |
1 | 0 | Add 160 |
2 | 160 | Add 160 |
3 | 320 | Subtract 240, process once, add 160 |
4 | 240 | Subtract 240, process once, add 160 |
5 | 160 | Add 160 |
6 | 320 | Subtract 240, process once, add 160 |
7 | 240 | Subtract 240, process once, add 160 |
8 | 160 | Add 160 |
Status Byte | 485 |Parameter | 486 |Description | 487 |
0xFE | 2 bytes It indicates which tracks are used. Bit 0 for track 0, ... Bit 15 for track 15. If the bit is set, the corresponding track is used. | Indication begin of multitrack. Must be in the beginning of the first track to work. A series of event 0x93 follows. | 490 |
0x93 | 4 bytes 1st byte is track number [0..15] The other 3 bytes are the relative adress of track data. Add nDataOffset (usually 0x1C) to find out the absolute address. | SSEQ is similar to MIDI in that track data are stored one after one track. Unlike mod music. | 493 |
0x00 .. 0x7f | Velocity: 1 byte [0..127] Duration: Variable Length | NOTE-ON. Duration is expressed in tick. 48 for quartet note. Usually it is NOT a multiple of 3. | 496 |
0x80 | Duration: Variable Length | REST. It tells the SSEQ-sequencer to wait for a certain tick. Usually it is a multiple of 3. | 499 |
0x81 | Bank & Program Number: Variable Length | bits[0..7] is the program number, bits[8..14] is the bank number. Bank change is seldomly found, so usually bank 0 is used. | 502 |
0x94 | Destination Address: 3 bytes (Add nDataOffset (usually 0x1C) to find out the absolute address.) | JUMP. A jump must be backward. So that the song will loop forever. 505 | |
0x95 | Call Address: 3 bytes (Add nDataOffset (usually 0x1C) to find out the absolute address.) | CALL. It's like a function call. The SSEQ-sequncer jumps to the address and starts playing at there, until it sees a RETURN event. | 508 |
0xFD | NONE | RETURN. The SSEQ will return to the caller's address + 4 (a Call event is 4 bytes in size). | 511 |
0xA0 .. 0xBf | See loveemu's sseq2mid for more details. | Some arithmetic operations / comparions. Affect how SSEQ is to be played. | 514 |
0xC0 | Pan Value: 1 byte [0..127], middle is 64 | PAN | 517 |
0xC1 | Volume Value: 1 byte [0..127] | VOLUME | 520 |
0xC2 | Master Volume Value: 1 byte [0..127] | MASTER VOLUME | 523 |
0xC3 | Value: 1 byte [0..64] (Add 64 to make it a MIDI value) | TRANSPOSE (Channel Coarse Tuning) | 526 |
0xC4 | Value: 1 byte | PITCH BEND | 529 |
0xC5 | Value: 1 byte | PITCH BEND RANGE | 532 |
0xC6 | Value: 1 byte | TRACK PRIORITY | 535 |
0xC7 | Value: 1 byte [0: Poly, 1: Mono] | MONO/POLY | 538 |
0xC8 | Value: 1 byte [0: Off, 1: On] | TIE (unknown) | 541 |
0xC9 | Value: 1 byte | PORTAMENTO CONTROL | 544 |
0xCA | Value: 1 byte [0: Off, 1: On] | MODULATION DEPTH | 547 |
0xCB | Value: 1 byte | MODULATION SPEED | 550 |
0xCC | Value: 1 byte [0: Pitch, 1: Volume, 2: Pan] | MODULATION TYPE | 553 |
0xCD | Value: 1 byte | MODULATION RANGE | 556 |
0xCE | Value: 1 byte | PORTAMENTO ON/OFF | 559 |
0xCF | Time: 1 byte | PORTAMENTO TIME | 562 |
0xD0 | Value: 1 byte | ATTACK RATE | 565 |
0xD1 | Value: 1 byte | DECAY RATE | 568 |
0xD2 | Value: 1 byte | SUSTAIN RATE | 571 |
0xD3 | Value: 1 byte | RELEASE RATE | 574 |
0xD4 | Count: 1 byte (how many times to be looped) | LOOP START MARKER | 577 |
0xFC | NONE | LOOP END MARKER | 580 |
0xD5 | Value: 1 byte | EXPRESSION | 583 |
0xD6 | Value: 1 byte | PRINT VARIABLE (unknown) | 586 |
0xE0 | Value: 2 byte | MODULATION DELAY | 589 |
0xE1 | BPM: 2 byte | TEMPO | 592 |
0xE3 | Value: 2 byte | SWEEP PITCH | 595 |
0xFF | NONE | EOT: End Of Track | 598 |
606 | 3. SSAR File Format607 | |
614 | typedef struct tagSsarRec { 615 | u32 nOffset; // relative offset of the archived SEQ file, absolute offset = nOffset + SSAR::nDataOffset 616 | u16 bnk; // bank 617 | u8 vol; // volume 618 | u8 cpr; // channel pressure 619 | u8 ppr; // polyphonic pressure 620 | u8 ply; // play 621 | u8 reserved[2]; 622 | } SSARREC; 623 | 624 | typedef struct tagSsar 625 | { 626 | struct tagNdsStdFile { 627 | char type[4]; // 'SSAR' 628 | u32 magic; // 0x0100feff 629 | u32 nFileSize; // Size of this SSAR file 630 | u16 nSize; // Size of this structure = 16 631 | u16 nBlock; // Number of Blocks = 1 632 | } file; 633 | struct { 634 | char type[4]; // 'DATA' 635 | u32 nSize; // Size of this structure 636 | u32 nDataOffset; // Offset of data 637 | u32 nCount; // nCount * 12 + 32 = nDataOffset 638 | SSARREC Rec[1]; // nCount of SSARREC 639 | } data; 640 | } SSAR; 641 |642 | 643 |
NB. Archived SSEQ files are not stored in sequence (order). So Rec[0].nOffset may point to 0x100 but Rec[1].nOffset points to 0x40.
644 |NB. Archived SSEQ files cannot be readily extracted from SSAR file because data in one SSEQ may 'call' data in other SSEQ.
645 | 646 | 647 | 648 | 649 |
650 | 4. SBNK File Format651 | |
SBNK stands for "Sound Bank". A bank is linked to up to 4 SWAR files which contain the samples. It define the instruments by which a SSEQ sequence can use. You may imagine SSEQ + SBNK + SWAR are similar to module music created by trackers.
654 | 655 |656 | typedef struct tagSbnkInstrument 657 | { 658 | u8 fRecord; // can be either 0, 1..4, 16 or 17 659 | u16 nOffset; // absolute offset of the data in file 660 | u8 reserved; // must be zero 661 | } SBNKINS; 662 | 663 | typedef struct tagSbnk 664 | { 665 | struct tagNdsStdFile { 666 | char type[4]; // 'SBNK' 667 | u32 magic; // 0x0100feff 668 | u32 nFileSize; // Size of this SBNK file 669 | u16 nSize; // Size of this structure = 16 670 | u16 nBlock; // Number of Blocks = 1 671 | } file; 672 | struct { 673 | char type[4]; // 'DATA' 674 | u32 nSize; // Size of this structure 675 | u32 reserved[8]; // reserved 0s, for use in runtime 676 | u32 nCount; // number of instrument 677 | SBNKINS Ins[1]; 678 | } data; 679 | } SBNK; 680 |681 | 682 |
So, after SBNK::data, there come SBNK::data::nCount of SBNKINS. After the last SBNKINS, there will be SBNK::data::nCount of instrument records. In each instrument records, we can find one or more wave/note definitions. 683 | 684 | 685 | 686 | 687 |
If SBNKINS::fRecord = 0, it is empty. SBNKINS::nOffset will also = 0.
690 |If SBNKINS::fRecord < 16, the record is a note/wave definition. I have seen values 1, 2 and 3. But it seems the value does not affect the wave/note definition that follows. Instrument record size is 16 bytes.
691 |692 | swav number 2 bytes // the swav used 693 | swar number 2 bytes // the swar used. NB. cross-reference to "1.3.2 Info Block - Entry, Record 2 BANK" 694 | note number 1 byte // 0..127 695 | Attack Rate 1 byte // 0..127 696 | Decay Rate 1 byte // 0..127 697 | Sustain Level 1 byte // 0..127 698 | Release Rate 1 byte // 0..127 699 | Pan 1 byte // 0..127, 64 = middle 700 |701 |
If SBNKINS::fRecord = 16, the record is a range of note/wave definitions. The number of definitions = 'upper note' - 'lower note' + 1. The Instrument Record size is 2 + no. of definitions * 12 bytes.
702 |703 | lower note 1 byte // 0..127 704 | upper note 1 byte // 0..127 705 | 706 | unknown 2 bytes // usually == 01 00 707 | swav number 2 bytes // the swav used 708 | swar number 2 bytes // the swar used. 709 | note number 1 byte 710 | Attack Rate 1 byte 711 | Decay Rate 1 byte 712 | Sustain Level 1 byte 713 | Release Rate 1 byte 714 | Pan 1 byte 715 | 716 | ... 717 | ... 718 | ... 719 | 720 | unknown 2 bytes // usually == 01 00 721 | swav number 2 bytes // the swav used 722 | swar number 2 bytes // the swar used. 723 | note number 1 byte 724 | Attack Rate 1 byte 725 | Decay Rate 1 byte 726 | Sustain Level 1 byte 727 | Release Rate 1 byte 728 | Pan 1 byte 729 |730 |
For example, lower note = 30, upper note = 40, there will be 40 - 30 + 1 = 11 wave/note definitions.
731 | The first wave/note definition applies to note 30.
732 | The second wave/note definition applies to note 31.
733 | The third wave/note definition applies to note 32.
734 | ...
735 | The eleventh wave/note definition applies to note 40.
If SBNKINS::fRecord = 17, the record is a regional wave/note definition.
738 |739 | The first 8 bytes defines the regions. They divide the full note range [0..127] into several regions (max. is 8) 740 | An example is: 741 | 25 35 45 55 65 127 0 0 (So there are 6 regions: 0..25, 26..35, 36..45, 46..55, 56..65, 66..127) 742 | Another example: 743 | 50 59 66 83 127 0 0 0 (5 regions: 0..50, 51..59, 60..66, 67..84, 85..127) 744 | 745 | Depending on the number of regions defined, the corresponding number of wave/note definitions follow: 746 | 747 | unknown 2 bytes // usually == 01 00 748 | swav number 2 bytes // the swav used 749 | swar number 2 bytes // the swar used. 750 | note number 1 byte 751 | Attack Rate 1 byte 752 | Decay Rate 1 byte 753 | Sustain Level 1 byte 754 | Release Rate 1 byte 755 | Pan 1 byte 756 | ... 757 | ... 758 | 759 | In the first example, for region 0..25, the first wave/note definition applies. 760 | For region 26..35, the 2nc wave/note definition applies. 761 | For region 36..45, the 3rd wave/note definition applies. 762 | ... 763 | For region 66..127, the 6th wave/note definition applies. 764 |765 | 766 | 767 |
REMARKS: Unknown bytes before wave/defnition definition = 5, not 1 in 768 | stage_04_bank.sbnk, stage_04.sdat, Rom No.1156
769 | 770 | 771 |The articulation data affects the playback of the SSEQ file. They are 'Attack Rate', 'Decay Rate', 'Sustain Level' and 'Release Rate' (all have a value in range [0..127])
773 | 774 |775 | amplitude (%) 776 | 777 | 100% | /\ 778 | | / \__________ 779 | | / \ 780 | | / \ 781 | 0% |/__________________\___ time (sec) 782 | 783 |784 |
Imagine how the amplitude of a note varies from begin to the end.
785 |The graph above shows the amplitude envelope when a note is sound. The y-axis is Amplitude, x-asix is time.
786 | 787 |Attack rate determines how fast the note reaches 100% amplitude. (See the first upward curve). Thus the highest value 127 means the sound reaches 100% amplitude in the shortest time; 0 means the longest time.
788 |Decay rate determines how fast the amplitude decays to 0% amplitude. Of course the sound will not drop to 0% but stops at sustain level. (See the first downward curve). Thus the highest value 127 means the sound reachs the sustain level in the shortest time; 0 means the longest time.
789 |Sustain level determines the amplitude at which the sound sustains. (See the horizonal part). Thus the highest value 127 means the sound sustains at 100% amplitude (no decay), while 0 means 0% (full decay).
790 |Release rate determines how fast the amplitude drops from 100% to 0%. Not from sustain level to 0%. (See the second downward curve). The value has the same meaning as Decay rate.
791 | 792 |See this file for more details on how to interpret the articulation data. The raw data column is the transformed value used for calculation.
793 |The SEQ Player treats 0 as the 100% amplitude value and -92544 (723*128) as the 0% amplitude value. The starting ampltitude is 0% (-92544).
794 | 795 |During the attack phase, in each cycle, the SSEQ Player calculates the new amplitude value: amplitude value = attack rate * amplitude value / 255. The attack phase stops when amplitude reaches 0.
796 |The times column shows how many cycles are needed to reach 100% amplitude value.
797 |The sec column shows the corresponding time needed to reach 100% amplitude value.
798 |The scale column is the corresponding value to feed in DLS Bank.
799 | 800 |During the decay phase, in each cycle, the SSEQ Player calculates the new amplitude value: amplitude value = amplitude value - decay rate. Note the starting amplitude value is 0. The decay phase stops when amplitude reaches sustain level.
801 |The other columns are self-explanatory.
802 | 803 |
804 | 5. SWAV File Format805 | |
SWAV doesn't appear in SDAT. They may be found in the ROM elsewhere. They can also be readily extracted from a SWAR file (see below).
809 | 810 | 811 |812 | // info about the sample 813 | typedef struct tagSwavInfo 814 | { 815 | u8 nWaveType; // 0 = PCM8, 1 = PCM16, 2 = (IMA-)ADPCM 816 | u8 bLoop; // Loop flag = TRUE|FALSE 817 | u16 nSampleRate; // Sampling Rate 818 | u16 nTime; // (ARM7_CLOCK / nSampleRate) [ARM7_CLOCK: 33.513982MHz / 2 = 1.6756991 E +7] 819 | u16 nLoopOffset; // Loop Offset (expressed in words (32-bits)) 820 | u32 nNonLoopLen; // Non Loop Length (expressed in words (32-bits)) 821 | } SWAVINFO; 822 | 823 | // Swav file format 824 | typedef struct tagSwav 825 | { 826 | struct tagNdsStdFile { 827 | char type[4]; // 'SWAV' 828 | u32 magic; // 0x0100feff 829 | u32 nFileSize; // Size of this SWAV file 830 | u16 nSize; // Size of this structure = 16 831 | u16 nBlock; // Number of Blocks = 1 832 | } file; 833 | struct { 834 | char type[4]; // 'DATA' 835 | u32 nSize; // Size of this structure 836 | SWAVINFO info; // info about the sample 837 | u8 data[1]; // array of binary data 838 | } data; 839 | } SWAV; 840 |841 | 842 | 843 | 844 |
845 | 6. SWAR File Format846 | |
851 | typedef struct tagSwar 852 | { 853 | struct tagNdsStdFile { 854 | char type[4]; // 'SWAR' 855 | u32 magic; // 0x0100feff 856 | u32 nFileSize; // Size of this SWAR file 857 | u16 nSize; // Size of this structure = 16 858 | u16 nBlock; // Number of Blocks = 1 859 | } file; 860 | struct { 861 | char type[4]; // 'DATA' 862 | u32 nSize; // Size of this structure 863 | u32 reserved[8]; // reserved 0s, for use in runtime 864 | u32 nSample; // Number of Samples 865 | } data; 866 | u32 nOffset[1]; // array of offsets of samples 867 | } SWAR; 868 |869 | 870 |
NB. After the array of offsets, the binary samples follow. Each sample has a SWAVINFO structure before the sample data. Therefore, it is easy to make a SWAV from the samples in SWAR.
871 | 872 | 873 | 874 | 875 |
876 | 7. STRM File Format877 | |
882 | typedef struct tagSTRM 883 | { 884 | struct tagNdsStdFile { 885 | char type[4]; // 'STRM' 886 | u32 magic; // 0x0100feff 887 | u32 nFileSize; // Size of this STRM file 888 | u16 nSize; // Size of this structure = 16 889 | u16 nBlock; // Number of Blocks = 2 890 | } file; 891 | struct { 892 | char type[4]; // 'HEAD' 893 | u32 nSize; // Size of this structure 894 | u8 nWaveType; // 0 = PCM8, 1 = PCM16, 2 = (IMA-)ADPCM 895 | u8 bLoop; // Loop flag = TRUE|FALSE 896 | u8 nChannel; // Channels 897 | u8 unknown; // always 0 898 | u16 nSampleRate; // Sampling Rate (perhaps resampled from the original) 899 | u16 nTime; // (1.0 / rate * ARM7_CLOCK / 32) [ARM7_CLOCK: 33.513982MHz / 2 = 1.6756991e7] 900 | u32 nLoopOffset; // Loop Offset (samples) 901 | u32 nSample; // Number of Samples 902 | u32 nDataOffset; // Data Offset (always 68h) 903 | u32 nBlock; // Number of Blocks 904 | u32 nBlockLen; // Block Length (Per Channel) 905 | u32 nBlockSample; // Samples Per Block (Per Channel) 906 | u32 nLastBlockLen; // Last Block Length (Per Channel) 907 | u32 nLastBlockSample; // Samples Per Last Block (Per Channel) 908 | u8 reserved[32]; // always 0 909 | } head; 910 | struct { 911 | char type[4]; // 'DATA' 912 | u32 nSize; // Size of this structure 913 | u8 data[1]; // Arrays of wave data 914 | } data; 915 | } SDATSTRM; 916 |917 | 918 |
A Block is the same as SWAV Wave Data.
921 | 922 |Mono (SWAV)
923 | Block 1Stereo (STRM)
930 | Block 1 LIt isn't too shocking that a game with the complexity of Neverwinter Nights 16 | (NWN) requires a large assortment of different data files. These files 17 | define everything from the basic mechanics of the game all the way to the videos 18 | played during the introduction.
19 |Assuming that you installed the game in the default directories, the data 20 | files are spread out in the following directories.
21 |When NWN starts, it has available several methods to locate the data 40 | files. Depending on the working directory or more probably the directory 41 | of the executable, NWN can locate three key files, "chitin.key", 42 | "dialog.tlk", and "nwn.ini".
43 |Chitin.key contains a listing of all the resources available to NWN at 44 | runtime. Given a resource name, chitin.key can be used to locate the name 45 | of the master data file (.BIF) containing the resource.
46 |Dialog.tlk is textual resource file. Given an number, dialog.tlk can 47 | return the text associated with that number. This method has at least two benefits. 48 | First, common strings can be stored in a central location instead of spread out amongst 49 | many script files. This makes it possible to change the text without 50 | having to modify or recompile the scripts that reference the string. The 51 | second benefit is that depending on the language of the user, a different 52 | dialog.tlk can be installed. For the French, the French dialog.tlk is 53 | installed. (Note: I haven't actually seen exactly how Bioware does 54 | language support. It is an assumption on my part that they just use different 55 | dialog.tlk files. They could have dialog.tlk reserved as the common English 56 | and then a different file name for each of the other languages.)
57 |Nwn.ini is a standard format Windows ini file. It contains much of the 58 | game configuration information. The section of most interest to us however 59 | is the "[Alias]" section. This section provides a mapping 60 | between logical directory names and their physical counterparts. This 61 | allowed the developers to not have to worry about the how the NWN installation 62 | would look at release time. As long as the group producing the 63 | installation system properly set the different keys in the "[Alias]" 64 | section, NWN will file the data files without modification.
65 |For add on programs such as my NWN Explorer, Bioware stored NWN's 66 | installation directory in the registry. The registry key 67 | "HKEY_LOCAL_MACHINE\SOFTWARE\BioWare\NWN\Neverwinter\Location" 68 | contains the installation directory. However, I would like to add that 69 | anyone who wishes to develop 3rd party application for NWN should also allow the 70 | user to specify the location of NWN. Even though I don't foresee Bioware 71 | removing this registry key, there is always the chance that it might be missing 72 | or invalid. Thus, it would be a shame if a user couldn't use your 3rd 73 | party application because you were too lazy to add this simple feature.
74 | 75 |NWN has a wide range of resource types. The following table lists most 78 | of these types. (Many of these resource types might not be used by NWN but 79 | were present in earlier Bioware games.)
80 | 81 |Resource Name | 86 |Resource Type | 87 |
---|---|
RES | 0x0000 |
BMP | 0x0001 |
MVE | 0x0002 |
TGA | 0x0003 |
WAV | 0x0004 |
PLT | 0x0006 |
INI | 0x0007 |
BMU | 0x0008 |
MPG | 0x0009 |
TXT | 0x000A |
PLH | 0x07D0 |
TEX | 0x07D1 |
MDL | 0x07D2 |
THG | 0x07D3 |
FNT | 0x07D5 |
LUA | 0x07D7 |
SLT | 0x07D8 |
NSS | 0x07D9 |
NCS | 0x07DA |
MOD | 0x07DB |
ARE | 0x07DC |
SET | 0x07DD |
IFO | 0x07DE |
BIC | 0x07DF |
WOK | 0x07E0 |
2DA | 0x07E1 |
TLK | 0x07E2 |
TXI | 0x07E6 |
GIT | 0x07E7 |
BTI | 0x07E8 |
UTI | 0x07E9 |
BTC | 0x07EA |
UTC | 0x07EB |
DLG | 0x07ED |
ITP | 0x07EE |
BTT | 0x07EF |
UTT | 0x07F0 |
DDS | 0x07F1 |
UTS | 0x07F3 |
LTR | 0x07F4 |
GFF | 0x07F5 |
FAC | 0x07F6 |
BTE | 0x07F7 |
UTE | 0x07F8 |
BTD | 0x07F9 |
UTD | 0x07FA |
BTP | 0x07FB |
UTP | 0x07FC |
DTF | 0x07FD |
GIC | 0x07FE |
GUI | 0x07FF |
CSS | 0x0800 |
CCS | 0x0801 |
BTM | 0x0802 |
UTM | 0x0803 |
DWK | 0x0804 |
PWK | 0x0805 |
BTG | 0x0806 |
UTG | 0x0807 |
JRL | 0x0808 |
SAV | 0x0809 |
UTW | 0x080A |
4PC | 0x080B |
SSF | 0x080C |
HAK | 0x080D |
NWM | 0x080E |
BIK | 0x080F |
PTM | 0x0811 |
PTT | 0x0812 |
ERF | 0x270D |
BIF | 0x270E |
KEY | 0x270F |
NWN has built-in support for different languages. Each of these 165 | languages also has a male and female version. These languages are commonly 166 | used in names, descriptions, and dialogs.
167 |Language | 172 |ID | 173 |
---|---|
English | 0 |
French, Male | 2 |
French, Female | 3 |
German, Male | 4 |
German, Female | 5 |
Italian, Male | 6 |
Italian, Female | 7 |
Spanish, Male | 8 |
Spanish, Female | 9 |
187 | 188 | 189 | -------------------------------------------------------------------------------- /specs/torlack/bif.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
BIF files contain the actual data files referenced in KEY files.
12 |14 | The BIF file begins with a header.
15 |20 | Offset | 21 |22 | Type | 23 |24 | Description | 25 |
---|---|---|
0x0000 | 28 |CHAR [4] | 29 |File type signature (usually "BIFF") | 30 |
0x0004 | 33 |CHAR [4] | 34 |Version number (usually "V1 ") | 35 |
0x0008 | 38 |UINT32 | 39 |Number of resources in the file | 40 |
0x000C | 43 |UINT32 | 44 |45 | Unknown value | 46 |
0x0010 | 49 |UINT32 | 50 |Offset from the start of file to the first resource structure | 51 |
0x0014 | 54 |Total size of the structure | 55 |
For each resource contained in a BIF, there is a corresponding entry in the 61 | resource list. This list begins in the file at the offset specified in 62 | the header. The resource structures are stored sequentially in the BIF 63 | file. The BIF header specifies the number of resources.
64 |69 | Offset | 70 |71 | Type | 72 |73 | Description | 74 |
---|---|---|
0x0000 | 77 |UINT32 | 78 |Key file ID of the resource | 79 |
0x0004 | 82 |UINT32 | 83 |Offset from the start of the file to the given resource | 84 |
0x0008 | 87 |UINT32 | 88 |Length of the resource in bytes | 89 |
0x000C | 92 |UINT32 | 93 |Type of the resource | 94 |
0x0010 | 97 |Total size of the structure | 98 |
(Note: ITP files are more correctly known as the GFF and BioWare's 12 | documentation can be found here.)
13 |An ITP file can be thought of as a hierarchical collection variables.
14 |That cleared it up, didn't it.
15 |In an ITP file, you have a series of what can be considered variable assignments 16 | such as 'PlayerName = "Francis"'. There can practically be an infinite 17 | number of variable assignments in one file. Also, ITP files allow you to 18 | nest variable assignments much the way a programmer might nest structures.
19 |ITP files are very common in NWN. The format is used for a wide collection 20 | of files such as; ITP, BIC, DLG, GIT, GFF, FAC, UTI, UTC, UTT, UTS, UTE, UTD, 21 | UTP, UTM, and UTW. All of these different files share the same common 22 | file format. So in reality calling the file format ITP is inaccurate, but 23 | it is the name we all know best.
24 |ITP files contain seven sections of data. The sections are as follows:
26 |57 | Offset | 58 |59 | Type | 60 |61 | Description | 62 |
---|---|---|
0x0000 | 65 |char [4] | 66 |4 byte signature. | 67 |
0x0004 | 70 |char [4] | 71 |4 byte version. | 72 |
0x0008 | 75 |UINT32 | 76 |Offset from start of the file to the first entry | 77 |
0x000C | 80 |UINT32 | 81 |Number of entries in the file | 82 |
0x0010 | 85 |UINT32 | 86 |Offset from start of the file to the first element | 87 |
0x0014 | 90 |UINT32 | 91 |Number of elements in the file | 92 |
0x0018 | 95 |UINT32 | 96 |Offset from start of the file to the first variable name | 97 |
0x001C | 100 |UINT32 | 101 |Number of names in the variable name block | 102 |
0x0020 | 105 |UINT32 | 106 |Offset from start of the file to the first variable data | 107 |
0x0024 | 110 |UINT32 | 111 |Number of bytes in the variable data block | 112 |
0x0028 | 115 |UINT32 | 116 |Offset from start of the file to the first multimap | 117 |
0x002C | 120 |UINT32 | 121 |Number of bytes in the multimap table | 122 |
0x0030 | 125 |UINT32 | 126 |Offset from start of the file to the first list | 127 |
0x0034 | 130 |UINT32 | 131 |Number of bytes in the list table | 132 |
0x0038 | 135 |Total length of the structure | 136 |
All offsets in the file header are from the start of the file. The file 141 | header is always at offset zero. It seems that the offset to the first 142 | entry is always 0x0038. But please note that for entries and elements, we 143 | are given the number of entries or elements. For the four remaining data 144 | sections, the total section length is given in bytes.
145 |The entity table is an array of structures of the following form:
147 |152 | Offset | 153 |154 | Type | 155 |156 | Description | 157 |
---|---|---|
0x0000 | 160 |UINT32 | 161 |Entity code (Use unknown) | 162 |
0x0004 | 165 |UINT32 | 166 |Element index or MultiMap offset | 167 |
0x0008 | 170 |UINT32 | 171 |Number of elements in this entry | 172 |
0x000C | 175 |Total length of the structure | 176 |
The first entry in the entry table is the root of whole hierarchy. From 181 | that one entry, all other entries and elements can be accessed in a logical 182 | form.
183 |Entries only serve one function, to contain one or more elements. 184 | Depending on the number of elements contained in an entry, accessing the 185 | elements can either be easy or require indirect referencing. When an 186 | entry contains only one element, and thus the value at offset 0x0008 is one, 187 | then the value at offset 0x0004 is the index in the element table of the only 188 | child. When an entry contains more than one element, then the value at 189 | offset 0x0004 contains a byte offset into the multimap table. This offset 190 | points to an array of element numbers stored as UINT32 values. 191 |
192 |Here is an example of how you might iterate through the list of elements for an 193 | entity. This code assumes that the whole file is resident in memory in a 194 | contiguous buffer.
195 |196 |218 |char *pFileData = AddressOfTheITPDataInMemory; 197 | Header *pHeader = (Header *) pFileData; 198 | Entry *pEntryTable = (Entry *) &pFileData [pHeader ->EntryOffset]; 199 | Element *pElementTable = (Element *) &pFileData [pHeader ->ElementOffset]; 200 | 201 | Entry *pEntry = &pEntryTable [0]; // For this example, use root entry 202 | 203 | if (pEntry ->Count == 1) // We have one element 204 | { 205 | Element *pElement = &pElementTable [pEntry ->Offset]; 206 | // Do something with the element 207 | } 208 | else //more than one 209 | { 210 | UINT32 *pMMap = (UINT32 *) &pFileData [pHeader ->MultiMapOffset + pEntry ->Offset]; 211 | for (int i = 0; i < pEntry ->Count; i++) 212 | { 213 | Element *pElement = &pElementTable [pMMap [i]]; 214 | // Do something with the element 215 | } 216 | }217 |
There is one trick you can use to reduce duplicated code. I use this trick 219 | in my software.
220 |221 |239 |char *pFileData = AddressOfTheITPDataInMemory; 222 | Header *pHeader = (Header *) pFileData; 223 | Entry *pEntryTable = (Entry *) &pFileData [pHeader ->EntryOffset]; 224 | Element *pElementTable = (Element *) &pFileData [pHeader ->ElementOffset]; 225 | 226 | Entry *pEntry = &pEntryTable [0]; // For this example, use root entry 227 | 228 | UINT32 *pMMap; 229 | if (pEntry ->Count == 1) 230 | pMMap = &pEntry ->Offset; 231 | else 232 | pMMap = (UINT32 *) &pFileData [pHeader ->MultiMapOffset + pEntry ->Offset];233 |for (int i = 0; i < pEntry ->Count; i++) 234 | { 235 | Element *pElement = &pElementTable [pMMap [i]]; 236 | // Do something with the element 237 | }238 |
The element table is an array of structures of the following form: 242 |
243 |248 | Offset | 249 |250 | Type | 251 |252 | Description | 253 |
---|---|---|
0x0000 | 256 |UINT32 | 257 |Variable type (0-15) | 258 |
0x0004 | 261 |UINT32 | 262 |Variable name index | 263 |
0x0008 | 266 |VARIES | 267 |Data for the variable | 268 |
0x000C | 271 |Total length of the structure | 272 |
As you can see, the element structure is very basic. From the structure we 277 | know the name of the variable, the type of the variable, and the assigned value 278 | of the variable. The only difficult part is extracting the value from the 279 | structure. 280 |
281 |There are 16 know variable types supported by the ITP format. They are as 282 | follows: 283 |
284 |289 | Value | 290 |291 | Type | 292 |293 | Access | 294 |295 | Data | 296 |
---|---|---|---|
0 | 299 |UINT8 | 300 |Direct | 301 |Unsigned byte | 302 |
1 | 305 |INT8 | 306 |Direct | 307 |Signed byte | 308 |
2 | 311 |UINT16 | 312 |Direct | 313 |Unsigned word | 314 |
3 | 317 |INT16 | 318 |Direct | 319 |Signed word | 320 |
4 | 323 |UINT32 | 324 |Direct | 325 |Unsigned longword | 326 |
5 | 329 |INT32 | 330 |Direct | 331 |Signed longword | 332 |
6 | 335 |UINT64 | 336 |Indirect | 337 |Unsigned quadword | 338 |
7 | 341 |INT64 | 342 |Indirect | 343 |Signed quadword | 344 |
8 | 347 |FLOAT | 348 |Indirect | 349 |Four byte floating point value | 350 |
9 | 353 |DOUBLE | 354 |Indirect | 355 |Eight byte floating point value | 356 |
10 | 359 |STRING | 360 |Indirect | 361 |Counted string | 362 |
11 | 365 |RESREF | 366 |Indirect | 367 |Counted resource name | 368 |
12 | 371 |STRREF | 372 |Complex | 373 |Multilingual capable string | 374 |
13 | 377 |DATREF | 378 |Complex | 379 |Counted binary data | 380 |
14 | 383 |CAPREF | 384 |Complex | 385 |List of child elements | 386 |
15 | 389 |LIST | 390 |Complex | 391 |List of child entries | 392 |
For the simple data types, UINT8, INT8, UINT16, INT16, UINT32, INT32 and FLOAT, 397 | the value is just stored directly into offset 0x0008. Since NWN is for 398 | Intel systems, all the data values are stored in little endian format. 399 | Thus, for UINT8 and INT8, the value is a single byte stored literally at offset 400 | 0x0008 in the element structure. A UINT16 or INT16 takes up the first two 401 | bytes. The remaining three basic data types take up all four bytes. 402 | Access to the different values of the element can be done with this structure. 403 |
404 |405 |425 |struct Element 406 | { 407 | UINT32 Type; 408 | UINT32 NameIndex; 409 | union 410 | { 411 | UINT8 ui8; 412 | INT8 si8; 413 | UINT16 ui16; 414 | INT16 si16; 415 | UINT32 ui32; 416 | INT32 si32; 417 | FLOAT flt; 418 | UINT32 Offset; 419 | }; 420 | };421 |Note: The "Offset" member can be used to access the indirect and complex 422 | data stored in the variable data section.. 423 |
424 |
426 | In the case of UINT64, INT64 and DOUBLE, the value stored at offset 0x0008 is a 427 | byte offset into the variable data region. Starting at that address would 428 | be stored the value in question. 429 |
430 |431 |452 |char *pFileData = AddressOfTheITPDataInMemory; 432 | Header *pHeader = (Header *) pFileData; 433 | Element *pElementTable = (Element *) &pFileData [pHeader ->ElementOffset]; 434 | 435 | Element *pElement = &pElementTable [0]; // For this example, use first element436 |if (pElement ->Type == 6) 437 | { 438 | UINT64 ul64 = *((UINT64 *) &pFileData [ 439 | pHeader ->VarDataOffset + pElement ->Offset]); 440 | } 441 | else if (pElement ->Type == 7) 442 | { 443 | INT64 l64 = *((INT64 *) &pFileData [ 444 | pHeader ->VarDataOffset + pElement ->Offset]); 445 | } 446 | else if (pElement ->Type == 9) 447 | { 448 | double d = *((double *) &pFileData [ 449 | pHeader ->VarDataOffset + pElement ->Offset]); 450 | }451 |
The STRING and RESREF types are two more indirect types but require a bit more 453 | work to access. For both the STRING and RESREF the actual data is stored 454 | in the variable data section starting at the given offset. They both 455 | being with the length of string. Following the length, are the actual 456 | bytes of the string. The string is not NULL terminated. However, 457 | STRING and RESREF do differ. In the case of STRING, the string length is 458 | stored as a UINT32 value. In the case of RESREF, the string length is 459 | stored as a UINT8 value.
460 |461 |483 |char *pFileData = AddressOfTheITPDataInMemory; 462 | Header *pHeader = (Header *) pFileData; 463 | Element *pElementTable = (Element *) &pFileData [pHeader ->ElementOffset]; 464 | 465 | Element *pElement = &pElementTable [0]; // For this example, use first element466 |if (pElement ->Type == 10) //STRING 467 | { 468 | UINT32 Length = *((UINT32 *) &pFileData [ 469 | pHeader ->VarDataOffset + pElement ->Offset]); 470 | char *String = (char *) &pFileData [ 471 | pHeader ->VarDataOffset + pElement ->Offset + 472 | sizeof (UINT32)] 473 | } 474 | else if (pElement ->Type == 11) //RESREF 475 | { 476 | UINT8 Length = *((UINT8 *) &pFileData [ 477 | pHeader ->VarDataOffset + pElement ->Offset]); 478 | char *String = (char *) &pFileData [ 479 | pHeader ->VarDataOffset + pElement ->Offset + 480 | sizeof (UINT8)] 481 | }482 |
The STRREF has two uses. First, it references a string in the dialog.tlk 484 | file. Second, it provides localized (human language specific) version of 485 | the string. The offset in the element points to a variable length 486 | structure that begins with the following 12 bytes of data. 487 |
488 |493 | Offset | 494 |495 | Type | 496 |497 | Description | 498 |
---|---|---|
0x0000 | 501 |UINT32 | 502 |Number of bytes included in the STRREF not including this count. | 503 |
0x0004 | 506 |INT32 | 507 |ID of the string in the in the dialog.tlk file. If none, then -1. | 508 |
0x0008 | 511 |UINT32 | 512 |Number of language specific strings. This value is usually zero. | 513 |
0x000C | 516 |Total length of the structure | 517 |
If the number of language specific strings is zero, then the length of the total 522 | STRREF is only 12 bytes. The text of the string can be retrieved 523 | from dialog.tlk. The value stored as the size of the STRREF will be 8 524 | since the 4 bytes in the count aren't included. However, if the number of 525 | language specific strings is not zero, then there will be the given number of 526 | the following structure following the initial STRREF structure. 527 |
528 |533 | Offset | 534 |535 | Type | 536 |537 | Description | 538 |
---|---|---|
0x0000 | 541 |UINT32 | 542 |Language of the string (values unknown) | 543 |
0x0004 | 546 |UINT32 | 547 |Number of bytes in the following string | 548 |
0x0008 | 551 |CHAR [] | 552 |Variable length string | 553 |
VARIES | 556 |Total length of the structure will always be 8 plus the length of 557 | the string | 558 |
The DATREF associates a block of arbitrary binary data with the element in 563 | question. At the offset in the variable data section given in the element 564 | is a UINT32 that has the number of bytes in the DATREF. Following the 565 | UINT32 is the binary data. 566 |
567 |The CAPREF element type allows for an element to contain an array of other 568 | elements by referencing a single entry. The offset in the element 569 | structure specifies the entry number. All the elements that entry 570 | references can be considered as the children of the element. 571 |
572 |The LIST element type allows for an element to contain an array of other 573 | entries. For a LIST type, the offset in the element structure specifies 574 | an offset into the list table. This is a byte offset even though the 575 | table itself is just a series of UINT32 values. At the first UINT32 576 | pointed to by the offset will be the number of entries referenced by the 577 | element. Following this count will be the actual entry 578 | numbers. 579 |
580 |581 |598 |char *pFileData = AddressOfTheITPDataInMemory; 582 | Header *pHeader = (Header *) pFileData; 583 | Element *pElementTable = (Element *) &pFileData [pHeader ->ElementOffset]; 584 | 585 | Element *pElement = &pElementTable [0]; // For this example, use first element586 |if (pElement ->Type == 15) //LIST 587 | { 588 | UINT32 *List = *((UINT32 *) &pFileData [ 589 | pHeader ->ListDataOffset + pElement ->Offset]); 590 | UINT32 EntryCount = List [0]; 591 | for (int i = 0; i < (int) EntryCount; i++) 592 | { 593 | UINT32 EntryIndex = List [i + 1]; 594 | // do something with Entry 595 | } 596 | }597 |
The variable name table is a series of variable names stored in 16 byte long 600 | character strings. Care should be taken when processing the variable name 601 | strings. If the string is less than 16 characters in length, it will be 602 | NULL terminated. However, if the variable name is 16 characters long, 603 | then it will not. The easiest thing to do is copy the string into a 17 604 | byte character string and then set the 17th character to NULL. Thus, if 605 | the string is 16 characters long, the 17th character will force a NULL 606 | termination.
607 |All other data tables are purely subservient to the entry and element 609 | tables. See the section on the elements to see how this information is 610 | interpreted.
611 |Even though I was able to decode much of the ITP format soon after the game was 613 | released, I want to thank Logxen, Seg Falt and Chanteur 614 | for the information they published about the file format. Their 615 | information filled in some very important missing elements such as CAPREF, 616 | DATREF and elements of STRREF.
617 | 618 | 619 | -------------------------------------------------------------------------------- /specs/torlack/key.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |The KEY file in Neverwinter Nights (NWN) is used to provide the game with a 16 | centralized location for locating resources contained in the BIF files. To the 17 | best of my knowledge, only one file, "chitin.key" uses this file 18 | format.
19 |The KEY file format is very basic. It consists a header and two large 20 | tables. At the start of the file is the header. It provides 21 | information about the file format, the file format version, offset and size 22 | information about the two tables.
23 |Offset | 28 |Type | 29 |Description | 30 |
---|---|---|
0x0000 | 33 |char [4] | 34 |4 byte signature ("KEY ") | 35 |
0x0004 | 38 |char [4] | 39 |4 byte version ("V1 ") | 40 |
0x0008 | 43 |UINT32 | 44 |Number of BIF files referenced in the key file | 45 |
0x000C | 48 |UINT32 | 49 |Number of resources (RES) contained in the key file | 50 |
0x0010 | 53 |UINT32 | 54 |Offset from the start of the file to the first BIF entry | 55 |
0x0014 | 58 |UINT32 | 59 |Offset from the start of the file to the first RES entry | 60 |
0x0018 | 63 |UINT32 [10] | 64 |Unknown | 65 |
0x0040 | 68 |Total Structure Size | 69 |
One of the two tables of interest to us is the BIF table. This table 74 | lists the all of the BIF files contained in the NWN data directory.
75 |Offset | 80 |Type | 81 |Description | 82 |
---|---|---|
0x0000 | 85 |UINT32 | 86 |Length of the BIF file in bytes | 87 |
0x0004 | 90 |UINT32 | 91 |Offset from the start of the KEY file to the BIF file name | 92 |
0x0008 | 95 |UINT16 | 96 |Length of the BIF file name in bytes. Not including the 97 | terminating 0. | 98 |
0x000A | 101 |UINT16 | 102 |Unknown | 103 |
0x000C | 106 |Total Structure Size | 107 |
The final table lists all of the resources contained within the BIF 112 | files.
113 |Offset | 118 |Type | 119 |Description | 120 |
0x0000 | 123 |char [16] | 124 |Name of the resource | 125 |
0x0010 | 128 |UINT16 | 129 |Type of the resource | 130 |
0x0012 | 133 |UINT32 | 134 |BIF id of the resource | 135 |
0x0016 | 138 |Total Structure Size | 139 |
NOTE: It is very important that the structure packing for this structure is 144 | set to BYTE or WORD alignment. Failure to do so will cause the UINT32 at 145 | offset 0x0012 to be placed at 0x0014 and thus yield buggy functionality. 146 | On non-Intel platforms, this alignment will cause alignment faults unless the 147 | compiler has a "unaligned" type or special care is made when accessing 148 | the UINT32.
149 |When NWN needs to locate a resource, it can be done just using the resource 150 | name. If the name is used by more than one resource type, then the type 151 | would also be required to locate the resource. To locate the resource, the 152 | RES table is be searched for a match. Once a match has been located, the 153 | BIF id of the resource gives the exact location of the resource.
154 |A BIF id is actually two numbers combined into one UINT32. The first 20 155 | bits starting from bit 0 specify the resource index inside the bif. The 156 | final 12 bits specify the BIF in the BIF table contained within the KEY 157 | file.
158 |Example: Suppose NWN needed to locate the resource 159 | "IT_GOLD001". It would start by searching the RES table for the 160 | entry "IT_GOLD001". Once located in the RES table, the BIF id is 161 | examined. Suppose with BIF id is 0x00400029. The lower 20 bits of 162 | the BIF id tell NWN that the "IT_GOLD001" resource is the 0x29th or 163 | 41st resource in the BIF file. From the upper 12 bits, NWN knows that the 164 | 4th BIF file contains the resource. So, the 4th record is examined in the 165 | BIF table. This yields the file name for the BIF. The BIF can then 166 | be opened and the 41st resource read from the file. (Information on how 167 | the 41st resource in the BIF file is located will be covered in the BIF file 168 | paper.)
169 | 170 |For more information on the resource types, see NWN 171 | Data File Basics.
172 | 173 | 174 | 175 | 176 | -------------------------------------------------------------------------------- /specs/torlack/mod.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |ERF Format Document: http://nwn.bioware.com/developers/erf.html
15 | BioWare's "For Developers" site: http://nwn.bioware.com/developers/
First off, the ERF file format is a file format used by many different 19 | files in NWN. This include files such as saved games (SAV), HAK packs (HAK), 20 | exported resource files (ERF), Neverwinter Nights modules (NWM), and of course, 21 | module files (MOD). ERF stands for Encapsulated Resource File.
22 |ERF files are relatively simple files that contain a collection of other 23 | game resources. They also provide space for a textual description of the 24 | contents of the file. In the case of modules, this is the module 25 | description.
26 |Like almost all data files in NWN, the ERF file begins with a header.
28 |Offset | 33 |Type | 34 |Description | 35 |
---|---|---|
0x0000 | 38 |CHAR [4] | 39 |File type signature. Depending on the file type, 40 | this value varies. | 41 |
0x0004 | 44 |CHAR [4] | 45 |Version number | 46 |
0x0008 | 49 |UINT32 | 50 |Number of strings in the description | 51 |
0x000C | 54 |UINT32 | 55 |Total length of the strings in bytes | 56 |
0x0010 | 59 |UINT32 | 60 |Number of resources in the file | 61 |
0x0014 | 64 |UINT32 | 65 |Offset from the start of file to the first string | 66 |
0x0018 | 69 |UINT32 | 70 |Offset from the start of file to the first resource 71 | structure | 72 |
0x001C | 75 |UINT32 | 76 |Offset from the start of file to the first position 77 | structure | 78 |
0x0020 | 81 |UINT32 | 82 |Year the file was created with 1900 being 0 | 83 |
0x0024 | 86 |UINT32 | 87 |Day of the year the file was created with Jan 1st being 0 | 88 |
0x0028 | 91 |UINT32 | 92 |String reference for the description. 93 | Varies depending on the file type: 94 | MOD = -1, SAV = 0, NWM = -1 95 | For ERF and HAK, this value is unpredictable |
96 |
0x002C | 99 |UINT32 [29] | 100 |Spare values all initialized to 0 | 101 |
0x00A0 | 104 |Total size of the structure | 105 |
Utilities such as the module editing tool and HAK pack editor from Bioware 112 | allow the user to specify strings that describe the contents of the module or 113 | HAK pack. These utilities also allow the user to supply this information 114 | in multiple languages. Each string is stored sequentially starting at the 115 | offset specified in the ERF file header.
116 | 117 |Offset | 122 |Type | 123 |Description | 124 |
---|---|---|
0x0000 | 127 |UINT32 | 128 |Language Identifier (See the document on data file 129 | basics) | 130 |
0x0004 | 133 |UINT32 | 134 |Length of the string in characters | 135 |
0x0008 | 138 |CHAR [*] | 139 |String data | 140 |
0x0008+Length | 143 |Total size of the structure | 144 |
For each resource contained in an ERF, there is a corresponding entry in 151 | the resource list. This list begins in the file at the offset specified in 152 | the header. The resource structures are stored sequentially in the ERF file. The 153 | ERF header specifies the number of resources.
154 |Offset | 159 |Type | 160 |Description | 161 |
---|---|---|
0x0000 | 164 |CHAR [16] | 165 |Name of the resource | 166 |
0x0010 | 169 |UINT32 | 170 |Index of the resource | 171 |
0x0014 | 174 |UINT16 | 175 |Resource type (See the document on data file basics) | 176 |
0x0016 | 179 |UINT16 | 180 |Reserved and zero | 181 |
0x0018 | 184 |Total size of the structure | 185 |
The name of the resource is padded with NULLs however they are not 190 | necessarily NULL terminated. In the case of a resource name that is 16 191 | characters long, no NULL will exist. It can be mixed case, but most all resources 192 | names are lowercase.
193 |For each resource, there is a corresponding position structure that defines 195 | where the resource is located in the ERF. This list begins in the file 196 | at the offset specified in the header. It is also sequentially stored in 197 | the ERF file.
198 |Offset | 203 |Type | 204 |Description | 205 |
---|---|---|
0x0000 | 208 |UINT32 | 209 |Offset from the start of the file to the given resource | 210 |
0x0004 | 213 |UINT32 | 214 |Length of the resource in bytes | 215 |
0x0008 | 218 |Total size of the structure | 219 |
The people at the OpenKnights 226 | project noticed that for MOD and NWM files only, there exists a strange block of data 227 | between the resource structures and the position structures. The size of 228 | the block seems to be eight bytes per resource. In my tests, all the data 229 | seemed to be NULL. What is strange about this data is that there doesn't 230 | exist an offset in the ERF file header. Having a block of data that 231 | contains no offset in the file header is very uncharacteristic for Bioware.
232 | 233 | 234 | 235 | -------------------------------------------------------------------------------- /specs/torlack/plt.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |A texture palette file is a variant on a standard texture. However, 16 | instead of being a fixed texture where all the colors are constant, a texture 17 | palette file allows the content creator to select from a wide range of color 18 | palettes for preset areas in the texture.
19 | 20 |There are four basic elements that go into converting a PLT to a real texture 21 | at runtime. Some elements are supplied by the PLT file while others are 22 | supplied by the game itself and the content creator.
23 |There are a total of ten different palette groups. The palette name is a 37 | human readable name displayed in the user interface. The group index is 38 | the index of the palette group. This matches the indices found in the PLT 39 | files. The UI graphic is the palette as it is presented to the content 40 | creator. Each UI graphic contains 256 different selections that corresponds 41 | directly to a row in the actual palette file in the last column of the table.
42 | 43 |Name | 49 |Group Index | 50 |UI | 51 |Palette File | 52 |
---|---|---|---|
Skin | 55 |0 | 56 |![]() |
57 | ![]() |
58 |
Hair | 61 |1 | 62 |![]() |
63 | ![]() |
64 |
Metal 1 | 67 |2 | 68 |![]() |
69 | ![]() |
70 |
Metal 2 | 73 |3 | 74 |![]() |
75 | ![]() |
76 |
Cloth 1 | 79 |4 | 80 |![]() |
81 | ![]() |
82 |
Cloth 2 | 85 |5 | 86 |![]() |
87 | ![]() |
88 |
Leather 1 | 91 |6 | 92 |![]() |
93 | ![]() |
94 |
Leather 2 | 97 |7 | 98 |![]() |
99 | ![]() |
100 |
Tattoo 1 | 103 |8 | 104 |![]() |
105 | ![]() |
106 |
Tattoo 2 | 109 |9 | 110 |![]() |
111 | ![]() |
112 |
The PLT file beings with a fixed header that defines the attributes of the 121 | texture. Following the header is pixel information.
122 |Offset | 127 |Type | 128 |Description | 129 |
---|---|---|
0x0000 | 132 |char [4] | 133 |4 byte signature ("PLT ") | 134 |
0x0004 | 137 |char [4] | 138 |4 byte version ("V1 ") | 139 |
0x0008 | 142 |UINT32 | 143 |Unknown | 144 |
0x000C | 147 |UINT32 | 148 |Unknown | 149 |
0x0010 | 152 |UINT32 | 153 |Width of the texture in pixels | 154 |
0x0014 | 157 |UINT32 | 158 |Height of the texture in pixels | 159 |
0x0018 | 162 |Total Structure Size | 163 |
The actual pixel data is an array of the following structure. There are 168 | (width X height) instances of this structure in a palette file.
169 |Offset | 174 |Type | 175 |Description | 176 |
---|---|---|
0x0000 | 179 |UINT8 | 180 |Color Index | 181 |
0x0001 | 184 |UINT8 | 185 |Palette Group Index | 186 |
0x0002 | 189 |Total Structure Size | 190 |
Imagine one of the body textures used to skin a model. From this PLT 196 | file, we have the width and height of the texture. When NWN started, it 197 | loads the palette files into memory. Finally, for a specific creature, we 198 | have 10 different values, one for each of the different palette groups. 199 | These values specify which palette is to be used. These values are 200 | supplied by the content creator.
201 |For each of the pixels in the PLT texture, we have a color index and a 202 | palette group index. To locate the actual color value for that pixel, the 203 | following process is used.
204 |I have no idea who originally decoded the PLT file format. It wasn't 215 | me. I read a very sketchy description on the Bioware forums and then 216 | filled in the blanks. Thanks whoever you are.
217 | 218 | 219 | 220 | 221 | -------------------------------------------------------------------------------- /specs/trn/trn.aswm.md: -------------------------------------------------------------------------------- 1 | # NWN2 TRN.ASWM packet structure 2 | _Thibaut CHARLES_ - CromFr@gmail.com 3 | 4 | This document is a mix of information: 5 | - I found by reverse engineering: basic structure, vertices, edged, triangles 6 | - Extracted from [Skywing NWN2DataLib](https://github.com/SkywingvL/nwn2dev-public/blob/master/NWN2DataLib) 7 | source code: everything else 8 | 9 | My implementation can be found [here](https://github.com/CromFr/nwn-lib-d/blob/master/source/nwn/trn.d), 10 | and supports parsing, serialization, mesh importation, path table & island 11 | baking. 12 | 13 | ## Packet 14 | 15 | TRN.ASWM packets are used to store the walkmesh information in TRN and TRX files. 16 | 17 | The packet itself is stored as is: 18 | 19 | | Type | Name | Description | 20 | | ---------------------------- | ---------------------- | ------------------------------------- | 21 | | `char[4]` | `data_type` | Always `COMP` for compressed walkmesh | 22 | | `uint32_t` | `compressed_data_size` | Size of `compressed_data` | 23 | | `uint32_t` | `uncomp_length` | Uncompressed walkmesh size | 24 | | `void[compressed_data_size]` | `compressed_data` | zlib-compressed walkmesh data | 25 | 26 | Uncompressed walkmesh data can be retrieved using `zlib.deflate`. The following structure information are for the uncompressed walkmesh data. 27 | 28 | ## TRN/TRX differences 29 | 30 | TRN.ASWM is slightly different weather it is stored in a TRX or TRN file. 31 | Generally, the TRX version is smaller as it has to be downloaded/loaded by the 32 | client, and contains the baked version of the walkmesh (with placeable 33 | walkmesh/walkmesh cutter alterations). 34 | 35 | - TRN version: 36 | + The whole map is stored, but no information on placeable walkmeshes nor 37 | walkmesh cutters 38 | + Triangle `clockwise` flag is not set 39 | + Contains map border geometry 40 | + Path tables & island list are empty 41 | - TRX version: 42 | + Baked counterpart of the TRN version 43 | + Map borders are not stored, only the center. 44 | + Triangles cut by a "walkmesh cutter" are removed (creates holes in the 45 | mesh) 46 | + Placeable walkmesh modifications appears 47 | + Path tables & island list are populated 48 | 49 | # Data blocks 50 | 51 | 52 | | Type | Name | Description | 53 | | ------------------------------------------------------------------------------------------ | -------------------- | --------------------------------- | 54 | | [`Header`](#header-struct-53-bytes) | `header` | | 55 | | [`Vertex[header.vertices_count]`](#vertex-struct-12-bytes) | `vertices` | | 56 | | [`Edge[header.edges_count]`](#edge-struct-16-bytes) | `edges` | | 57 | | [`Triangle[header.triangles_count]`](#triangle-struct-64-bytes) | `triangles` | | 58 | | [`TilesHeader`](#tilesheader-struct-20-bytes) | `tiles_header` | | 59 | | [`Tile[tiles_header.grid_height * tiles_header.grid_width]`](#tile-struct-variable-length) | `tiles` | | 60 | | `uint32_t` | `tiles_border_size` | Width of the map borders in tiles | 61 | | `uint32_t` | `islands_length` | Length of `islands` | 62 | | [`Island[islands_length]`](#island-struct-variable-length) | `islands` | | 63 | | [`IslandPathNode[islands_length * islands_length]`](#islandpathnode-struct-8-btyes) | `islands_path_nodes` | Pathfinding data | 64 | 65 | 66 | # Structures 67 | 68 | ## Header struct (53 bytes) 69 | 70 | | Type | Name | Description | 71 | | ---------- | ------------------ | -------------------- | 72 | | `uint32_t` | `version` | | 73 | | `char[32]` | `name` | | 74 | | `uint8_t` | `owns_data` | | 75 | | `uint32_t` | `vertices_count` | | 76 | | `uint32_t` | `edges_count` | | 77 | | `uint32_t` | `triangles_count` | | 78 | | `uint32_t` | `triangles_offset` | Seems to be always 0 | 79 | 80 | 81 | ## Vertex struct (12 bytes) 82 | | Type | Description | 83 | | ---------- | ------------------------------- | 84 | | `float[3]` | x/y/z coordinates of the vertex | 85 | 86 | 87 | ## Edge struct (16 bytes) 88 | 89 | | Type | Description | 90 | | ------------- | ----------------------------------------------------------- | 91 | | `uint32_t[2]` | Vertex indices | 92 | | `uint32_t[2]` | Attached triangle indices. -1 if a triangle does not exist. | 93 | 94 | ##### Notes 95 | 96 | - The struct defines a edge between two triangles, with the shared edge. 97 | - Every triangle has 3 Edge structs. 98 | - Probably only used to ease path tables generation 99 | - TODO: I should try remove all Edges from a baked trx and see what happens 100 | 101 | ## Triangle struct (64 bytes) 102 | 103 | | Type | Name | Description | 104 | | ------------- | ------------------ | --------------------------------------------------------------------------------------------------- | 105 | | `uint32_t[3]` | `vertices` | Vertex indices composing the triangle | 106 | | `uint32_t[3]` | `linked_edges` | Edge indices corresponding to this triangle edges | 107 | | `uint32_t[3]` | `linked_triangles` | Triangle indices that have an edge in common with this triangle. -1 is there is no triangle on one. | 108 | | `float[2]` | `center` | Triangle center x/y coordinates | 109 | | `float[3]` | `normal` | Normal vector | 110 | | `float` | `dot` | Dot product at plane | 111 | | `uint16_t` | `island` | -1 if the triangle is non walkable, else it is an island index | 112 | | `uint16_t` | `flags` | Walkmesh flags | 113 | 114 | - `linked_triangles[i]` and `linked_edges[i]` must share the same edge 115 | 116 | ##### Triangle flags 117 | - `walkable = 0x01`: if the triangle can be walked on 118 | - `clockwise = 0x04`: vertices are wound clockwise and not ccw 119 | - `dirt = 0x08`: Floor type (for sound effects) 120 | - `grass = 0x10`: Floor type (for sound effects) 121 | - `stone = 0x20`: Floor type (for sound effects) 122 | - `wood = 0x40`: Floor type (for sound effects) 123 | - `carpet = 0x80`: Floor type (for sound effects) 124 | - `metal = 0x100`: Floor type (for sound effects) 125 | - `swamp = 0x200`: Floor type (for sound effects) 126 | - `mud = 0x400`: Floor type (for sound effects) 127 | - `leaves = 0x800`: Floor type (for sound effects) 128 | - `water = 0x1000`: Floor type (for sound effects) 129 | - `puddles = 0x2000`: Floor type (for sound effects) 130 | 131 | ##### island & walkable flag 132 | - In TRN files: 133 | + Map border megatiles triangles have `island=-1` and walkable flag set to 0 134 | + Bakeable triangles (map center) have a valid `island` (ie: != -1), and the 135 | walkable flag set to 1/0 if the triangle is meant to be walkable/non 136 | walkable. 137 | - In TRX files: 138 | + Map borders are not stored 139 | + Non walkable triangles have `island=-1` and walkable flag set to 0 140 | + Triangles cut by a "walkmesh cutter" are not stored 141 | 142 | ##### Notes 143 | - For the raw map: 144 | + `unknownA` is always 0 (or -0) 145 | - For the icy peak map 146 | + -498.294 ==> 475.229 147 | 148 | 149 | ## TilesHeader struct (20 bytes) 150 | 151 | 152 | | Type | Name | Description | 153 | | ---------- | ------------- | ------------------------------------------------------------------------------------- | 154 | | `uint32_t` | `flags` | Always 31 in TRX files, 15 in TRN files | 155 | | `float` | `width` | Width in meters of a terrain tile (most likely to be 10.0) | 156 | | `uint32_t` | `grid_height` | Number of tiles along Y axis | 157 | | `uint32_t` | `grid_width` | Number of tiles along X axis | 158 | | `uint32_t` | `border_size` | Width of the map borders in tiles (8 means that 8 tiles will be removed on each side) | 159 | 160 | 161 | ## Tile struct (variable length) 162 | 163 | A tile is a square (generally 10 x 10 meters) containing mesh triangles. In TRX files, the tiles also contains pre-calculated pathfinding information. 164 | 165 | | Type | Name | Description | 166 | | ----------------------------------------------------- | ------------------- | ------------------------------------------------------------------------------------------ | 167 | | [`TileHeader`](#tileheader-struct-57-bytes) | `header` | | 168 | | [`Vertex[]`](#vertex-struct-12-bytes) | `vertices` | Unused by nwn2. Might be usable with `tile.header.owns_data == true` | 169 | | [`Edge[]`](#edge-struct-16-bytes) | `edges` | Unused by nwn2. Might be usable with `tile.header.owns_data == true` | 170 | | [`PathTableHeader`](#pathtableheader-struct-13-bytes) | `path_table_header` | | 171 | | `ubyte[]` | `local_to_node` | `local_to_node[localTriangleIndex]` is an index in `nodes` | 172 | | `uint32_t[]` | `node_to_local` | `node_to_local[nodeValue]` is a local triangle index | 173 | | `ubyte[]` | `nodes` | `nodes[node_to_local_length * fromLTNIndex + destLTNIndex]` is an index in `node_to_local` | 174 | | `uint32_t` | `flags` | Reuse value `aswm.tiles_header.tiles_flags` | 175 | 176 | - All triangles of a tile must have consecutive indices 177 | - The triangle owned by a tile can be retrieved using 178 | `aswm.triangles[header.triangles_offset .. header.triangles_offset + header.triangles_count]` 179 | - A tile must not re-use triangles owned by another tile (will crash NWN2) 180 | - TODO: I need to try making non-square tiles and see how the game handles it 181 | 182 | ### Tile path table 183 | 184 | This is a pre-calculated pathfinding table, that reference the next triangle to 185 | go to in order to reach any other triangle of the tile. 186 | 187 | This is how a path on a tile is calculated: 188 | 1. Get local triangle indices: 189 | + `fromLocIdx = fromGlobIdx - tile.header.triangles_offset` for the starting 190 | triangle 191 | + `destLocIdx = destGlobIdx - tile.header.triangles_offset` for the triangle 192 | you are trying to reach 193 | + If `fromLocIdx` or `destLocIdx` equals `255`, it means the triangle is not 194 | walkable 195 | 2. Get node indices: 196 | + `fromNodeIdx = tile.local_to_node[fromLocIdx]` 197 | + `destNodeIdx = tile.local_to_node[destLocIdx]` 198 | 3. Get the Node value 199 | + `node = tile.nodes[fromNodeIdx * header.node_to_local_length + 200 | destNodeIdx]` 201 | + `node == 255` can mean two things: 202 | * Destination cannot be reached, because one of the triangles is non- 203 | walkable or there is an obstacle that can't be get around without 204 | leaving the tile. 205 | * If `fromNodeIdx == destNodeIdx`, this means you already reached the 206 | destination triangle 207 | 4. Get the next local triangle index to go to in order to reach destination 208 | + `nextLocIdx = tile.node_to_local[node & 0b0111_1111]` 209 | 5. Loop on 2. with `fromNodeIdx = nextLocIdx`, until `nextLocIdx == destNodeIdx` 210 | 211 | [](trn.aswm.path_tables.svg) 212 | 213 | ##### Notes: 214 | 215 | - `nodes[i] & 0b1000_0000` is a bit flag for if there is a clear line of sight 216 | between the two triangle. It's not clear what LOS is since two linked 217 | triangles on flat ground may not have LOS = 1 in game files. A value of 0 218 | means the game will have to calculate LOS in real time. 219 | 220 | 221 | 222 | ### TileHeader struct (57 bytes) 223 | 224 | | Type | Name | Description | 225 | | ---------- | ------------------ | ---------------------------------------------------------------- | 226 | | `char[32]` | `name` | Sometime contains garbage, sometime contains only null values | 227 | | `ubyte` | `owns_data` | 1 if the tile stores vertices / edges. NWN2 always set this to 0 | 228 | | `uint32_t` | `vertices_count` | Number of vertices in this tile | 229 | | `uint32_t` | `edges_count` | Number of edges in this tile | 230 | | `uint32_t` | `triangles_count` | Number of triangles in this tile (walkable + unwalkable) | 231 | | `float` | `size_x` | TODO: Always 0? | 232 | | `float` | `size_y` | TODO: Always 0? | 233 | | `uint32_t` | `triangles_offset` | | 234 | 235 | 236 | 237 | ### PathTableHeader struct (13 bytes) 238 | 239 | | Type | Name | Description | 240 | | ---------- | ---------------------- | ------------------------------------------------------------------------------------------ | 241 | | `uint32_t` | `compression_flags` | Always 0. Note: it's pointless to compress path table since the aswm is already compressed | 242 | | `uint32_t` | `local_to_node_length` | Length of `local_to_node` array | 243 | | `ubyte` | `node_to_local_length` | Length of `node_to_local` array | 244 | | `uint32_t` | `rle_table_size` | Always 0. | 245 | 246 | - `compression_flags` values: `rle = 1`, `zcompress = 2` 247 | 248 | 249 | 250 | ## Island struct (variable length) 251 | 252 | An island is a fraction of a tile, where all walkable triangles can be reached 253 | without leaving the tile. This is a pathfinding related struct that is not 254 | available in TRN files. 255 | 256 | | Type | Name | Description | 257 | | ----------------------------------------------- | ---------------------------- | ---------------------------------------- | 258 | | [`IslandHeader`](#islandheader-struct-24-bytes) | `header` | | 259 | | `uint32_t` | `linked_islands_length` | Length of `linked_islands` | 260 | | `uint32_t[]` | `linked_islands` | Island indices linked to this one | 261 | | `uint32_t` | `linked_islands_dist_length` | Length of `linked_islands_dist` | 262 | | `float[]` | `linked_islands_dist` | Distance to linked islands | 263 | | `uint32_t` | `linked_islands_exit_length` | Length of `linked_islands_exit` | 264 | | `uint32_t[]` | `linked_islands_exit` | Exit triangles leading to linked islands | 265 | 266 | - `linked_islands_dist[i]` and `linked_islands_exit[i]` are the distance & exit 267 | to `linked_islands[i]` 268 | - I don't see the point of storing 3 times the same length. Maybe the nwn2 devs 269 | did not have time clean this. 270 | 271 | 272 | ### IslandHeader struct (24 bytes) 273 | 274 | | Type | Name | Description | 275 | | ---------- | ----------------- | ---------------------------------------------------------------------------------------------------------- | 276 | | `uint32_t` | `index` | Index of the island in the `aswm.islands` array | 277 | | `uint32_t` | `tile` | Value looks pretty random, but is identical for all islands. TODO: try setting it to associated tile index | 278 | | `float[3]` | `center` | Center of the island. Z is always 0 | 279 | | `uint32_t` | `triangles_count` | Number of triangles in this island | 280 | 281 | 282 | 283 | 284 | ## IslandPathNode struct (8 bytes) 285 | 286 | Pathfinding across islands works very similarly to [Tile path table](#tile-path-table). 287 | 288 | `aswm.islands_path_nodes[fromIslandIdx * aswm.islands_length + toIslandIdx]` is 289 | the next island to go to in order to reach `toIslandIdx` from `fromIslandIdx` 290 | 291 | | Type | Name | Description | 292 | | ---------- | ---------- | --------------------------- | 293 | | `uint16_t` | `next` | Next island to go to | 294 | | `uint16_t` | `_padding` | unused | 295 | | `float` | `weight` | Distance to the next island | -------------------------------------------------------------------------------- /specs/trn/trn.md: -------------------------------------------------------------------------------- 1 | # TRN file format 2 | 3 | Used for .trx and .trn files. 4 | 5 | ## Header struct (12 bytes) 6 | 7 | | Type | Name | Description | 8 | | ---------- | --------------- | ----------------- | 9 | | `char[4]` | `file_type` | Always `NWN2 ` | 10 | | `uint16_t` | `version_major` | | 11 | | `uint16_t` | `version_minor` | | 12 | | `uint32_t` | `packet_count` | Number of packets | 13 | 14 | ## PacketKeyTable struct (8 bytes) 15 | 16 | | Type | Name | Description | 17 | | ---------- | -------- | -------------------------------------------------------- | 18 | | `char[4]` | `type` | Packet type name | 19 | | `uint32_t` | `offset` | Byte offset of the packet from the beginning of the file | 20 | 21 | 22 | ## Packet struct (variable length) 23 | 24 | | Type | Name | Description | 25 | | --------------- | ------ | ------------------------- | 26 | | `char[4]` | `type` | Packet type name | 27 | | `uint32_t` | `size` | Packet data size in bytes | 28 | | `uint8_t[size]` | `data` | Packet data | 29 | 30 | Each packet contains different data depending on their `type` 31 | 32 | There are 4 types of packets: 33 | - `TRWH`: Terrain Width Height info 34 | - `TRRN`: Terrain geometry info 35 | - [`ASWM`](trn.aswm.md): Walkmesh geometry & pathfinding data 36 | - `WATR`: Water geometry info 37 | 38 | See [Tanita's specs](trn_tanita.pdf) for information about `TRWH`, `TRRN` and `WATR` packets. 39 | -------------------------------------------------------------------------------- /specs/trn/trn_tanita.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xoreos/xoreos-docs/4e1c197aa09b532ef466ff8ceccfd6221e80c3c9/specs/trn/trn_tanita.pdf -------------------------------------------------------------------------------- /templates/COPYING: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | -------------------------------------------------------------------------------- /templates/JadeMDL.bt: -------------------------------------------------------------------------------- 1 | //-------------------------------------- 2 | //--- 010 Editor v6.0.2 Binary Template 3 | // 4 | // File: JadeMDL.bt 5 | // Author: Enrico Horn (Farmboy0) 6 | // Revision: 0.2 7 | // Purpose: to map the model format of the jade engine from bioware 8 | // 9 | // To the extent possible under law, the authors have dedicated all copyright 10 | // and related and neighboring rights to this document to the public domain 11 | // worldwide. This document is distributed without any warranty. 12 | // 13 | // You should have received a copy of the CC0 Public Domain Dedication along 14 | // with this document. 15 | // If not, see