├── .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 . 42 | -------------------------------------------------------------------------------- /specs/bioware/2DA_Format.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xoreos/xoreos-docs/4e1c197aa09b532ef466ff8ceccfd6221e80c3c9/specs/bioware/2DA_Format.pdf -------------------------------------------------------------------------------- /specs/bioware/AreaFile_Format.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xoreos/xoreos-docs/4e1c197aa09b532ef466ff8ceccfd6221e80c3c9/specs/bioware/AreaFile_Format.pdf -------------------------------------------------------------------------------- /specs/bioware/CommonGFFStructs.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xoreos/xoreos-docs/4e1c197aa09b532ef466ff8ceccfd6221e80c3c9/specs/bioware/CommonGFFStructs.pdf -------------------------------------------------------------------------------- /specs/bioware/Conversation_Format.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xoreos/xoreos-docs/4e1c197aa09b532ef466ff8ceccfd6221e80c3c9/specs/bioware/Conversation_Format.pdf -------------------------------------------------------------------------------- /specs/bioware/Creature_Format.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xoreos/xoreos-docs/4e1c197aa09b532ef466ff8ceccfd6221e80c3c9/specs/bioware/Creature_Format.pdf -------------------------------------------------------------------------------- /specs/bioware/DoorPlaceableGFF.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xoreos/xoreos-docs/4e1c197aa09b532ef466ff8ceccfd6221e80c3c9/specs/bioware/DoorPlaceableGFF.pdf -------------------------------------------------------------------------------- /specs/bioware/ERF_Format.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xoreos/xoreos-docs/4e1c197aa09b532ef466ff8ceccfd6221e80c3c9/specs/bioware/ERF_Format.pdf -------------------------------------------------------------------------------- /specs/bioware/Encounter_Format.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xoreos/xoreos-docs/4e1c197aa09b532ef466ff8ceccfd6221e80c3c9/specs/bioware/Encounter_Format.pdf -------------------------------------------------------------------------------- /specs/bioware/Faction_Format.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xoreos/xoreos-docs/4e1c197aa09b532ef466ff8ceccfd6221e80c3c9/specs/bioware/Faction_Format.pdf -------------------------------------------------------------------------------- /specs/bioware/GFF_Format.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xoreos/xoreos-docs/4e1c197aa09b532ef466ff8ceccfd6221e80c3c9/specs/bioware/GFF_Format.pdf -------------------------------------------------------------------------------- /specs/bioware/IFO_Format.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xoreos/xoreos-docs/4e1c197aa09b532ef466ff8ceccfd6221e80c3c9/specs/bioware/IFO_Format.pdf -------------------------------------------------------------------------------- /specs/bioware/Item_Format.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xoreos/xoreos-docs/4e1c197aa09b532ef466ff8ceccfd6221e80c3c9/specs/bioware/Item_Format.pdf -------------------------------------------------------------------------------- /specs/bioware/Journal_Format.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xoreos/xoreos-docs/4e1c197aa09b532ef466ff8ceccfd6221e80c3c9/specs/bioware/Journal_Format.pdf -------------------------------------------------------------------------------- /specs/bioware/KeyBIF_Format.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xoreos/xoreos-docs/4e1c197aa09b532ef466ff8ceccfd6221e80c3c9/specs/bioware/KeyBIF_Format.pdf -------------------------------------------------------------------------------- /specs/bioware/LocalizedStrings_Format.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xoreos/xoreos-docs/4e1c197aa09b532ef466ff8ceccfd6221e80c3c9/specs/bioware/LocalizedStrings_Format.pdf -------------------------------------------------------------------------------- /specs/bioware/PaletteITP_Format.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xoreos/xoreos-docs/4e1c197aa09b532ef466ff8ceccfd6221e80c3c9/specs/bioware/PaletteITP_Format.pdf -------------------------------------------------------------------------------- /specs/bioware/README: -------------------------------------------------------------------------------- 1 | BioWare's Aurora specs: 2 | 3 | Copyright of these files is held by BioWare corp. 4 | -------------------------------------------------------------------------------- /specs/bioware/SSF_Format.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xoreos/xoreos-docs/4e1c197aa09b532ef466ff8ceccfd6221e80c3c9/specs/bioware/SSF_Format.pdf -------------------------------------------------------------------------------- /specs/bioware/SoundObject_Format.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xoreos/xoreos-docs/4e1c197aa09b532ef466ff8ceccfd6221e80c3c9/specs/bioware/SoundObject_Format.pdf -------------------------------------------------------------------------------- /specs/bioware/Store_Format.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xoreos/xoreos-docs/4e1c197aa09b532ef466ff8ceccfd6221e80c3c9/specs/bioware/Store_Format.pdf -------------------------------------------------------------------------------- /specs/bioware/TalkTable_Format.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xoreos/xoreos-docs/4e1c197aa09b532ef466ff8ceccfd6221e80c3c9/specs/bioware/TalkTable_Format.pdf -------------------------------------------------------------------------------- /specs/bioware/Trigger_Format.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xoreos/xoreos-docs/4e1c197aa09b532ef466ff8ceccfd6221e80c3c9/specs/bioware/Trigger_Format.pdf -------------------------------------------------------------------------------- /specs/bioware/Waypoint_Format.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xoreos/xoreos-docs/4e1c197aa09b532ef466ff8ceccfd6221e80c3c9/specs/bioware/Waypoint_Format.pdf -------------------------------------------------------------------------------- /specs/foxpro/README.md: -------------------------------------------------------------------------------- 1 | # Foxpro database format 2 | 3 | File format for `.cdx`, `.dbf`, `.fpt` files. 4 | 5 | Used for NWN2 (and eventually other games) `SetCampaign(Int|Float|Object|...)` script functions. 6 | -------------------------------------------------------------------------------- /specs/foxpro/cdx.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xoreos/xoreos-docs/4e1c197aa09b532ef466ff8ceccfd6221e80c3c9/specs/foxpro/cdx.pdf -------------------------------------------------------------------------------- /specs/foxpro/dbf.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xoreos/xoreos-docs/4e1c197aa09b532ef466ff8ceccfd6221e80c3c9/specs/foxpro/dbf.pdf -------------------------------------------------------------------------------- /specs/foxpro/fpt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xoreos/xoreos-docs/4e1c197aa09b532ef466ff8ceccfd6221e80c3c9/specs/foxpro/fpt.pdf -------------------------------------------------------------------------------- /specs/foxpro/idx.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xoreos/xoreos-docs/4e1c197aa09b532ef466ff8ceccfd6221e80c3c9/specs/foxpro/idx.pdf -------------------------------------------------------------------------------- /specs/foxpro/idx2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xoreos/xoreos-docs/4e1c197aa09b532ef466ff8ceccfd6221e80c3c9/specs/foxpro/idx2.pdf -------------------------------------------------------------------------------- /specs/foxpro/macro.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xoreos/xoreos-docs/4e1c197aa09b532ef466ff8ceccfd6221e80c3c9/specs/foxpro/macro.pdf -------------------------------------------------------------------------------- /specs/foxpro/table.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xoreos/xoreos-docs/4e1c197aa09b532ef466ff8ceccfd6221e80c3c9/specs/foxpro/table.pdf -------------------------------------------------------------------------------- /specs/nds/articulation.htm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xoreos/xoreos-docs/4e1c197aa09b532ef466ff8ceccfd6221e80c3c9/specs/nds/articulation.htm -------------------------------------------------------------------------------- /specs/nds/sdat.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |

Nitro Composer File (*.sdat) Specification

5 | 6 |

Current status

7 |

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 |

Acknowledgement

15 | Many thanks to the following persons whose works I have studied: 16 |

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 Contents

26 |
27 |

0. Introduction 28 |

32 |

1. SDAT File Format 33 |

40 |

2. SSEQ File Format

41 |

3. SSAR File Format

42 |

4. SBNK File Format

43 |

5. SWAV File Format

44 |

6. SWAR File Format

45 |

7. STRM File Format

46 | 47 | 48 | 49 | 50 | 51 |
52 |

0. Introduction

53 |
54 | 55 |

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

0.1 Terminology

64 | File format is explained in C-style struct declaration. 65 | The following types of variable are used: 66 |
 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 |

0.2 NDS Standard File Header

76 | Many files (besides sound-related) found in DS game rom share this header structure: 77 | 78 |
 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 Format

93 |
94 | 95 | The file has the following structure: 96 | 97 |
 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 |

1.1 Header

112 | The Header appears at offset 0 in the SDAT file. All offsets in this structure are absolute offsets. 113 | 114 |
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 |

1.2 Symbol Block

137 | It appears at offset 0x40, right after the Header. It may be omitted. It contains the symbols (or "filenames") of each sound file in the SDAT file. All offsets are relative to this block's starting address (i.e. 0x40).
138 | NB. Some files doesn't have Symbol Block.
139 | NB. The value of nSize of Symbol Block may not be 32-bit aligned. However, the value of nSymbSize in Header is. 140 | 141 |
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 |

1.2.1 Symbol Block - Record

154 | There are a total of 8 records in the Symbol Block. They are: 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 |
Record No.Record NameDescription
0SEQSequence (for music)
1SEQARCSequence Archive (for sound effect)
2BANKSound Bank
3WAVEARCWave Archive
4PLAYER*Player (Group-related)
5GROUPGroup of SEQ/SEQARC/BANK/WAVEARC
6PLAYER2*Player2 (Stream-related)
7STRMStream
* 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>>
170 |
171 | All offsets are relative to Symbol block's starting address (i.e. 0x40). Each record (except Record 1 "SEQARC") has the following structure: 172 | 173 |
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 |

1.2.2 Symbol Block - Entry

231 | EXCEPT for Record 1 "SEQARC", an Entry in the record is a null terminated string. This corresponds to the "filename" of a sound file in the SDAT file. 232 |

233 | For Record 1 "SEQARC", since a SEQARC file is a collection of Sequence files, therefore this record contains a sub-record. And this sub-record contains the symbols ("filenames") of each of the archived SEQ files. 234 | 235 | 236 |

1.3 Info Block

237 | The Info Block appears just after the Symbol Block. It contains some information of each sound file in the SDAT file. All offsets are relative to this block's starting address. 238 | 239 |
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 |

1.3.1 Info Block - Record

250 | There are a total of 8 records in the Info Block. The Record Names in 1.2.1 above applies here as well. All offsets are relative to Info block's starting address. With modifications, the code example above could be used to access the Info records and entries. 251 | 252 |
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 |

1.3.2 Info Block - Entry

262 | 263 |

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 |

279 | Record 1 "SEQARC" 280 | 281 |
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 |

292 | Record 2 "BANK" 293 | 294 |
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 |

306 | Record 3 "WAVEARC" 307 | 308 |
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 |

319 | Record 4 "PLAYER" 320 | 321 |
322 | typedef struct tagSDATInfoPlayer
323 | {
324 | 	u8  unknown;
325 | 	u8  padding[3];
326 | 	u32 unknown2;
327 | } SDATINFOPlayer;
328 | 
329 | 330 | Remarks: None 331 | 332 |

333 | Record 5 "GROUP" 334 | 335 |
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 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 |
ValueType
0x0700SEQ
0x0803SEQARC
0x0601BANK
0x0402WAVEARC
357 | 358 | 359 |

360 | Record 6 "PLAYER2" 361 | 362 |
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 |

374 | Record 7 "STRM" 375 | 376 |
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 |

1.4 FAT

391 | The FAT appears just after the Info Block. It contains the records of offset and size of each sound file in the SDAT file. 392 | 393 |
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 |

1.4.1 FAT - Record

404 | It contains the offset and size of the sound file. All the offsets are relative to the SDAT Header structure's beginning address. 405 | 406 |
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 |

1.5 File Block

416 | The File Block is the last block and appears just after the FAT. It has a small header (the structure below) which contains the total size and number of sound of files. All the sound files are stored after this structure. 417 | 418 |
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 Format

434 |
435 | 436 |

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 |

2.1 Description

461 |

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 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 |
cyclevariableaction
10Add 160
2160Add 160
3320Subtract 240, process once, add 160
4240Subtract 240, process once, add 160
5160Add 160
6320Subtract 240, process once, add 160
7240Subtract 240, process once, add 160
8160Add 160
479 | 480 |

2.2 Events

481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 |
Status ByteParameterDescription
0xFE2 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.
0x934 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.
0x00 .. 0x7fVelocity: 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.
0x80Duration: Variable LengthREST. It tells the SSEQ-sequencer to wait for a certain tick. Usually it is a multiple of 3.
0x81Bank & Program Number: Variable Lengthbits[0..7] is the program number, bits[8..14] is the bank number. Bank change is seldomly found, so usually bank 0 is used.
0x94Destination 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 |
0x95Call 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.
0xFDNONERETURN. The SSEQ will return to the caller's address + 4 (a Call event is 4 bytes in size).
0xA0 .. 0xBfSee loveemu's sseq2mid for more details.Some arithmetic operations / comparions. Affect how SSEQ is to be played.
0xC0Pan Value: 1 byte [0..127], middle is 64PAN
0xC1Volume Value: 1 byte [0..127]VOLUME
0xC2Master Volume Value: 1 byte [0..127]MASTER VOLUME
0xC3Value: 1 byte [0..64] (Add 64 to make it a MIDI value)TRANSPOSE (Channel Coarse Tuning)
0xC4Value: 1 bytePITCH BEND
0xC5Value: 1 bytePITCH BEND RANGE
0xC6Value: 1 byteTRACK PRIORITY
0xC7Value: 1 byte [0: Poly, 1: Mono]MONO/POLY
0xC8Value: 1 byte [0: Off, 1: On]TIE (unknown)
0xC9Value: 1 bytePORTAMENTO CONTROL
0xCAValue: 1 byte [0: Off, 1: On]MODULATION DEPTH
0xCBValue: 1 byteMODULATION SPEED
0xCCValue: 1 byte [0: Pitch, 1: Volume, 2: Pan]MODULATION TYPE
0xCDValue: 1 byteMODULATION RANGE
0xCEValue: 1 bytePORTAMENTO ON/OFF
0xCFTime: 1 bytePORTAMENTO TIME
0xD0Value: 1 byteATTACK RATE
0xD1Value: 1 byteDECAY RATE
0xD2Value: 1 byteSUSTAIN RATE
0xD3Value: 1 byteRELEASE RATE
0xD4Count: 1 byte (how many times to be looped)LOOP START MARKER
0xFCNONELOOP END MARKER
0xD5Value: 1 byteEXPRESSION
0xD6Value: 1 bytePRINT VARIABLE (unknown)
0xE0Value: 2 byteMODULATION DELAY
0xE1BPM: 2 byteTEMPO
0xE3Value: 2 byteSWEEP PITCH
0xFFNONEEOT: End Of Track
600 | 601 |

602 | 603 | 604 | 605 |
606 |

3. SSAR File Format

607 |
608 | 609 | 610 | SSAR stands for "(Sound) Sequence Archive". It is a collection of sequences (used mainly for sound effect). Therefore, each archived SSEQ is usually short, with one or two notes. 611 | 612 | 613 |
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 Format

651 |
652 | 653 |

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 |

4.1 Instrument Record

688 | 689 |

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.

736 | 737 |

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 |

4.2 Articulation Data

772 |

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 Format

805 |
806 | 807 | 808 |

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 Format

846 |
847 | 848 | SWAR stands for "(Sound) Wave Archive". It is a collection of mono wave (SWAV) samples only (which can be in either PCM8, PCM16 or ADPCM compression). 849 | 850 |
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 Format

877 |
878 | 879 | STRM stands for "Stream". It is an individual mono/stereo wave file (PCM8, PCM16 or ADPCM). 880 | 881 |
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 |

7.1 Wave Data

919 | 920 |

A Block is the same as SWAV Wave Data.

921 | 922 |

Mono (SWAV)

923 | Block 1
924 | Block 2
925 | ...
926 | Block N (Last Block)
927 | 928 | 929 |

Stereo (STRM)

930 | Block 1 L
931 | Block 1 R
932 | Block 2 L
933 | Block 2 R
934 | ...
935 | Block N L (Last Block)
936 | 937 | Block N R (Last Block) 938 | 939 | 940 | 941 | -------------------------------------------------------------------------------- /specs/torlack/README: -------------------------------------------------------------------------------- 1 | Torlack's reversed specs, used to be available at 2 | -------------------------------------------------------------------------------- /specs/torlack/basics.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | NWN Data File Basics 9 | 10 | 11 | 12 | 13 |

NWN Data File Basics

14 | 15 |

It 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 |
    22 |
  • C:\NeverwinterNights\NWN - Game executables and key data files
  • 23 |
  • C:\NeverwinterNights\NWN\ambient - Ambient sounds
  • 24 |
  • C:\NeverwinterNights\NWN\data - Primary data files
  • 25 |
  • C:\NeverwinterNights\NWN\dmvault - Dungeon master characters
  • 26 |
  • C:\NeverwinterNights\NWN\hak - Hak packs to augment game data
  • 27 |
  • C:\NeverwinterNights\NWN\localvault - Your local characters
  • 28 |
  • C:\NeverwinterNights\NWN\modules - User created modules
  • 29 |
  • C:\NeverwinterNights\NWN\movies - Cut scene and introduction movies
  • 30 |
  • C:\NeverwinterNights\NWN\music - Game music files
  • 31 |
  • C:\NeverwinterNights\NWN\nwm - NWN game modules
  • 32 |
  • C:\NeverwinterNights\NWN\override - Collection of resources that replace 33 | resources contained in the "data" directory.
  • 34 |
  • C:\NeverwinterNights\NWN\saves - Saved games
  • 35 |
  • C:\NeverwinterNights\NWN\servervault - Server characters for multiplayer 36 | games run on your computer
  • 37 |
  • C:\NeverwinterNights\NWN\texturepacks - Texture packs
  • 38 |
39 |

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 |

Resource Types

76 | 77 |

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 |
82 |
83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 |
Resource NameResource Type
RES0x0000
BMP0x0001
MVE0x0002
TGA0x0003
WAV0x0004
PLT0x0006
INI0x0007
BMU0x0008
MPG0x0009
TXT0x000A
PLH0x07D0
TEX0x07D1
MDL0x07D2
THG0x07D3
FNT0x07D5
LUA0x07D7
SLT0x07D8
NSS0x07D9
NCS0x07DA
MOD0x07DB
ARE0x07DC
SET0x07DD
IFO0x07DE
BIC0x07DF
WOK0x07E0
2DA0x07E1
TLK0x07E2
TXI0x07E6
GIT0x07E7
BTI0x07E8
UTI0x07E9
BTC0x07EA
UTC0x07EB
DLG0x07ED
ITP0x07EE
BTT0x07EF
UTT0x07F0
DDS0x07F1
UTS0x07F3
LTR0x07F4
GFF0x07F5
FAC0x07F6
BTE0x07F7
UTE0x07F8
BTD0x07F9
UTD0x07FA
BTP0x07FB
UTP0x07FC
DTF0x07FD
GIC0x07FE
GUI0x07FF
CSS0x0800
CCS0x0801
BTM0x0802
UTM0x0803
DWK0x0804
PWK0x0805
BTG0x0806
UTG0x0807
JRL0x0808
SAV0x0809
UTW0x080A
4PC0x080B
SSF0x080C
HAK0x080D
NWM0x080E
BIK0x080F
PTM0x0811
PTT0x0812
ERF0x270D
BIF0x270E
KEY0x270F
161 |
162 |
163 |

Languages

164 |

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 |
168 |
169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 |
LanguageID
English0
French, Male2
French, Female3
German, Male4
German, Female5
Italian, Male6
Italian, Female7
Spanish, Male8
Spanish, Female9
184 |
185 |
186 |

 

187 | 188 | 189 | -------------------------------------------------------------------------------- /specs/torlack/bif.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | What are Module File 4 | 5 | 6 | 7 | 8 | 9 | 10 |

What are BIF Files?

11 |

BIF files contain the actual data files referenced in KEY files.

12 |

BIF File Header

13 |

14 | The BIF file begins with a header.

15 |
16 |
17 | 18 | 19 | 21 | 23 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 |
20 | Offset 22 | Type 24 | Description
0x0000CHAR [4]File type signature (usually "BIFF")
0x0004CHAR [4]Version number (usually "V1  ")
0x0008UINT32Number of resources in the file
0x000CUINT32 45 | Unknown value
0x0010UINT32Offset from the start of file to the first resource structure
0x0014Total size of the structure
57 |
58 |
59 |

Resource Structures

60 |

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 |
65 |
66 | 67 | 68 | 70 | 72 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 |
69 | Offset 71 | Type 73 | Description
0x0000UINT32Key file ID of the resource
0x0004UINT32Offset from the start of the file to the given resource
0x0008UINT32Length of the resource in bytes
0x000CUINT32Type of the resource
0x0010Total size of the structure
100 |
101 |
102 | 103 | 104 | -------------------------------------------------------------------------------- /specs/torlack/itp.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | What is an ITP File 4 | 5 | 6 | 7 | 8 | 9 | 10 |

What is an ITP File?

11 |

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

Data Stored in an ITP File

25 |

ITP files contain seven sections of data.  The sections are as follows:

26 |
    27 |
  1. 28 | File Header 29 | - Gives us enough information to begin decoding the remaining sections. 30 |
  2. 31 | Entry Table 32 | - Provides us with our collections of variables and their nesting. 33 |
  3. 34 | Element Table 35 | - Provides the information about all the variables contained within an entry. 36 |
  4. 37 | Variable Names Table 38 | - List of all the different variables names. 39 |
  5. 40 | Variable Data Section 41 | -  Depending on the element in question, this data block can contain a 42 | assortment of data. 43 |
  6. 44 | Multiple Element Map (MultiMap) 45 | - Used to list which elements are contained within an entry when that entry 46 | contains more than one element. 47 |
  7. 48 | List Section - Provides a method for an element to have one or more 49 | entries as children.
  8. 50 |
51 |

The File Header

52 |
53 |
54 | 55 | 56 | 58 | 60 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 |
57 | Offset 59 | Type 61 | Description
0x0000char [4]4 byte signature.
0x0004char [4]4 byte version.
0x0008UINT32Offset from start of the file to the first entry
0x000CUINT32Number of entries in the file
0x0010UINT32Offset from start of the file to the first element
0x0014UINT32Number of elements in the file
0x0018UINT32Offset from start of the file to the first variable name
0x001CUINT32Number of names in the variable name block
0x0020UINT32Offset from start of the file to the first variable data
0x0024UINT32Number of bytes in the variable data block
0x0028UINT32Offset from start of the file to the first multimap
0x002CUINT32Number of bytes in the multimap table
0x0030UINT32Offset from start of the file to the first list
0x0034UINT32Number of bytes in the list table
0x0038Total length of the structure
138 |
139 |
140 |

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

146 |

The entity table is an array of structures of the following form:

147 |
148 |
149 | 150 | 151 | 153 | 155 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 |
152 | Offset 154 | Type 156 | Description
0x0000UINT32Entity code (Use unknown)
0x0004UINT32Element index or MultiMap offset
0x0008UINT32Number of elements in this entry
0x000CTotal length of the structure
178 |
179 |
180 |

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

There is one trick you can use to reduce duplicated code.  I use this trick 219 | in my software.

220 |
221 |
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 |
239 |

The Element Table 240 |

241 |

The element table is an array of structures of the following form: 242 |

243 |
244 |
245 | 246 | 247 | 249 | 251 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 |
248 | Offset 250 | Type 252 | Description
0x0000UINT32Variable type (0-15)
0x0004UINT32Variable name index
0x0008VARIESData for the variable
0x000CTotal length of the structure
274 |
275 |
276 |

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 |
285 |
286 | 287 | 288 | 290 | 292 | 294 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 |
289 | Value 291 | Type 293 | Access 295 | Data
0UINT8DirectUnsigned byte
1INT8DirectSigned byte
2UINT16DirectUnsigned word
3INT16DirectSigned word 
4UINT32DirectUnsigned longword 
5INT32DirectSigned longword 
6UINT64IndirectUnsigned quadword 
7INT64IndirectSigned quadword
8FLOATIndirectFour byte floating point value
9DOUBLEIndirectEight byte floating point value
10STRINGIndirectCounted string
11RESREFIndirectCounted resource name
12STRREFComplexMultilingual capable string
13DATREFComplexCounted binary data 
14CAPREFComplexList of child elements
15LISTComplexList of child entries
394 |
395 |
396 |

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

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 |
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 element
436 |
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 |
452 |

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 |
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 element
466 |
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 |
483 |

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 |
489 |
490 | 491 | 492 | 494 | 496 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 |
493 | Offset 495 | Type 497 | Description
0x0000UINT32Number of bytes included in the STRREF not including this count.
0x0004INT32ID of the string in the in the dialog.tlk file.  If none, then -1.
0x0008UINT32Number of language specific strings.  This value is usually zero.
0x000CTotal length of the structure
519 |
520 |
521 |

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 |
529 |
530 | 531 | 532 | 534 | 536 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 558 | 559 |
533 | Offset 535 | Type 537 | Description
0x0000UINT32Language of the string (values unknown)
0x0004UINT32Number of bytes in the following string
0x0008CHAR []Variable length string
VARIESTotal length of the structure will always be 8 plus the length of 557 | the string
560 |
561 |
562 |

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 |
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 element
586 |
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 |
598 |

The Variable Name Table

599 |

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 |

The Remaining Tables

608 |

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 |

Credits

612 |

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 9 | 10 | 11 | 12 | 13 |

What is a KEY File?

14 | 15 |

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 |
24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 |
OffsetTypeDescription
0x0000char [4]4 byte signature ("KEY ")
0x0004char [4]4 byte version ("V1  ")
0x0008UINT32Number of BIF files referenced in the key file
0x000CUINT32Number of resources (RES) contained in the key file
0x0010UINT32Offset from the start of the file to the first BIF entry
0x0014UINT32Offset from the start of the file to the first RES entry
0x0018UINT32 [10]Unknown
0x0040Total Structure Size
71 |
72 |
73 |

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 |
76 |
77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 |
OffsetTypeDescription
0x0000UINT32Length of the BIF file in bytes
0x0004UINT32Offset from the start of the KEY file to the BIF file name
0x0008UINT16Length of the BIF file name in bytes.  Not including the 97 | terminating 0.
0x000AUINT16Unknown
0x000CTotal Structure Size
109 |
110 |
111 |

The final table lists all of the resources contained within the BIF 112 | files. 

113 |
114 |
115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 |
OffsetTypeDescription
0x0000char [16]Name of the resource
0x0010UINT16Type of the resource
0x0012UINT32BIF id of the resource
0x0016Total Structure Size
141 |
142 |
143 |

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 | What are Module File 9 | 10 | 11 | 12 | 13 |

Official BioWare Documentation

14 |

ERF Format Document: http://nwn.bioware.com/developers/erf.html
15 |
BioWare's "For Developers" site: http://nwn.bioware.com/developers/

16 | 17 |

What are ERF Files?

18 |

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 |

ERF File Header

27 |

Like almost all data files in NWN, the ERF file begins with a header.

28 |
29 |
30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 72 | 73 | 74 | 75 | 76 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 |
OffsetTypeDescription
0x0000CHAR [4]File type signature.  Depending on the file type, 40 | this value varies.
0x0004CHAR [4]Version number
0x0008UINT32Number of strings in the description
0x000CUINT32Total length of the strings in bytes
0x0010UINT32Number of resources in the file
0x0014UINT32Offset from the start of file to the first string
0x0018UINT32Offset from the start of file to the first resource 71 | structure
0x001CUINT32Offset from the start of file to the first position 77 | structure
0x0020UINT32Year the file was created with 1900 being 0
0x0024UINT32Day of the year the file was created with Jan 1st being 0
0x0028UINT32String 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
0x002CUINT32 [29]Spare values all initialized to 0
0x00A0Total size of the structure
107 |
108 |
109 |

Strings in the ERF Files

110 | 111 |

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 |
118 |
119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 |
OffsetTypeDescription
0x0000UINT32Language Identifier (See the document on data file 129 | basics)
0x0004UINT32Length of the string in characters
0x0008CHAR [*]String data
0x0008+LengthTotal size of the structure
146 |
147 |
148 | 149 |

Resource Structures

150 |

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 |
155 |
156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 |
OffsetTypeDescription
0x0000CHAR [16]Name of the resource
0x0010UINT32Index of the resource
0x0014UINT16Resource type (See the document on data file basics)
0x0016UINT16Reserved and zero
0x0018Total size of the structure
187 |
188 |
189 |

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 |

Position Structures

194 |

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 |
199 |
200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 |
OffsetTypeDescription
0x0000UINT32Offset from the start of the file to the given resource
0x0004UINT32Length of the resource in bytes
0x0008Total size of the structure
221 |
222 |
223 | 224 |

Strange Blank Data

225 |

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 | NWN Texture Palette File 9 | 10 | 11 | 12 | 13 |

What is a Texture Palette File (PLT)?

14 | 15 |

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 |
    24 |
  • Palette Group - An index value from 0 to 9 that selects which palette 25 | group is to be used (from PLT file).
  • 26 |
  • Palette Index  - An index value from 0 to 255 that selects which 27 | palette to use in a palette group (from the content creator).
  • 28 |
  • Color Index - An index value from 0 to 255 that selects a specific color 29 | from the given palette (from PLT file).
  • 30 |
  • Palette File - A file that contains the actual color values for each 31 | palette in a group (supplied by game).
  • 32 |
33 | 34 |

Palette Groups 

35 | 36 |

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 |
44 |
45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 |
NameGroup IndexUIPalette File
Skin0
Hair1
Metal 12
Metal 23
Cloth 14
Cloth 25
Leather 16
Leather 27
Tattoo 18
Tattoo 29
114 | 115 |
116 |
117 | 118 |

Texture Palette File Format

119 | 120 |

The PLT file beings with a fixed header that defines the attributes of the 121 | texture.  Following the header is pixel information.

122 |
123 |
124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 |
OffsetTypeDescription
0x0000char [4]4 byte signature ("PLT ")
0x0004char [4]4 byte version ("V1  ")
0x0008UINT32Unknown
0x000CUINT32Unknown
0x0010UINT32Width of the texture in pixels
0x0014UINT32Height of the texture in pixels
0x0018Total Structure Size
165 |
166 |
167 |

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 |
170 |
171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 |
OffsetTypeDescription
0x0000UINT8Color Index
0x0001UINT8Palette Group Index
0x0002Total Structure Size
192 |
193 |
194 |

Example

195 |

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 |
    205 |
  1. Using the palette group index for the pixel in the texture, get the 206 | palette index for that palette group as specified by the 10 values supplied 207 | by the content creator.
  2. 208 |
  3. The palette index corresponds to a row in the palette file for given 209 | palette group.  This row become our palette for the pixel.
  4. 210 |
  5. Take the color index for the pixel in question and retrieve the color from 211 | the selected palette.
  6. 212 |
213 |

Credits

214 |

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?sanitize=true)](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 . 16 | // 17 | //------------------------------------------------------ 18 | // Colors: 19 | //------------------------------------------------------ 20 | // Dark Red : array of file pointers(not the array definition) 21 | // Black : array of strings(node names) 22 | 23 | // Light Blue : file header 24 | // Light Purple : geometry header 25 | 26 | // Aqua : model header without geometry header 27 | // Dark Aqua : animation incl. events array without geometry header 28 | 29 | // Light Green : node header 30 | // Light Blue : gob node without node header 31 | // Yellow : light node without node header 32 | // Dark Yellow : emitter node without node header 33 | // Orange : skin node without node header 34 | // Red : animation data node without node header 35 | // Gray : mesh node without node header 36 | //-------------------------------------- 37 | 38 | //------------------------------------------------------ 39 | // generic structures 40 | 41 | typedef struct { 42 | string s ; 43 | } strings ; 44 | 45 | typedef struct { 46 | float x; 47 | float y; 48 | float z; 49 | } vertex ; 50 | 51 | typedef struct { 52 | float w; 53 | float x; 54 | float y; 55 | float z; 56 | } quaternion ; 57 | 58 | typedef struct { 59 | uint32 p_array_start ; 60 | uint32 nr_used_entries; 61 | uint32 nr_alloc_entries; 62 | } array_definition ; 63 | 64 | typedef struct { 65 | array_definition def; 66 | if (def.nr_used_entries > 0) { 67 | local int pos = FTell(); 68 | 69 | GoToPointer(def.p_array_start); 70 | uint32 p_index[def.nr_used_entries] ; 71 | 72 | FSeek(pos); 73 | } 74 | } array; 75 | 76 | typedef struct { 77 | array array_info; 78 | 79 | local int pos = FTell(); 80 | 81 | local int i; 82 | for (i = 0; i < array_info.def.nr_used_entries; i++) { 83 | GoToPointer(array_info.p_index[i]); 84 | strings str; 85 | } 86 | 87 | FSeek(pos); 88 | } string_array; 89 | 90 | //------------------------------------------------------ 91 | // generic header structures 92 | 93 | typedef struct { 94 | uint32 bin_mdl_id ; 95 | uint32 mdl_size; 96 | uint32 mdx_size_vertexes; 97 | uint32 mdx_size_normals; 98 | uint32 unknown_length; 99 | } header_file ; 100 | 101 | void GoToPointer(uint32 p) { 102 | FSeek(sizeof(header_file) + p); 103 | } 104 | 105 | typedef struct { 106 | uint32 p_func1 ; 107 | uint32 p_func2 ; 108 | char model_name[32]; 109 | uint32 p_node_header ; 110 | uint32 count_nodes; 111 | array_definition unknown1; 112 | array_definition unknown2; 113 | uint32 ref_count; 114 | ubyte type; 115 | ubyte padding[3]; 116 | } header_geometry ; 117 | 118 | typedef struct { 119 | header_geometry geometry; 120 | uint32 field_50 ; 121 | uint32 count_child_model; 122 | array animations; 123 | uint32 p_supermodel ; 124 | vertex bound_min; 125 | vertex bound_max; 126 | float model_radius; 127 | uint32 field_84; 128 | float scale; 129 | char supermodel_name[32]; 130 | uint32 p_node1 ; 131 | uint32 field_B0; 132 | uint32 field_B4; 133 | uint32 field_B8; 134 | uint32 p_mdx ; 135 | string_array names; 136 | uint32 field_CC; 137 | array unknown_node_array; 138 | } header_model ; 139 | 140 | //------------------------------------------------------ 141 | // general node structures 142 | 143 | enum controller_type_all { 144 | position=8, 145 | orientation=20, 146 | scale=36 147 | }; 148 | enum controller_type_light { 149 | light_position=8, 150 | light_orientation=20, 151 | light_scale=36, 152 | light_color=76, 153 | light_radius=88, 154 | light_radius_shadow=96, 155 | light_vert_displacement=100, 156 | light_multiplier=140 157 | }; 158 | enum controller_type_mesh { 159 | mesh_position=8, 160 | mesh_orientation=20, 161 | mesh_scale=36, 162 | mesh_self_illum_color=100, 163 | mesh_unknown=132 164 | }; 165 | 166 | typedef struct { 167 | uint32 has_header : 1; // 1 168 | uint32 has_light : 1; // 2 169 | uint32 has_emitter : 1; // 4 170 | uint32 has_camera: 1; // 8 171 | uint32 has_reference : 1; // 10 172 | uint32 has_mesh : 1; // 20 173 | uint32 has_skin : 1; // 40 174 | uint32 has_anim : 1; // 80 175 | uint32 has_dangly : 1; // 100 176 | uint32 has_aabb : 1; // 200 177 | uint32 has_unknown400: 1; // 400 178 | uint32 has_unknown800: 1; // 800 179 | uint32 has_gob: 1; // 1000 180 | uint32 has_collision: 1; // 2000 181 | uint32 has_sphere: 1; // 4000 182 | uint32 has_capsule: 1; // 8000 183 | uint32 has_unknown10000: 1; // 10000 184 | uint32 has_dangly_bone: 1; // 20000 185 | uint32 has_controllers: 1; // 40000 186 | uint32 has_unknown80000: 1; // 80000 187 | } content_node; 188 | 189 | typedef struct { 190 | content_node content; 191 | short node_number1; 192 | short node_number2; 193 | uint32 p_mdl ; 194 | uint32 p_parent_node ; 195 | vertex position; 196 | quaternion orientation; 197 | uint32 p_children ; 198 | uint32 count_children; 199 | float scale; 200 | float max_anim_distance; 201 | } header_node ; 202 | 203 | //------------------------------------------------------ 204 | // specific node structures 205 | 206 | // forward declaration of the node struct 207 | struct node; 208 | 209 | typedef struct { 210 | ushort animated_uv : 1; 211 | ushort light_mapped : 1; 212 | ushort bgr_geometry : 1; 213 | ushort beaming : 1; 214 | ushort render : 1; 215 | } node_flags; 216 | 217 | enum mesh_type { 218 | point_list=0, 219 | line_list, 220 | line_strip, 221 | triangle_list, 222 | triangle_strip, 223 | triangle_fan, 224 | unknown 225 | }; 226 | 227 | typedef struct { 228 | vertex normal; 229 | float distance; 230 | ushort unknown1; 231 | ushort unknown2; 232 | ushort vertex_id[3]; 233 | ushort unknown3; 234 | } face; 235 | 236 | /* 237 | dcl_position v0 238 | dcl_normal v3 239 | dcl_texcoord v4 240 | dcl_color v5 241 | dcl_texcoord v7 242 | dcl_texcoord v8 243 | dcl_texcoord v9 244 | dcl_texcoord v10 245 | dcl_tangent v11 246 | dcl_tangent v12 247 | dcl_tangent v13 248 | */ 249 | typedef struct { 250 | uint32 has_vertex : 1; 251 | uint32 has_UV1 : 1; 252 | uint32 has_UV2 : 1; 253 | uint32 has_UV3 : 1; 254 | uint32 has_UV4 : 1; 255 | uint32 has_texcoord4 : 1; 256 | uint32 has_color : 1; 257 | uint32 has_tangent : 1; 258 | uint32 has_offset4 : 1; 259 | uint32 has_offset5 : 1; 260 | uint32 has_offset6 : 1; 261 | uint32 has_4bytes3 : 1; 262 | } vertex_struct_flags; 263 | 264 | typedef struct { 265 | array_definition faces; 266 | vertex bound_min; 267 | vertex bound_max; 268 | float radius; 269 | vertex average; 270 | uint32 transparency; 271 | node_flags flags; 272 | ushort shadow; 273 | char texture[32]; 274 | uint32 count_vertex_index; 275 | uint32 p_vertex_index_array ; 276 | int32 unknown4; 277 | mesh_type type; 278 | uint32 unknown5 ; 279 | float unknown6; 280 | float unknown7; 281 | uint32 mdx_vertex_struct_size; 282 | vertex_struct_flags flags; 283 | int32 mdx_vertex_struct_offset_vertex; 284 | int32 mdx_vertex_struct_offset_texcoord4; 285 | int32 mdx_vertex_struct_offset_color; 286 | int32 mdx_vertex_struct_offset_UV1; 287 | int32 mdx_vertex_struct_offset_UV2; 288 | int32 mdx_vertex_struct_offset_UV3; 289 | int32 mdx_vertex_struct_offset_UV4; 290 | int32 mdx_vertex_struct_offset_tangent; 291 | int32 mdx_vertex_struct_offset4; 292 | int32 mdx_vertex_struct_offset5; 293 | int32 mdx_vertex_struct_offset6; 294 | int32 mdx_vertex_struct_offset_4bytes3; 295 | ushort mdx_vertex_struct_count; 296 | ushort count_texture; 297 | uint32 mdx_offset_vertex_struct ; 298 | uint32 unknown16 ; 299 | int32 material_id; 300 | uint32 material_groupid; 301 | float illumination[3]; 302 | float alpha; 303 | float texture_coords_w; 304 | uint32 unknown17 ; 305 | uint32 mdx_offset_normals ; 306 | uint32 mdx_size_normals; 307 | } header_mesh ; 308 | 309 | typedef struct(node &n) { 310 | if (n.mesh.faces.nr_used_entries > 0) { 311 | GoToPointer(n.mesh.faces.p_array_start); 312 | face faces[n.mesh.faces.nr_used_entries] ; 313 | } 314 | if (n.mesh.p_vertex_index_array > 0) { 315 | GoToPointer(n.mesh.p_vertex_index_array); 316 | ushort vertex_index_array[n.mesh.count_vertex_index] ; 317 | } 318 | } data_mesh ; 319 | 320 | typedef struct { 321 | uint32 unknown1; 322 | uint32 unknown2; 323 | uint32 unknown3; 324 | uint32 mdx_vertex_struct_offset_bone_weights; 325 | uint32 mdx_vertex_struct_offset_bone_mapping_id; 326 | uint32 p_bone_mapping ; 327 | uint32 count_bone_mapping; 328 | array_definition bone_quats; 329 | array_definition bone_vertex; 330 | array_definition bone_constants; 331 | short bone_parts[47]; 332 | short spare; 333 | } header_skin ; 334 | 335 | typedef struct(node &n) { 336 | if (n.skin.count_bone_mapping > 0) { 337 | GoToPointer(n.skin.p_bone_mapping); 338 | short bone_mapping[n.skin.count_bone_mapping] ; 339 | } 340 | if (n.skin.bone_quats.nr_used_entries > 0) { 341 | GoToPointer(n.skin.bone_quats.p_array_start); 342 | quaternion q[n.skin.bone_quats.nr_used_entries] ; 343 | } 344 | if (n.skin.bone_vertex.nr_used_entries > 0) { 345 | GoToPointer(n.skin.bone_vertex.p_array_start); 346 | vertex v[n.skin.bone_vertex.nr_used_entries] ; 347 | } 348 | } data_skin ; 349 | 350 | typedef struct { 351 | ushort unknown1; 352 | ushort unknown2; 353 | uint32 unknown3; 354 | uint32 unknown4; 355 | uint32 unknown5; 356 | float x; 357 | float y; 358 | float z; 359 | } header_gob ; 360 | 361 | typedef struct { 362 | float f[8]; 363 | ushort u[2]; 364 | float f1[6]; 365 | array_definition a; 366 | } header_dabo ; 367 | 368 | typedef struct { 369 | uint32 u1[23]; 370 | float u2[16]; 371 | } header_light ; 372 | 373 | typedef struct { 374 | uint32 flag_p2p : 1; // 1 375 | uint32 flag_p2p_sel : 1; // 2 376 | uint32 flag_affected_wind : 1; // 4 377 | uint32 flag_tinted : 1; // 8 378 | uint32 flag_bounce : 1; // 10 379 | uint32 flag_random : 1; // 20 380 | uint32 flag_inherit : 1; // 40 381 | uint32 flag_inherit_vel : 1; // 80 382 | uint32 flag_inherit_local : 1; // 100 383 | uint32 flag_splat : 1; // 200 384 | uint32 flag_inherit_part : 1; // 400 385 | uint32 flag_unknown800 : 1; // 800 386 | uint32 flag_unknown1000 : 1; // 1000 387 | uint32 flag_unknown2000 : 1; // 2000 388 | uint32 flag_unknown4000 : 1; // 4000 389 | uint32 flag_unknown8000 : 1; // 8000 390 | } emitter_flags; 391 | 392 | typedef struct { 393 | float dead_space; 394 | float blast_radius; 395 | float blast_length; 396 | uint32 grid_x; 397 | uint32 grid_y; 398 | uint32 space; 399 | uint32 space2; 400 | uint32 space3; 401 | char update[32]; 402 | char render[32]; 403 | char blend[32]; 404 | char texture[32]; 405 | char chunk[32]; 406 | float unknown[55]; 407 | uint32 p_next_emitter ; 408 | uint32 unknown2; 409 | uint32 unknown3; 410 | emitter_flags flags; 411 | } header_emitter ; 412 | 413 | struct entry_aabb; 414 | 415 | typedef struct { 416 | vertex bound_min; 417 | vertex bound_max; 418 | uint32 p_left_node ; 419 | uint32 p_right_node ; 420 | int32 part_number_leaf_face; 421 | uint32 plane; 422 | if (p_left_node > 0) { 423 | GoToPointer(p_left_node); 424 | entry_aabb left; 425 | } 426 | if (p_right_node > 0) { 427 | GoToPointer(p_right_node); 428 | entry_aabb right; 429 | } 430 | } entry_aabb ; 431 | 432 | typedef struct { 433 | uint32 p_aabb ; 434 | array_definition a1; 435 | array_definition a2; 436 | uint32 p_aabb2 ; 437 | uint32 u1; 438 | uint32 u2; 439 | } header_aabb ; 440 | 441 | typedef struct(node &n) { 442 | if (n.aabb.a1.nr_used_entries > 0) { 443 | GoToPointer(n.aabb.a1.p_array_start); 444 | vertex verts[n.aabb.a1.nr_used_entries] ; 445 | } 446 | if (n.aabb.a2.nr_used_entries > 0) { 447 | GoToPointer(n.aabb.a2.p_array_start); 448 | float f2[2*n.aabb.a2.nr_used_entries] ; 449 | } 450 | 451 | GoToPointer(n.aabb.p_aabb); 452 | entry_aabb root; 453 | 454 | } data_aabb ; 455 | 456 | typedef struct { 457 | uint32 controller_type; 458 | array_definition controllers; 459 | uint32 p_timekeys ; 460 | uint32 count_timekeys; 461 | uint32 p_data ; 462 | uint32 count_data; 463 | uint32 always6; 464 | } header_controller ; 465 | 466 | typedef struct(content_node &c) { 467 | if (c.has_light) { 468 | controller_type_light type; 469 | } else if (c.has_mesh) { 470 | controller_type_mesh type; 471 | } else { 472 | controller_type_all type; 473 | } 474 | short unknown1 ; 475 | ushort value_count; 476 | ushort timekey_start; 477 | ushort data_start; 478 | byte data_type; 479 | byte padding[3]; 480 | } controller ; 481 | 482 | typedef struct(node &n) { 483 | local int c; 484 | if (n.controllers.controllers.nr_used_entries > 0) { 485 | GoToPointer(n.controllers.controllers.p_array_start); 486 | for (c = 0; c < n.controllers.controllers.nr_used_entries; c++) { 487 | controller key(n.header.content) ; 488 | } 489 | } 490 | if (n.controllers.count_timekeys > 0) { 491 | GoToPointer(n.controllers.p_timekeys); 492 | ushort controller_timekeys[n.controllers.count_timekeys] ; 493 | } 494 | 495 | if (n.controllers.count_data > 0) { 496 | for (c = 0; c < n.controllers.controllers.nr_used_entries; c++) { 497 | GoToPointer(n.controllers.p_data + 4 * key[c].data_start); 498 | if (key[c].data_type == 2) { 499 | vertex controller_data_2[key[c].value_count] ; 500 | } 501 | if (key[c].data_type == 4) { 502 | uint32 controller_data_4[key[c].value_count] ; 503 | } 504 | if (key[c].data_type == 5) { 505 | typedef struct { 506 | uint64 data : 11 ; 507 | uint64 data : 11 ; 508 | uint64 data : 10 ; 509 | uint64 data : 11 ; 510 | uint64 data : 10 ; 511 | uint64 data : 11 ; 512 | } datatype5 ; 513 | typedef struct { 514 | uint32 data[2] ; 515 | } datatype_5; 516 | datatype_5 controller_data_5[key[c].value_count] ; 517 | } 518 | } 519 | } 520 | } data_controller ; 521 | 522 | string ReadD5( datatype5 &d5 ) { 523 | local float x = 1.0 - ( d5.data[0] / Pow(2,10)); 524 | local float y = 1.0 - ( d5.data[1] / Pow(2,10)); 525 | local float z = 1.0 - ( d5.data[2] / Pow(2,9)); 526 | local float w = 1.0 - ( d5.data[3] / Pow(2,10)); 527 | string s; 528 | SPrintf(s, "%f,%f,%f,%f", x,y,z,w); 529 | return s; 530 | } 531 | 532 | typedef struct { 533 | header_node header; 534 | if (header.content.has_light) { 535 | header_light light; 536 | } 537 | if (header.content.has_emitter) { 538 | header_emitter emitter; 539 | } 540 | if (header.content.has_mesh) { 541 | header_mesh mesh; 542 | } 543 | if (header.content.has_skin) { 544 | header_skin skin; 545 | } 546 | if (header.content.has_aabb) { 547 | header_aabb aabb; 548 | } 549 | if (header.content.has_gob) { 550 | header_gob gob; 551 | } 552 | if (header.content.has_dangly_bone) { 553 | header_dabo dabo; 554 | } 555 | if (header.content.has_controllers) { 556 | header_controller controllers; 557 | } 558 | if (header.content.has_aabb) { 559 | data_aabb aabb_data(this); 560 | } 561 | if (header.content.has_skin) { 562 | data_skin skin_data(this); 563 | } 564 | if (header.content.has_mesh) { 565 | if (mesh.faces.nr_used_entries > 0 || mesh.p_vertex_index_array > 0) { 566 | data_mesh mesh_data(this); 567 | } 568 | } 569 | if (header.content.has_controllers) { 570 | if (controllers.controllers.nr_used_entries > 0 || 571 | controllers.count_timekeys > 0 || 572 | controllers.count_data > 0) { 573 | data_controller controller_data(this); 574 | } 575 | } 576 | if (header.count_children > 0) { 577 | GoToPointer(header.p_children); 578 | uint32 p_children[header.count_children] ; 579 | 580 | local int i; 581 | for (i = 0; i < header.count_children; i++) { 582 | GoToPointer(p_children[i]); 583 | node children; 584 | } 585 | } 586 | } node ; 587 | 588 | //------------------------------------------------------ 589 | // animation structures 590 | 591 | typedef struct { 592 | float length; 593 | char name[32]; 594 | uint32 u1; 595 | } event ; 596 | 597 | typedef struct(int nr_of_events) { 598 | local int e; 599 | for (e = 0; e < nr_of_events; e++) { 600 | event ev; 601 | } 602 | } event_array; 603 | 604 | typedef struct { 605 | header_geometry geometry; 606 | float length; 607 | float transistion; 608 | byte bool1; 609 | byte bool2; 610 | char name[32]; 611 | ushort unknown1; 612 | array_definition events; 613 | uint32 unknown3; 614 | } header_animation; 615 | 616 | typedef struct { 617 | header_animation header; 618 | 619 | if (header.events.nr_used_entries > 0) { 620 | GoToPointer(header.events.p_array_start); 621 | event_array events(header.events.nr_used_entries); 622 | } 623 | 624 | GoToPointer(header.geometry.p_node_header); 625 | node animation_node; 626 | } animation ; 627 | 628 | //------------------------------------------------------ 629 | // main definition 630 | 631 | LittleEndian(); 632 | header_file hf; 633 | header_model hm; 634 | 635 | GoToPointer(hm.geometry.p_node_header); 636 | node root; 637 | 638 | local int a; 639 | for (a = 0; a < hm.animations.def.nr_used_entries; a++) { 640 | GoToPointer(hm.animations.p_index[a]); 641 | animation anim; 642 | } 643 | 644 | //------------------------------------------------------ 645 | // read Methods 646 | 647 | string ReadStrings( strings &str ) { 648 | return str.s; 649 | } 650 | 651 | string ReadVertex( vertex &v ) { 652 | string s; 653 | SPrintf(s, "x=%f,y=%f,z=%f", v.x, v.y, v.z); 654 | return s; 655 | } 656 | 657 | string ReadQuaternion( quaternion &q ) { 658 | string s; 659 | SPrintf(s, "%f,%f,%f,%f", q.w, q.x, q.y, q.z); 660 | return s; 661 | } 662 | 663 | string ReadPointer(uint32 &p) { 664 | uint32 v = 0; 665 | if (p > 0) { 666 | v = sizeof(header_file) + p; 667 | } 668 | string s; 669 | SPrintf(s, "%Xh", v); 670 | return s; 671 | } 672 | 673 | 674 | string ReadArrayDef( array_definition &a ) { 675 | string s; 676 | SPrintf(s, "%s(%i)", ReadPointer(a.p_array_start), a.nr_used_entries); 677 | return s; 678 | } 679 | 680 | string ReadController( controller &key ) { 681 | string s; 682 | SPrintf(s, "%i", key.type); 683 | return s; 684 | } 685 | 686 | string ReadNode( node &n ) { 687 | string s = hm.names.str[n.header.node_number2].s; 688 | if (n.header.content.has_light) { 689 | s+="+Ligh"; 690 | } 691 | if (n.header.content.has_emitter) { 692 | s+="+Emit"; 693 | } 694 | if (n.header.content.has_camera) { 695 | s+="+Came"; 696 | } 697 | if (n.header.content.has_reference) { 698 | s+="+Refs"; 699 | } 700 | if (n.header.content.has_mesh) { 701 | s+="+Mesh"; 702 | } 703 | if (n.header.content.has_skin) { 704 | s+="+Skin"; 705 | } 706 | if (n.header.content.has_anim) { 707 | s+="+Anim"; 708 | } 709 | if (n.header.content.has_dangly) { 710 | s+="+Dang"; 711 | } 712 | if (n.header.content.has_aabb) { 713 | s+="+AABB"; 714 | } 715 | if (n.header.content.has_unknown400) { 716 | s+="+U400"; 717 | } 718 | if (n.header.content.has_unknown800) { 719 | s+="+U800"; 720 | } 721 | if (n.header.content.has_gob) { 722 | s+="+Gob"; 723 | } 724 | if (n.header.content.has_collision) { 725 | s+="+Coll"; 726 | } 727 | if (n.header.content.has_sphere) { 728 | s+="+Sphr"; 729 | } 730 | if (n.header.content.has_capsule) { 731 | s+="+Caps"; 732 | } 733 | if (n.header.content.has_unknown10000) { 734 | s+="+U10000"; 735 | } 736 | if (n.header.content.has_dangly_bone) { 737 | s+="+DaBo"; 738 | } 739 | if (n.header.content.has_controllers) { 740 | s+="+Cntl"; 741 | } 742 | if (n.header.content.has_unknown80000) { 743 | s+="+U80000"; 744 | } 745 | if (n.header.count_children > 0) { 746 | local string child_count; 747 | SPrintf(child_count, "(%d)", n.header.count_children); 748 | s+=child_count; 749 | } 750 | return s; 751 | } 752 | 753 | string ReadEvent(event &e) { 754 | local string s; 755 | SPrintf(s, "%s(%f)", e.name, e.length); 756 | return s; 757 | } 758 | 759 | string ReadAnim(animation &anim) { 760 | return anim.header.geometry.model_name; 761 | } 762 | -------------------------------------------------------------------------------- /templates/NWN1MDL.bt: -------------------------------------------------------------------------------- 1 | //-------------------------------------- 2 | //--- 010 Editor v6.0.2 Binary Template 3 | // 4 | // File: NWN1MDL.bt 5 | // Author: Enrico Horn (Farmboy0) 6 | // Revision: 0.9 7 | // Purpose: to map the model format of the NWN1 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 . 16 | // 17 | //------------------------------------------------------ 18 | // Colors: 19 | //------------------------------------------------------ 20 | // Dark Red : array of file pointers(not the array definition) 21 | // Black : mdx data(mesh) 22 | // Dark Gray : mdx data(skin) 23 | 24 | // Light Blue : file header 25 | // Light Purple : geometry header 26 | 27 | // Aqua : model header without geometry header 28 | // Dark Aqua : animation incl. events array without geometry header 29 | 30 | // Light Green : node header 31 | // Red : controller data 32 | // Yellow : light node without node header 33 | // Dark Yellow : emitter node without node header 34 | // 0xA000A0 : reference node without node header 35 | // Gray : mesh node without node header 36 | // Orange : skin node without node header 37 | // 0x8090FF : anim node without node header 38 | // 0x909000 : dangly node without node header 39 | // Dark Blue : AABB node without node header 40 | //------------------------------------------------------ 41 | 42 | //------------------------------------------------------ 43 | // generic structures 44 | 45 | typedef struct { 46 | float x; 47 | float y; 48 | float z; 49 | } vertex ; 50 | 51 | typedef struct { 52 | float u; 53 | float v; 54 | } texcoord ; 55 | 56 | typedef struct { 57 | float r; 58 | float g; 59 | float b; 60 | } color ; 61 | 62 | typedef struct { 63 | ubyte r; 64 | ubyte g; 65 | ubyte b; 66 | ubyte a; 67 | } rgba ; 68 | 69 | typedef struct { 70 | float w; 71 | float x; 72 | float y; 73 | float z; 74 | } quaternion ; 75 | 76 | typedef struct { 77 | uint32 p_array_start ; 78 | uint32 nr_used_entries; 79 | uint32 nr_alloc_entries; 80 | } array_definition ; 81 | 82 | typedef struct { 83 | array_definition def; 84 | if (def.nr_used_entries > 0) { 85 | local int pos = FTell(); 86 | 87 | GoToPointer(def.p_array_start); 88 | uint32 p_index[def.nr_used_entries] ; 89 | 90 | FSeek(pos); 91 | } 92 | } array; 93 | 94 | //------------------------------------------------------ 95 | // generic header structures 96 | 97 | typedef struct { 98 | uint32 bin_mdl_id ; 99 | uint32 p_start_mdx ; 100 | uint32 size_mdx; 101 | } header_file ; 102 | 103 | typedef struct { 104 | uint32 p_func1 ; 105 | uint32 p_func2 ; 106 | char name[64]; 107 | uint32 p_node_header ; 108 | uint32 count_nodes; 109 | array_definition unknown1; 110 | array_definition unknown2; 111 | uint32 ref_count; 112 | ubyte type; 113 | ubyte padding[3]; 114 | } header_geometry ; 115 | 116 | typedef struct { 117 | header_geometry geometry; 118 | byte unknown0 ; 119 | byte unknown1 ; 120 | byte flags ; 121 | byte fog ; 122 | uint32 count_child_model; 123 | array animations; 124 | uint32 p_supermodel ; 125 | vertex bound_min; 126 | vertex bound_max; 127 | float model_radius; 128 | float scale; 129 | char supermodel_name[64]; 130 | } header_model ; 131 | 132 | //------------------------------------------------------ 133 | // general node structures 134 | 135 | enum controller_type_all { 136 | position=8, 137 | orientation=20, 138 | scale=36 139 | }; 140 | enum controller_type_light { 141 | light_position=8, 142 | light_orientation=20, 143 | light_scale=36, 144 | light_color=76, 145 | light_radius=88, 146 | light_radius_shadow=96, 147 | light_vert_displacement=100, 148 | light_multiplier=140 149 | }; 150 | enum controller_type_emitter { 151 | emitter_position=8, 152 | emitter_orientation=20, 153 | emitter_scale=36, 154 | emitter_alpha_end=80, 155 | emitter_alpha_start=84, 156 | emitter_birthrate=88, 157 | emitter_bounce_co=92, 158 | emitter_color_end=96, 159 | emitter_color_start=108, 160 | emitter_combine_time=120, 161 | emitter_drag=124, 162 | emitter_fps=128, 163 | emitter_frame_end=132, 164 | emitter_frame_start=136, 165 | emitter_grav=140, 166 | emitter_life_exp=144, 167 | emitter_mass=148, 168 | emitter_p2p_bezier2=152, 169 | emitter_p2p_bezier3=156, 170 | emitter_particle_rot=160, 171 | emitter_rand_vel=164, 172 | emitter_size_start=168, 173 | emitter_size_end=172, 174 | emitter_size_start_y=176, 175 | emitter_size_end_y=180, 176 | emitter_spread=184, 177 | emitter_threshold=188, 178 | emitter_velocity=192, 179 | emitter_size_x=196, 180 | emitter_size_y=200, 181 | emitter_blur_length=204, 182 | emitter_lightning_delay=208, 183 | emitter_lightning_radius=212, 184 | emitter_lightning_scale=216, 185 | emitter_detonate=228, 186 | emitter_alpha_mid=464, 187 | emitter_color_mid=468, 188 | emitter_percent_start=480, 189 | emitter_percent_mid=481, 190 | emitter_percent_end=482, 191 | emitter_size_mid=484, 192 | emitter_size_mid_y=488 193 | }; 194 | enum controller_type_mesh { 195 | mesh_position=8, 196 | mesh_orientation=20, 197 | mesh_scale=36, 198 | mesh_self_illum_color=100, 199 | mesh_alpha=128 200 | }; 201 | 202 | typedef struct { 203 | uint32 has_header : 1; // 1 204 | uint32 has_light : 1; // 2 205 | uint32 has_emitter : 1; // 4 206 | uint32 has_camera: 1; // 8 207 | uint32 has_reference : 1; // 10 208 | uint32 has_mesh : 1; // 20 209 | uint32 has_skin : 1; // 40 210 | uint32 has_anim : 1; // 80 211 | uint32 has_dangly : 1; // 100 212 | uint32 has_aabb : 1; // 200 213 | } content_node; 214 | 215 | typedef struct { 216 | uint32 p_func1 ; 217 | uint32 p_func2 ; 218 | uint32 p_func3 ; 219 | uint32 p_func4 ; 220 | uint32 p_func5 ; 221 | uint32 p_func6 ; 222 | uint32 color_inherit; 223 | uint32 node_number; 224 | char node_name[32]; 225 | uint32 p_geometry ; 226 | uint32 p_parent_node ; 227 | array_definition children; 228 | array_definition controller_keys; 229 | array_definition controller_data; 230 | content_node content; 231 | } header_node ; 232 | 233 | typedef struct(content_node &c) { 234 | if (c.has_light) { 235 | controller_type_light type; 236 | } else if (c.has_mesh) { 237 | controller_type_mesh type; 238 | } else if (c.has_emitter) { 239 | controller_type_emitter type; 240 | } else { 241 | controller_type_all type; 242 | } 243 | ushort value_count; 244 | ushort timekey_start; 245 | ushort data_start; 246 | byte column_count; 247 | byte padding; 248 | } controller ; 249 | 250 | //------------------------------------------------------ 251 | // specific node structures 252 | 253 | // forward declaration of the node struct 254 | struct node; 255 | 256 | //------------------------------------------------------ 257 | // light node 258 | 259 | typedef struct { 260 | float flare_radius; 261 | array_definition unknown; 262 | array_definition flare_sizes; 263 | array_definition flare_positions; 264 | array_definition flare_color_shifts; 265 | array_definition flare_textures; 266 | uint32 light_priority; 267 | uint32 ambient_only; 268 | uint32 dynamic_type; 269 | uint32 affect_dynamic; 270 | uint32 shadow; 271 | uint32 generate_flare; 272 | uint32 fading; 273 | } header_light ; 274 | 275 | typedef struct { 276 | string name; 277 | } texture_name ; 278 | 279 | typedef struct(node &n) { 280 | if (n.light.flare_textures.nr_used_entries > 0) { 281 | GoToPointer(n.light.flare_textures.p_array_start); 282 | uint32 p_texture[n.light.flare_textures.nr_used_entries] ; 283 | 284 | local int i; 285 | for (i = 0; i < n.light.flare_textures.nr_used_entries; i++) { 286 | GoToPointer(p_texture[i]); 287 | texture_name texture; 288 | } 289 | } 290 | if (n.light.flare_sizes.nr_used_entries > 0) { 291 | GoToPointer(n.light.flare_sizes.p_array_start); 292 | float flare_size[n.light.flare_sizes.nr_used_entries] ; 293 | } 294 | if (n.light.flare_positions.nr_used_entries > 0) { 295 | GoToPointer(n.light.flare_positions.p_array_start); 296 | float flare_position[n.light.flare_positions.nr_used_entries] ; 297 | } 298 | if (n.light.flare_color_shifts.nr_used_entries > 0) { 299 | GoToPointer(n.light.flare_color_shifts.p_array_start); 300 | float flare_color_shift[3*n.light.flare_color_shifts.nr_used_entries] ; 301 | } 302 | } data_light ; 303 | 304 | //------------------------------------------------------ 305 | // emitter node 306 | 307 | typedef struct { 308 | uint32 flag_p2p : 1; // 1 309 | uint32 flag_p2p_sel : 1; // 2 310 | uint32 flag_affected_wind : 1; // 4 311 | uint32 flag_tinted : 1; // 8 312 | uint32 flag_bounce : 1; // 10 313 | uint32 flag_random : 1; // 20 314 | uint32 flag_inherit : 1; // 40 315 | uint32 flag_inherit_vel : 1; // 80 316 | uint32 flag_inherit_local : 1; // 100 317 | uint32 flag_splat : 1; // 200 318 | uint32 flag_inherit_part : 1; // 400 319 | } emitter_flags; 320 | 321 | typedef struct { 322 | float dead_space; 323 | float blast_radius; 324 | float blast_length; 325 | uint32 grid_x; 326 | uint32 grid_y; 327 | uint32 space; 328 | char update[32]; 329 | char render[32]; 330 | char blend[32]; 331 | char texture[64]; 332 | char chunk[16]; 333 | uint32 texture_is_2sided; 334 | uint32 loop; 335 | uint16 render_order; 336 | uint16 padding; 337 | emitter_flags flags; 338 | } header_emitter ; 339 | 340 | //------------------------------------------------------ 341 | // reference node 342 | 343 | typedef struct { 344 | char referenced_model_name[64]; 345 | uint32 reattachable; 346 | } header_reference ; 347 | 348 | //------------------------------------------------------ 349 | // mesh node 350 | 351 | enum mesh_type { 352 | point_list=0, 353 | line_list, 354 | line_strip, 355 | triangle_list, 356 | triangle_strip, 357 | triangle_fan, 358 | unknown 359 | }; 360 | 361 | typedef struct { 362 | vertex normal; 363 | float distance; 364 | int32 surface_id; 365 | ushort adj_face_ids[3]; 366 | ushort vertex_id[3]; 367 | } face; 368 | 369 | typedef struct { 370 | uint32 p_func1 ; 371 | uint32 p_func2 ; 372 | array_definition faces; 373 | vertex bound_min; 374 | vertex bound_max; 375 | float radius; 376 | vertex average; 377 | color diffuse; 378 | color ambient; 379 | color specular; 380 | float shininess; 381 | uint32 shadow; 382 | uint32 beaming; 383 | uint32 render; 384 | uint32 transparency; 385 | uint32 unknown1; 386 | char texture0[64]; 387 | char texture1[64]; 388 | char texture2[64]; 389 | char texture3[64]; 390 | uint32 tile_fade; 391 | array_definition vertex_indices; 392 | array_definition face_leftover; 393 | array_definition vertex_indices_count; 394 | array_definition vertex_indices_offset; 395 | int32 p_mdx_unknown1 ; 396 | uint32 unknown2 ; 397 | mesh_type type; 398 | int32 p_start_mdx ; 399 | int32 p_mdx_vertex ; 400 | uint16 count_vertexes; 401 | uint16 count_textures; 402 | int32 p_mdx_texture0 ; 403 | int32 p_mdx_texture1 ; 404 | int32 p_mdx_texture2 ; 405 | int32 p_mdx_texture3 ; 406 | int32 p_mdx_vertex_normals ; 407 | int32 p_mdx_vertex_colors ; 408 | int32 p_mdx_tex_anim0 ; 409 | int32 p_mdx_tex_anim1 ; 410 | int32 p_mdx_tex_anim2 ; 411 | int32 p_mdx_tex_anim3 ; 412 | int32 p_mdx_tex_anim4 ; 413 | int32 p_mdx_tex_anim5 ; 414 | byte light_mapped; 415 | byte rotate_texture; 416 | uint16 padding; 417 | float vertex_normal_sum; 418 | uint32 unknown3; 419 | } header_mesh ; 420 | 421 | typedef struct(node &n) { 422 | if (n.mesh.faces.nr_used_entries > 0) { 423 | GoToPointer(n.mesh.faces.p_array_start); 424 | face faces[n.mesh.faces.nr_used_entries] ; 425 | } 426 | if (n.mesh.vertex_indices_count.nr_used_entries > 0) { 427 | GoToPointer(n.mesh.vertex_indices_count.p_array_start); 428 | uint32 vertex_indices_count[n.mesh.vertex_indices_count.nr_used_entries] ; 429 | } 430 | if (n.mesh.vertex_indices_offset.nr_used_entries > 0) { 431 | GoToPointer(n.mesh.vertex_indices_offset.p_array_start); 432 | int32 vertex_indices_offset[n.mesh.vertex_indices_offset.nr_used_entries] ; 433 | } 434 | } data_mesh ; 435 | 436 | typedef struct(node &n) { 437 | if (n.mesh.p_mdx_vertex != -1) { 438 | GoToMDXPointer(n.mesh.p_mdx_vertex); 439 | vertex v[n.mesh.count_vertexes] ; 440 | } 441 | if (n.mesh.p_mdx_texture0 != -1) { 442 | GoToMDXPointer(n.mesh.p_mdx_texture0); 443 | texcoord uv0[n.mesh.count_vertexes] ; 444 | } 445 | if (n.mesh.p_mdx_texture1 != -1) { 446 | GoToMDXPointer(n.mesh.p_mdx_texture1); 447 | texcoord uv1[n.mesh.count_vertexes] ; 448 | } 449 | if (n.mesh.p_mdx_texture2 != -1) { 450 | GoToMDXPointer(n.mesh.p_mdx_texture2); 451 | texcoord uv2[n.mesh.count_vertexes] ; 452 | } 453 | if (n.mesh.p_mdx_texture3 != -1) { 454 | GoToMDXPointer(n.mesh.p_mdx_texture3); 455 | texcoord uv3[n.mesh.count_vertexes] ; 456 | } 457 | if (n.mesh.p_mdx_vertex_normals != -1) { 458 | GoToMDXPointer(n.mesh.p_mdx_vertex_normals); 459 | vertex normal[n.mesh.count_vertexes] ; 460 | } 461 | if (n.mesh.p_mdx_vertex_colors != -1) { 462 | GoToMDXPointer(n.mesh.p_mdx_vertex_colors); 463 | rgba color_rgba[n.mesh.count_vertexes] ; 464 | } 465 | if (n.mesh.vertex_indices_offset.nr_used_entries > 0) { 466 | local int i; 467 | for (i = 0; i < n.mesh.vertex_indices_offset.nr_used_entries; i++) { 468 | GoToMDXPointer(n.mesh_data.vertex_indices_offset[i]); 469 | short indices[n.mesh_data.vertex_indices_count[i]] ; 470 | } 471 | } 472 | if (n.mesh.p_mdx_tex_anim0 != -1) { 473 | GoToMDXPointer(n.mesh.p_mdx_tex_anim0); 474 | rgba anim0[3*n.mesh.count_vertexes] ; 475 | } 476 | if (n.mesh.p_mdx_tex_anim1 != -1) { 477 | GoToMDXPointer(n.mesh.p_mdx_tex_anim1); 478 | rgba anim1[3*n.mesh.count_vertexes] ; 479 | } 480 | if (n.mesh.p_mdx_tex_anim2 != -1) { 481 | GoToMDXPointer(n.mesh.p_mdx_tex_anim2); 482 | rgba anim2[3*n.mesh.count_vertexes] ; 483 | } 484 | if (n.mesh.p_mdx_tex_anim3 != -1) { 485 | GoToMDXPointer(n.mesh.p_mdx_tex_anim3); 486 | rgba anim3[3*n.mesh.count_vertexes] ; 487 | } 488 | if (n.mesh.p_mdx_tex_anim4 != -1) { 489 | GoToMDXPointer(n.mesh.p_mdx_tex_anim4); 490 | rgba anim4[3*n.mesh.count_vertexes] ; 491 | } 492 | if (n.mesh.p_mdx_tex_anim5 != -1) { 493 | GoToMDXPointer(n.mesh.p_mdx_tex_anim5); 494 | rgba anim5[n.mesh.count_vertexes] ; 495 | } 496 | } mdx_mesh ; 497 | 498 | //------------------------------------------------------ 499 | // skin node 500 | 501 | typedef struct { 502 | array_definition weights; 503 | int32 p_weight_vertex ; 504 | int32 p_bone_ref_index ; 505 | int32 p_bone_mapping ; 506 | int32 count_bone_mapping; 507 | array_definition bone_quats; 508 | array_definition bone_vertex; 509 | array_definition bone_constants; 510 | short bone_parts[17]; 511 | short spare; 512 | } header_skin ; 513 | 514 | typedef struct(node &n) { 515 | if (n.skin.count_bone_mapping > 0) { 516 | GoToPointer(n.skin.p_bone_mapping); 517 | short bone_mapping[n.skin.count_bone_mapping] ; 518 | } 519 | if (n.skin.bone_quats.nr_used_entries > 0) { 520 | GoToPointer(n.skin.bone_quats.p_array_start); 521 | quaternion q[n.skin.bone_quats.nr_used_entries] ; 522 | } 523 | if (n.skin.bone_vertex.nr_used_entries > 0) { 524 | GoToPointer(n.skin.bone_vertex.p_array_start); 525 | vertex v[n.skin.bone_vertex.nr_used_entries] ; 526 | } 527 | if (n.skin.bone_constants.nr_used_entries > 0) { 528 | GoToPointer(n.skin.bone_constants.p_array_start); 529 | short constants[2*n.skin.bone_constants.nr_used_entries] ; 530 | } 531 | } data_skin ; 532 | 533 | typedef struct { 534 | float weight0; 535 | float weight1; 536 | float weight2; 537 | float weight3; 538 | } bone_weights; 539 | 540 | typedef struct { 541 | short index0; 542 | short index1; 543 | short index2; 544 | short index3; 545 | } bone_index; 546 | 547 | typedef struct(node &n) { 548 | if (n.skin.p_weight_vertex != -1) { 549 | GoToMDXPointer(n.skin.p_weight_vertex); 550 | bone_weights bw[n.mesh.count_vertexes] ; 551 | } 552 | if (n.skin.p_bone_ref_index != -1) { 553 | GoToMDXPointer(n.skin.p_bone_ref_index); 554 | bone_index bi[n.mesh.count_vertexes] ; 555 | } 556 | } mdx_skin ; 557 | 558 | //------------------------------------------------------ 559 | // anim node 560 | 561 | typedef struct { 562 | float sample_period; 563 | array_definition animation_vertices; 564 | array_definition animation_texcoords; 565 | array_definition animation_normals; 566 | uint32 p_animation_vertex ; 567 | uint32 p_animation_texcoord ; 568 | uint32 count_set_vertex; 569 | uint32 count_set_vertex_texcoord; 570 | } header_anim ; 571 | 572 | typedef struct(node &n) { 573 | if (n.anim.p_animation_vertex > 0) { 574 | GoToPointer(n.anim.p_animation_vertex); 575 | vertex anim_vertex[n.anim.count_set_vertex*n.mesh.count_vertexes] ; 576 | } 577 | if (n.anim.p_animation_texcoord > 0) { 578 | GoToPointer(n.anim.p_animation_texcoord); 579 | texcoord anim_texcoord[n.anim.count_set_vertex_texcoord*n.mesh.count_vertexes] ; 580 | } 581 | } data_anim ; 582 | 583 | //------------------------------------------------------ 584 | // dangly node 585 | 586 | typedef struct { 587 | array_definition constraints; 588 | float displacement; 589 | float tightness; 590 | float period; 591 | } header_dangly ; 592 | 593 | typedef struct(node &n) { 594 | if (n.dangly.constraints.nr_used_entries > 0) { 595 | GoToPointer(n.dangly.constraints.p_array_start); 596 | float constraint[n.dangly.constraints.nr_used_entries] ; 597 | } 598 | } data_dangly ; 599 | 600 | //------------------------------------------------------ 601 | // aabb node 602 | 603 | struct entry_aabb; 604 | 605 | typedef struct { 606 | vertex bound_min; 607 | vertex bound_max; 608 | uint32 p_left_node ; 609 | uint32 p_right_node ; 610 | int32 part_number_leaf_face; 611 | uint32 plane; 612 | if (p_left_node > 0) { 613 | GoToPointer(p_left_node); 614 | entry_aabb left; 615 | } 616 | if (p_right_node > 0) { 617 | GoToPointer(p_right_node); 618 | entry_aabb right; 619 | } 620 | } entry_aabb ; 621 | 622 | typedef struct { 623 | uint32 p_aabb ; 624 | } header_aabb ; 625 | 626 | typedef struct(node &n) { 627 | GoToPointer(n.aabb.p_aabb); 628 | entry_aabb root; 629 | } data_aabb ; 630 | 631 | //------------------------------------------------------ 632 | // model node 633 | 634 | typedef struct { 635 | header_node header; 636 | if (header.content.has_light) { 637 | header_light light; 638 | } 639 | if (header.content.has_emitter) { 640 | header_emitter emitter; 641 | } 642 | if (header.content.has_reference) { 643 | header_reference reference; 644 | } 645 | if (header.content.has_mesh) { 646 | header_mesh mesh; 647 | } 648 | if (header.content.has_skin) { 649 | header_skin skin; 650 | } 651 | if (header.content.has_anim) { 652 | header_anim anim; 653 | } 654 | if (header.content.has_dangly) { 655 | header_dangly dangly; 656 | } 657 | if (header.content.has_aabb) { 658 | header_aabb aabb; 659 | } 660 | if (header.content.has_aabb) { 661 | data_aabb aabb_data(this); 662 | } 663 | if (header.content.has_dangly) { 664 | if (dangly.constraints.nr_used_entries > 0) { 665 | data_dangly dangly_data(this); 666 | } 667 | } 668 | if (header.content.has_anim) { 669 | if (anim.p_animation_vertex > 0) { 670 | data_anim anim_data(this); 671 | } 672 | } 673 | if (header.content.has_skin) { 674 | if (skin.count_bone_mapping > 0 || 675 | skin.bone_quats.nr_used_entries > 0 || 676 | skin.bone_vertex.nr_used_entries > 0 || 677 | skin.bone_constants.nr_used_entries > 0) { 678 | 679 | data_skin skin_data(this); 680 | } 681 | } 682 | if (header.content.has_mesh) { 683 | if (mesh.faces.nr_used_entries > 0) { 684 | data_mesh mesh_data(this); 685 | } 686 | } 687 | if (header.content.has_light) { 688 | if (light.flare_textures.nr_used_entries > 0 || 689 | light.flare_sizes.nr_used_entries > 0 || 690 | light.flare_positions.nr_used_entries > 0 || 691 | light.flare_color_shifts.nr_used_entries > 0) { 692 | 693 | data_light light_data(this); 694 | } 695 | } 696 | if (header.children.nr_used_entries > 0) { 697 | GoToPointer(header.children.p_array_start); 698 | uint32 p_children[header.children.nr_used_entries] ; 699 | 700 | local int i; 701 | for (i = 0; i < header.children.nr_used_entries; i++) { 702 | GoToPointer(p_children[i]); 703 | node children; 704 | } 705 | } 706 | if (header.controller_keys.nr_used_entries > 0) { 707 | GoToPointer(header.controller_keys.p_array_start); 708 | 709 | local int c; 710 | for (c = 0; c < header.controller_keys.nr_used_entries; c++) { 711 | controller key(header.content) ; 712 | } 713 | } 714 | if (header.controller_data.nr_used_entries > 0) { 715 | GoToPointer(header.controller_data.p_array_start); 716 | float data[header.controller_data.nr_used_entries] ; 717 | } 718 | if (header.content.has_skin && 719 | (skin.p_weight_vertex != -1 || 720 | skin.p_bone_ref_index != -1)) { 721 | 722 | mdx_skin skin_mdx(this); 723 | } 724 | if (header.content.has_mesh && mesh.count_vertexes > 0) { 725 | GoToPointer(mesh.p_start_mdx); 726 | mdx_mesh mdx(this); 727 | } 728 | } node ; 729 | 730 | //------------------------------------------------------ 731 | // animation structures 732 | 733 | typedef struct { 734 | float start; 735 | char name[32]; 736 | } event ; 737 | 738 | typedef struct(int nr_of_events) { 739 | local int e; 740 | for (e = 0; e < nr_of_events; e++) { 741 | event ev; 742 | } 743 | } event_array; 744 | 745 | typedef struct { 746 | header_geometry geometry; 747 | float length; 748 | float transition; 749 | char name_root_node[64]; 750 | array_definition events; 751 | } header_animation; 752 | 753 | typedef struct { 754 | header_animation header; 755 | 756 | if (header.events.nr_used_entries > 0) { 757 | GoToPointer(header.events.p_array_start); 758 | event_array events(header.events.nr_used_entries); 759 | } 760 | 761 | GoToPointer(header.geometry.p_node_header); 762 | node animation_node; 763 | } animation ; 764 | 765 | //------------------------------------------------------ 766 | // main definition 767 | 768 | LittleEndian(); 769 | header_file hf; 770 | if (hf.bin_mdl_id == 0) { 771 | header_model hm; 772 | 773 | GoToPointer(hm.geometry.p_node_header); 774 | node root; 775 | 776 | local int a; 777 | for (a = 0; a < hm.animations.def.nr_used_entries; a++) { 778 | GoToPointer(hm.animations.p_index[a]); 779 | animation anim; 780 | } 781 | } 782 | 783 | //------------------------------------------------------ 784 | // GoTo(MDX)Pointer 785 | 786 | void GoToPointer(uint32 p) { 787 | FSeek(sizeof(header_file) + p); 788 | } 789 | 790 | uint32 mdxPointer(int32 p) { 791 | local uint32 pos = 0; 792 | if (p > -1) { 793 | pos = p; 794 | } 795 | return sizeof(header_file) + hf.p_start_mdx + pos; 796 | } 797 | 798 | void GoToMDXPointer(int32 p) { 799 | FSeek(mdxPointer(p)); 800 | } 801 | 802 | //------------------------------------------------------ 803 | // read Methods 804 | 805 | string ReadVertex( vertex &v ) { 806 | local string s; 807 | SPrintf(s, "x=%f,y=%f,z=%f", v.x, v.y, v.z); 808 | return s; 809 | } 810 | 811 | string ReadTexCoord( texcoord &uv ) { 812 | local string s; 813 | SPrintf(s, "u=%f,v=%f", uv.u, uv.v); 814 | return s; 815 | } 816 | 817 | string ReadColor( color &c ) { 818 | local string s; 819 | SPrintf(s, "r=%f,g=%f,b=%f", c.r, c.g, c.b); 820 | return s; 821 | } 822 | 823 | string ReadRGBA( rgba &c ) { 824 | local string s; 825 | SPrintf(s, "r=%u,g=%u,b=%u,a=%u", c.r, c.g, c.b,c.a); 826 | return s; 827 | } 828 | 829 | string ReadQuaternion( quaternion &q ) { 830 | local string s; 831 | SPrintf(s, "%f,%f,%f,%f", q.w, q.x, q.y, q.z); 832 | return s; 833 | } 834 | 835 | string ReadPointer( uint32 &p ) { 836 | local uint32 v = 0; 837 | if (p > 0) { 838 | v = sizeof(header_file) + p; 839 | } 840 | local string s; 841 | SPrintf(s, "%Xh", v); 842 | return s; 843 | } 844 | 845 | string ReadMDXPointer( int32 &p ) { 846 | local int32 v = -1; 847 | if (p >= 0) { 848 | v = mdxPointer(p); 849 | } 850 | local string s; 851 | SPrintf(s, "%Xh", v); 852 | return s; 853 | } 854 | 855 | string ReadArrayDef( array_definition &a ) { 856 | local string s; 857 | SPrintf(s, "%s(%i)", ReadPointer(a.p_array_start), a.nr_used_entries); 858 | return s; 859 | } 860 | 861 | string ReadController( controller &key ) { 862 | local string s; 863 | SPrintf(s, "%i", key.type); 864 | return s; 865 | } 866 | 867 | string ReadNode( node &n ) { 868 | local string s = n.header.node_name; 869 | if (n.header.content.has_light) { 870 | s+="+Ligh"; 871 | } 872 | if (n.header.content.has_emitter) { 873 | s+="+Emit"; 874 | } 875 | if (n.header.content.has_camera) { 876 | s+="+Came"; 877 | } 878 | if (n.header.content.has_reference) { 879 | s+="+Refs"; 880 | } 881 | if (n.header.content.has_mesh) { 882 | s+="+Mesh"; 883 | } 884 | if (n.header.content.has_skin) { 885 | s+="+Skin"; 886 | } 887 | if (n.header.content.has_anim) { 888 | s+="+Anim"; 889 | } 890 | if (n.header.content.has_dangly) { 891 | s+="+Dang"; 892 | } 893 | if (n.header.content.has_aabb) { 894 | s+="+AABB"; 895 | } 896 | if (n.header.children.nr_used_entries > 0) { 897 | local string child_count; 898 | SPrintf(child_count, "(%d)", n.header.children.nr_used_entries); 899 | s+=child_count; 900 | } 901 | return s; 902 | } 903 | 904 | string ReadEvent( event &e ) { 905 | local string s; 906 | SPrintf(s, "%s(%f)", e.name, e.start); 907 | return s; 908 | } 909 | 910 | string ReadAnim( animation &anim ) { 911 | return anim.header.geometry.name; 912 | } 913 | -------------------------------------------------------------------------------- /templates/NWN2GR2.bt: -------------------------------------------------------------------------------- 1 | //-------------------------------------- 2 | //--- 010 Editor v6.0.3 Binary Template 3 | // 4 | // File: NWNGR2.bt 5 | // Author: Enrico Horn (Farmboy0) based upon work by berenm 6 | // Revision: 0.1 7 | // Purpose: to map the granny2 file from NWN2 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 . 16 | // 17 | //------------------------------------------------------ 18 | // Colors: 19 | //------------------------------------------------------ 20 | // Light Blue : gr2 header 21 | // Light Purple : gr2 info 22 | // Light Green : gr2 section header 23 | // Red : relocation 24 | // Blue : marshallings 25 | // Black : data(possibly packed) 26 | //------------------------------------------------------ 27 | 28 | typedef struct { 29 | uint32 magic[4] ; 30 | uint32 size; 31 | uint32 format; 32 | uint32 reserved[2]; 33 | } header ; 34 | 35 | typedef struct { 36 | uint32 version; 37 | uint32 file_size; 38 | uint32 crc32 ; 39 | 40 | uint32 sections_offset ; 41 | uint32 sections_count; 42 | 43 | uint32 type_section; 44 | uint32 type_offset ; 45 | 46 | uint32 root_section; 47 | uint32 root_offset ; 48 | 49 | uint32 tag; 50 | uint32 extra[4]; 51 | } info ; 52 | 53 | typedef struct { 54 | uint32 compression; 55 | 56 | uint32 data_offset ; 57 | uint32 data_size; 58 | 59 | uint32 decompressed_size; 60 | uint32 alignment; 61 | 62 | uint32 steps[2]; 63 | 64 | uint32 relocations_offset ; 65 | uint32 relocations_count; 66 | 67 | uint32 marshallings_offset ; 68 | uint32 marshallings_count; 69 | } section ; 70 | 71 | typedef struct { 72 | uint32 offset ; 73 | uint32 target_section; 74 | uint32 target_offset ; 75 | } relocation ; 76 | 77 | typedef struct { 78 | uint32 count; 79 | uint32 offset ; 80 | uint32 target_section; 81 | uint32 target_offset ; 82 | } marshalling ; 83 | 84 | typedef struct (section &sec) { 85 | local int r, m; 86 | 87 | if (sec.relocations_count > 0) { 88 | FSeek(sec.relocations_offset); 89 | for (r = 0; r < sec.relocations_count; r++) { 90 | relocation rel; 91 | } 92 | } 93 | 94 | if (sec.marshallings_count > 0) { 95 | FSeek(sec.marshallings_offset); 96 | for (m = 0; m < sec.marshallings_count; m++) { 97 | marshalling mar; 98 | } 99 | } 100 | 101 | if (sec.data_size > 0) { 102 | FSeek(sec.data_offset); 103 | byte data[sec.data_size] ; 104 | } 105 | 106 | } section_data; 107 | 108 | LittleEndian(); 109 | header h; 110 | info i; 111 | 112 | local int s; 113 | for (s = 0; s < i.sections_count; s++) { 114 | section sec; 115 | } 116 | for (s = 0; s < i.sections_count; s++) { 117 | if (sec[s].relocations_count > 0 || 118 | sec[s].marshallings_count > 0 || 119 | sec[s].data_size > 0) { 120 | 121 | section_data data(sec[s]); 122 | } 123 | } 124 | 125 | -------------------------------------------------------------------------------- /templates/NWN2MDB.bt: -------------------------------------------------------------------------------- 1 | //-------------------------------------- 2 | //--- 010 Editor v6.0.2 Binary Template 3 | // 4 | // File: NWN2MDB.bt 5 | // Author: Enrico Horn (Farmboy0) 6 | // Revision: 0.9 7 | // Purpose: to map the model format of the NWN2 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 . 16 | // 17 | //------------------------------------------------------ 18 | // Colors: 19 | //------------------------------------------------------ 20 | // Light Blue : mdb header 21 | // Light Purple : mdb keys 22 | 23 | // 0xA000A0 : reference packets (HOOK, HAIR, HELM) 24 | // Gray : rigd packet(mesh without animation) 25 | // Orange : skin packet(mesh with animation) 26 | // 0x8090FF : walk packet(mesh with walking info) 27 | // Dark Blue : collision packets (COL2, COL3, COLS) 28 | 29 | // Dark Red : trwh packet(terrain width and height) 30 | // Dark Green : trrn packet(terrain) 31 | // Dark Aqua : watr packet(water) 32 | // 0x8090FF : aswm packet(compressed walk mesh) 33 | //------------------------------------------------------ 34 | 35 | //------------------------------------------------------ 36 | // generic structures 37 | 38 | typedef struct { 39 | float x; 40 | float y; 41 | float z; 42 | } vertex ; 43 | 44 | typedef struct { 45 | float u; 46 | float v; 47 | float w; 48 | } texcoord ; 49 | 50 | typedef struct { 51 | float r; 52 | float g; 53 | float b; 54 | } color ; 55 | 56 | typedef struct { 57 | ubyte r; 58 | ubyte g; 59 | ubyte b; 60 | ubyte a; 61 | } rgba ; 62 | 63 | typedef struct { 64 | float w; 65 | float x; 66 | float y; 67 | float z; 68 | } quaternion ; 69 | 70 | typedef struct { 71 | vertex v[3]; 72 | } rot_matrix; 73 | 74 | typedef struct { 75 | uint32 alpha_test : 1; // 1 76 | uint32 alpha_blend : 1; // 2 77 | uint32 add_blend : 1; // 4 78 | uint32 env_mapping: 1; // 8 79 | uint32 cutscene_mesh : 1; // 10 80 | uint32 glow_effect : 1; // 20 81 | uint32 no_shadow : 1; // 40 82 | uint32 proj_texture : 1; // 80 83 | } material_flags; 84 | 85 | typedef struct { 86 | char map_diffuse[32]; 87 | char map_normal[32]; 88 | char map_tint[32]; 89 | char map_glow[32]; 90 | color color_diffuse; 91 | color color_specular; 92 | float specular_power; 93 | float specular_value; 94 | material_flags flags; 95 | } material; 96 | 97 | typedef struct { 98 | uint16 index_vertex[3]; 99 | } face; 100 | 101 | typedef struct(int count) { 102 | face f[count]; 103 | } face_array; 104 | 105 | //------------------------------------------------------ 106 | // generic header structure 107 | 108 | typedef struct { 109 | char nwn2[4]; 110 | uint16 version_major; 111 | uint16 version_minor; 112 | uint32 count_packets; 113 | } header_mdb ; 114 | 115 | typedef struct { 116 | char signature[4]; 117 | uint32 p_packet ; 118 | } packet_key ; 119 | 120 | //------------------------------------------------------ 121 | // mesh packet structures 122 | 123 | struct packet_mdb_mesh; 124 | 125 | typedef struct (packet_mdb_mesh &mesh) { 126 | vertex position; 127 | vertex normal; 128 | 129 | if (Strncmp(mesh.signature,"SKIN",4) == 0) { 130 | 131 | float bone_weights[4]; 132 | byte bone_indexes[4]; 133 | 134 | } 135 | if (Strncmp(mesh.signature,"RIGD",4) == 0 || 136 | Strncmp(mesh.signature,"SKIN",4) == 0) { 137 | 138 | vertex tangent; 139 | vertex binormal; 140 | 141 | } 142 | 143 | texcoord uvw; 144 | 145 | if (Strncmp(mesh.signature,"SKIN",4) == 0) { 146 | 147 | float bone_count; 148 | 149 | } 150 | 151 | } vertex_struct; 152 | 153 | typedef struct { 154 | uint32 walkable : 1; // 1 155 | uint32 reserved : 2; // 2,4 156 | uint32 dirt: 1; // 8 157 | uint32 grass : 1; // 10 158 | uint32 stone : 1; // 20 159 | uint32 wood : 1; // 40 160 | uint32 carpet : 1; // 80 161 | uint32 metal : 1; // 100 162 | uint32 swamp : 1; // 200 163 | uint32 mud : 1; // 400 164 | uint32 leafes : 1; // 800 165 | uint32 water : 1; // 1000 166 | uint32 puddles : 1; // 2000 167 | } face_WALK_flags; 168 | 169 | typedef struct { 170 | uint16 index_vertex[3]; 171 | face_WALK_flags flags; 172 | } face_WALK; 173 | 174 | typedef struct { 175 | char signature[4]; 176 | uint32 size_packet; 177 | char name[32]; 178 | 179 | if (Strncmp(signature,"SKIN",4) == 0) { 180 | 181 | char name_skeleton[32]; 182 | 183 | } 184 | if (Strncmp(signature,"WALK",4) == 0) { 185 | 186 | uint32 flags; 187 | 188 | } else { 189 | 190 | material mat; 191 | 192 | } 193 | 194 | uint32 count_verts; 195 | uint32 count_faces; 196 | 197 | vertex_struct v(this)[count_verts] ; 198 | 199 | if (Strncmp(signature,"WALK",4) == 0) { 200 | 201 | face_WALK f[count_faces] ; 202 | 203 | } else { 204 | 205 | face f[count_faces] ; 206 | 207 | } 208 | 209 | } packet_mdb_mesh ; 210 | 211 | //------------------------------------------------------ 212 | // sphere collision packet structures 213 | 214 | typedef struct { 215 | uint32 bone_index; 216 | float radius; 217 | } item_COLS; 218 | 219 | typedef struct { 220 | char signature[4]; 221 | uint32 size_packet; 222 | uint32 count_items; 223 | item_COLS c[count_items] ; 224 | } packet_mdb_COLS ; 225 | 226 | //------------------------------------------------------ 227 | // reference packet structures 228 | 229 | typedef struct { 230 | uint16 type; 231 | uint16 size; 232 | } packet_mdb_HOOK_flags; 233 | 234 | enum hair_shortening { 235 | hair_low, 236 | hair_short, 237 | hair_ponytail 238 | }; 239 | 240 | enum helm_hiding { 241 | helm_hiding_none, 242 | helm_hiding_hair, 243 | helm_hiding_hair_partial, 244 | helm_hiding_head 245 | }; 246 | 247 | typedef struct { 248 | char signature[4]; 249 | uint32 size_packet; 250 | char name[32]; 251 | 252 | if (Strncmp(signature,"HOOK",4) == 0) { 253 | 254 | packet_mdb_HOOK_flags flags; 255 | 256 | } else if (Strncmp(signature,"HAIR",4) == 0) { 257 | 258 | hair_shortening flags; 259 | 260 | } else if (Strncmp(signature,"HELM",4) == 0) { 261 | 262 | helm_hiding flags; 263 | 264 | } 265 | 266 | vertex position; 267 | rot_matrix orientation; 268 | } packet_mdb_refs ; 269 | 270 | //------------------------------------------------------ 271 | // terrain packet structures 272 | 273 | typedef struct { 274 | char signature[4]; 275 | uint32 size_packet; 276 | uint32 count_x; 277 | uint32 count_y; 278 | uint32 unknown; 279 | } packet_mdb_TRWH ; 280 | 281 | //------------------------------------------------------ 282 | 283 | typedef struct { 284 | vertex position; 285 | vertex normal; 286 | rgba color_tint; 287 | float xy_coord_0to10[2]; 288 | float xy_coord_0to1[2]; 289 | } vertex_struct_TRRN; 290 | 291 | typedef struct(int count) { 292 | vertex_struct_TRRN v[count]; 293 | } vertex_array_TRRN; 294 | 295 | typedef struct { 296 | vertex position; 297 | vertex direction; 298 | vertex dimension; 299 | } grass_blade; 300 | 301 | typedef struct(int count) { 302 | grass_blade blade[count]; 303 | } grass_blade_array; 304 | 305 | typedef struct { 306 | char name[32]; 307 | char texture[32]; 308 | uint32 count_blades ;; 309 | grass_blade_array blades(count_blades); 310 | } grass_struct_TRRN; 311 | 312 | typedef struct { 313 | char signature[4]; 314 | uint32 size_packet; 315 | char name[128]; 316 | char texture1[32]; 317 | char texture2[32]; 318 | char texture3[32]; 319 | char texture4[32]; 320 | char texture5[32]; 321 | char texture6[32]; 322 | texcoord uvw[6]; 323 | 324 | uint32 count_verts; 325 | uint32 count_faces; 326 | vertex_array_TRRN vertexes(count_verts) ; 327 | face_array faces(count_faces) ; 328 | 329 | uint32 size_dds1 ; 330 | if (size_dds1 > 0) { 331 | byte dds1[size_dds1] ; 332 | } 333 | 334 | uint32 size_dds2 ; 335 | if (size_dds2 > 0) { 336 | byte dds2[size_dds2] ; 337 | } 338 | 339 | uint32 size_grass; 340 | if (size_grass > 0) { 341 | grass_struct_TRRN grass[size_grass] ; 342 | } 343 | 344 | } packet_mdb_TRRN ; 345 | 346 | //------------------------------------------------------ 347 | 348 | typedef struct { 349 | char name[32]; 350 | float dir_x; 351 | float dir_y; 352 | float rate; 353 | float angle; 354 | } texture_WATR; 355 | 356 | typedef struct { 357 | vertex position; 358 | float xy_coord_0to5[2]; 359 | float xy_coord_0to1[2]; 360 | } vertex_struct_WATR; 361 | 362 | typedef struct(int count) { 363 | vertex_struct_WATR v[count]; 364 | } vertex_array_WATR; 365 | 366 | typedef struct { 367 | char signature[4]; 368 | uint32 size_packet; 369 | char name[128]; 370 | color water_color; 371 | float ripple_x; 372 | float ripple_y; 373 | float smoothness; 374 | float ref_bias; 375 | float ref_power; 376 | float always180f; 377 | float always05f; 378 | texture_WATR textures[3]; 379 | float offset_x; 380 | float offset_y; 381 | 382 | uint32 count_verts; 383 | uint32 count_faces; 384 | vertex_array_WATR vertexes(count_verts) ; 385 | face_array faces(count_faces) ; 386 | uint32 water_flag[count_faces] ; 387 | 388 | uint32 size_dds1 ; 389 | if (size_dds1 > 0) { 390 | byte dds1[size_dds1] ; 391 | } 392 | 393 | uint32 terrain_x ; 394 | uint32 terrain_y ; 395 | } packet_mdb_WATR ; 396 | 397 | //------------------------------------------------------ 398 | 399 | typedef struct { 400 | char signature[4]; 401 | uint32 size_packet; 402 | char compressed[4]; 403 | uint32 size_packed; 404 | uint32 size_unpacked; 405 | byte zip_data[size_packed]; 406 | } packet_mdb_ASWM ; 407 | 408 | 409 | //------------------------------------------------------ 410 | // main definition 411 | 412 | LittleEndian(); 413 | header_mdb header; 414 | packet_key keys[header.count_packets]; 415 | 416 | local int p; 417 | for (p = 0; p < header.count_packets; p++) { 418 | FSeek(keys[p].p_packet); 419 | 420 | if (Strncmp(keys[p].signature,"RIGD",4) == 0) { 421 | SetBackColor(cSilver); 422 | } else if (Strncmp(keys[p].signature,"SKIN",4) == 0) { 423 | SetBackColor(0x0090FF); 424 | } else if (Strncmp(keys[p].signature,"WALK",4) == 0) { 425 | SetBackColor(0x8090FF); 426 | } else if (Strncmp(keys[p].signature,"COL2",4) == 0) { 427 | SetBackColor(cDkBlue); 428 | } else if (Strncmp(keys[p].signature,"COL3",4) == 0) { 429 | SetBackColor(cDkBlue); 430 | } else if (Strncmp(keys[p].signature,"COLS",4) == 0) { 431 | SetBackColor(cDkBlue); 432 | } else if (Strncmp(keys[p].signature,"HOOK",4) == 0) { 433 | SetBackColor(0xA000A0); 434 | } else if (Strncmp(keys[p].signature,"HAIR",4) == 0) { 435 | SetBackColor(0xA000A0); 436 | } else if (Strncmp(keys[p].signature,"HELM",4) == 0) { 437 | SetBackColor(0xA000A0); 438 | } else if (Strncmp(keys[p].signature,"TRWH",4) == 0) { 439 | SetBackColor(cDkRed); 440 | } else if (Strncmp(keys[p].signature,"TRRN",4) == 0) { 441 | SetBackColor(cDkGreen); 442 | } else if (Strncmp(keys[p].signature,"WATR",4) == 0) { 443 | SetBackColor(cDkAqua); 444 | } else if (Strncmp(keys[p].signature,"ASWM",4) == 0) { 445 | SetBackColor(0x8090FF); 446 | } 447 | 448 | if (Strncmp(keys[p].signature,"COLS",4) == 0) { 449 | packet_mdb_COLS COLS; 450 | } else if (Strncmp(keys[p].signature,"HOOK",4) == 0 || 451 | Strncmp(keys[p].signature,"HAIR",4) == 0 || 452 | Strncmp(keys[p].signature,"HELM",4) == 0) { 453 | packet_mdb_refs REFS; 454 | } else if (Strncmp(keys[p].signature,"TRWH",4) == 0) { 455 | packet_mdb_TRWH TRWH; 456 | } else if (Strncmp(keys[p].signature,"TRRN",4) == 0) { 457 | packet_mdb_TRRN TRRN; 458 | } else if (Strncmp(keys[p].signature,"WATR",4) == 0) { 459 | packet_mdb_WATR WATR; 460 | } else if (Strncmp(keys[p].signature,"ASWM",4) == 0) { 461 | packet_mdb_ASWM ASWM; 462 | } else { 463 | packet_mdb_mesh MESH; 464 | } 465 | } 466 | 467 | //------------------------------------------------------ 468 | // read Methods 469 | 470 | string ReadVertex( vertex &v ) { 471 | local string s; 472 | SPrintf(s, "x=%f,y=%f,z=%f", v.x, v.y, v.z); 473 | return s; 474 | } 475 | 476 | string ReadTexCoord( texcoord &uvw ) { 477 | local string s; 478 | SPrintf(s, "u=%f,v=%f,w=%f", uvw.u, uvw.v, uvw.w); 479 | return s; 480 | } 481 | 482 | string ReadColor( color &c ) { 483 | local string s; 484 | SPrintf(s, "r=%f,g=%f,b=%f", c.r, c.g, c.b); 485 | return s; 486 | } 487 | 488 | string ReadRGBA( rgba &c ) { 489 | local string s; 490 | SPrintf(s, "r=%u,g=%u,b=%u,a=%u", c.r, c.g, c.b,c.a); 491 | return s; 492 | } 493 | 494 | string ReadQuaternion( quaternion &q ) { 495 | local string s; 496 | SPrintf(s, "%f,%f,%f,%f", q.w, q.x, q.y, q.z); 497 | return s; 498 | } 499 | 500 | string ReadPacketKey( packet_key &key ) { 501 | local string s; 502 | SPrintf(s, "%s(%Xh)", key.signature, key.p_packet); 503 | return s; 504 | } 505 | 506 | string ReadMesh(packet_mdb_mesh &mesh) { 507 | local string s; 508 | SPrintf(s, "%s(%s, v=%u, f=%u)", mesh.name, mesh.signature, mesh.count_verts, mesh.count_faces); 509 | return s; 510 | } 511 | 512 | string ReadRefs(packet_mdb_refs &refs) { 513 | local string s; 514 | SPrintf(s, "%s(%s)", refs.name, refs.signature); 515 | return s; 516 | } 517 | 518 | string ReadTRRN(packet_mdb_TRRN &trrn) { 519 | local string s; 520 | SPrintf(s, "%s(%i)", trrn.name,trrn.size_grass); 521 | return s; 522 | } 523 | 524 | string ReadWATR(packet_mdb_WATR &watr) { 525 | local string s; 526 | SPrintf(s, "%s", watr.name); 527 | return s; 528 | } 529 | --------------------------------------------------------------------------------