rw-parser-ng 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +88 -0
  3. package/lib/index.d.ts +6 -0
  4. package/lib/index.js +26 -0
  5. package/lib/renderware/RwFile.d.ts +10 -0
  6. package/lib/renderware/RwFile.js +33 -0
  7. package/lib/renderware/RwSections.d.ts +20 -0
  8. package/lib/renderware/RwSections.js +29 -0
  9. package/lib/renderware/dff/DffModelType.d.ts +5 -0
  10. package/lib/renderware/dff/DffModelType.js +9 -0
  11. package/lib/renderware/dff/DffParser.d.ts +155 -0
  12. package/lib/renderware/dff/DffParser.js +418 -0
  13. package/lib/renderware/errors/RwParseError.d.ts +6 -0
  14. package/lib/renderware/errors/RwParseError.js +34 -0
  15. package/lib/renderware/txd/TxdParser.d.ts +38 -0
  16. package/lib/renderware/txd/TxdParser.js +185 -0
  17. package/lib/renderware/utils/ImageDecoder.d.ts +23 -0
  18. package/lib/renderware/utils/ImageDecoder.js +512 -0
  19. package/lib/renderware/utils/ImageFormatEnums.d.ts +25 -0
  20. package/lib/renderware/utils/ImageFormatEnums.js +32 -0
  21. package/lib/renderware/utils/RwVersion.d.ts +7 -0
  22. package/lib/renderware/utils/RwVersion.js +30 -0
  23. package/lib/scripts/sort_models.d.ts +1 -0
  24. package/lib/scripts/sort_models.js +185 -0
  25. package/lib/scripts/sort_worker.d.ts +1 -0
  26. package/lib/scripts/sort_worker.js +140 -0
  27. package/lib/src/index.d.ts +8 -0
  28. package/lib/src/index.js +28 -0
  29. package/lib/src/renderware/RwFile.d.ts +10 -0
  30. package/lib/src/renderware/RwFile.js +33 -0
  31. package/lib/src/renderware/RwSections.d.ts +20 -0
  32. package/lib/src/renderware/RwSections.js +29 -0
  33. package/lib/src/renderware/common/types.d.ts +50 -0
  34. package/lib/src/renderware/common/types.js +2 -0
  35. package/lib/src/renderware/dff/DffModelType.d.ts +5 -0
  36. package/lib/src/renderware/dff/DffModelType.js +9 -0
  37. package/lib/src/renderware/dff/DffParser.d.ts +112 -0
  38. package/lib/src/renderware/dff/DffParser.js +418 -0
  39. package/lib/src/renderware/errors/RwParseError.d.ts +6 -0
  40. package/lib/src/renderware/errors/RwParseError.js +34 -0
  41. package/lib/src/renderware/ifp/IfpData.d.ts +28 -0
  42. package/lib/src/renderware/ifp/IfpData.js +9 -0
  43. package/lib/src/renderware/ifp/IfpParser.d.ts +12 -0
  44. package/lib/src/renderware/ifp/IfpParser.js +196 -0
  45. package/lib/src/renderware/txd/TxdParser.d.ts +38 -0
  46. package/lib/src/renderware/txd/TxdParser.js +185 -0
  47. package/lib/src/renderware/utils/ImageDecoder.d.ts +23 -0
  48. package/lib/src/renderware/utils/ImageDecoder.js +512 -0
  49. package/lib/src/renderware/utils/ImageFormatEnums.d.ts +25 -0
  50. package/lib/src/renderware/utils/ImageFormatEnums.js +32 -0
  51. package/lib/src/renderware/utils/RwVersion.d.ts +7 -0
  52. package/lib/src/renderware/utils/RwVersion.js +30 -0
  53. package/lib/src/utils/ByteStream.d.ts +17 -0
  54. package/lib/src/utils/ByteStream.js +65 -0
  55. package/lib/utils/ByteStream.d.ts +16 -0
  56. package/lib/utils/ByteStream.js +60 -0
  57. package/package.json +64 -0
  58. package/src/index.ts +10 -0
  59. package/src/renderware/RwFile.ts +22 -0
  60. package/src/renderware/RwSections.ts +27 -0
  61. package/src/renderware/common/types.ts +59 -0
  62. package/src/renderware/dff/DffModelType.ts +5 -0
  63. package/src/renderware/dff/DffParser.ts +596 -0
  64. package/src/renderware/errors/RwParseError.ts +11 -0
  65. package/src/renderware/ifp/IfpData.ts +33 -0
  66. package/src/renderware/ifp/IfpParser.ts +203 -0
  67. package/src/renderware/txd/TxdParser.ts +234 -0
  68. package/src/renderware/utils/ImageDecoder.ts +571 -0
  69. package/src/renderware/utils/ImageFormatEnums.ts +28 -0
  70. package/src/renderware/utils/RwVersion.ts +28 -0
  71. package/src/utils/ByteStream.ts +75 -0
  72. package/tsconfig.json +68 -0
@@ -0,0 +1,596 @@
1
+ import { RwFile } from '../RwFile';
2
+ import { RwSections } from '../RwSections';
3
+ import { RwParseStructureNotFoundError } from '../errors/RwParseError';
4
+ import RwVersion from '../utils/RwVersion';
5
+ import { DffModelType } from './DffModelType';
6
+ import { RwColor, RwMatrix3, RwMatrix4, RwSphere, RwTextureCoordinate, RwTriangle, RwVector3 } from '../common/types';
7
+
8
+ export interface RwDff {
9
+ modelType: DffModelType,
10
+ version: string,
11
+ versionNumber: number,
12
+ geometryList: RwGeometryList | null,
13
+ frameList: RwFrameList | null,
14
+ atomics: number[],
15
+ dummies: string[],
16
+ animNodes: RwAnimNode[],
17
+ }
18
+
19
+ export interface RwClump {
20
+ atomicCount: number,
21
+ lightCount?: number,
22
+ cameraCount?: number,
23
+ }
24
+
25
+ export interface RwAnimNode {
26
+ boneId: number,
27
+ bonesCount: number,
28
+ bones: RwBone[],
29
+ }
30
+
31
+ export interface RwBone {
32
+ boneId: number,
33
+ boneIndex: number,
34
+ flags: number,
35
+ }
36
+
37
+
38
+ export interface RwFrame {
39
+ rotationMatrix: RwMatrix3,
40
+ coordinatesOffset: RwVector3,
41
+ parentFrame: number,
42
+ }
43
+
44
+ export interface RwFrameList {
45
+ frameCount: number,
46
+ frames: RwFrame[],
47
+ }
48
+
49
+ export interface RwTexture {
50
+ textureFiltering: number,
51
+ uAddressing: number,
52
+ vAddressing: number,
53
+ usesMipLevels: boolean,
54
+ textureName: string,
55
+ }
56
+
57
+ export interface RwMaterial {
58
+ color: RwColor,
59
+ isTextured: boolean,
60
+ ambient?: number,
61
+ specular?: number,
62
+ diffuse?: number,
63
+ texture?: RwTexture,
64
+ }
65
+
66
+ export interface RwMaterialList {
67
+ materialInstanceCount: number,
68
+ materialData: RwMaterial[],
69
+ }
70
+
71
+ export interface RwGeometry {
72
+ vertexColorInformation: RwColor[],
73
+ textureCoordinatesCount: number,
74
+ textureMappingInformation: RwTextureCoordinate[][],
75
+ hasVertices: boolean,
76
+ hasNormals: boolean,
77
+ triangleInformation: RwTriangle[],
78
+ vertexInformation: RwVector3[],
79
+ normalInformation: RwVector3[],
80
+ boundingSphere?: RwSphere,
81
+ materialList: RwMaterialList,
82
+ binMesh: RwBinMesh,
83
+ skin?: RwSkin,
84
+ }
85
+
86
+ export interface RwGeometryList {
87
+ geometricObjectCount: number,
88
+ geometries: RwGeometry[],
89
+ }
90
+
91
+ export interface RwAtomic {
92
+ frameIndex: number,
93
+ geometryIndex: number,
94
+ flags: number,
95
+ }
96
+
97
+ export interface RwBinMesh {
98
+ meshCount: number,
99
+ meshes: RwMesh[],
100
+ }
101
+
102
+ export interface RwSkin {
103
+ boneCount: number,
104
+ usedBoneCount: number,
105
+ maxWeightsPerVertex: number,
106
+ boneVertexIndices: number[][],
107
+ vertexWeights: number[][],
108
+ inverseBoneMatrices: RwMatrix4[],
109
+ }
110
+
111
+ export interface RwMesh {
112
+ materialIndex: number,
113
+ indexCount: number,
114
+ indices: number[],
115
+ }
116
+
117
+ export class DffParser extends RwFile {
118
+
119
+ constructor(buffer: Buffer) {
120
+ super(buffer);
121
+ }
122
+
123
+ parse(): RwDff {
124
+ let version: string | undefined;
125
+ let versionNumber: number | undefined;
126
+ let atomics: number[] = [];
127
+ let dummies: string[] = [];
128
+ let animNodes: RwAnimNode[] = [];
129
+ let geometryList: RwGeometryList | null = null;
130
+ let frameList: RwFrameList | null = null;
131
+
132
+ while (this.getPosition() < this.getSize()) {
133
+ const header = this.readSectionHeader();
134
+
135
+ if (header.sectionType === 0) {
136
+ break;
137
+ }
138
+
139
+ if (header.sectionSize == 0) {
140
+ continue;
141
+ }
142
+
143
+ switch (header.sectionType) {
144
+ case RwSections.RwClump:
145
+ // Multiple clumps are used in SA player models, so we should eventually support it
146
+ versionNumber = RwVersion.unpackVersion(header.versionNumber);
147
+ version = RwVersion.versions[versionNumber];
148
+ break;
149
+ case RwSections.RwFrameList:
150
+ frameList = this.readFrameList();
151
+ break;
152
+ case RwSections.RwExtension:
153
+ const extensionHeader = this.readSectionHeader();
154
+ switch (extensionHeader.sectionType) {
155
+ case RwSections.RwNodeName:
156
+ dummies.push(this.readString(extensionHeader.sectionSize));
157
+ break;
158
+ case RwSections.RwAnim:
159
+ animNodes.push(this.readAnimNode());
160
+ break;
161
+ default:
162
+ console.debug(`Extension type ${extensionHeader.sectionType} (${extensionHeader.sectionType.toString(16)}) not found at offset (${this.getPosition().toString(16)}). Skipping ${extensionHeader.sectionSize} bytes.`);
163
+ this.skip(extensionHeader.sectionSize);
164
+ break;
165
+ }
166
+ break;
167
+ case RwSections.RwGeometryList:
168
+ geometryList = this.readGeometryList();
169
+ break;
170
+ case RwSections.RwAtomic:
171
+ const atomic = this.readAtomic();
172
+ atomics[atomic.geometryIndex] = atomic.frameIndex;
173
+ break;
174
+ case RwSections.RwNodeName:
175
+ // For some reason, this frame is outside RwExtension.
176
+ dummies.push(this.readString(header.sectionSize));
177
+ break;
178
+ case RwSections.RwAnim:
179
+ // For III / VC models
180
+ animNodes.push(this.readAnimNode());
181
+ break;
182
+ default:
183
+ console.debug(`Section type ${header.sectionType} (${header.sectionType.toString(16)}) not found at offset (${this.getPosition().toString(16)}). Skipping ${header.sectionSize} bytes.`);
184
+ this.skip(header.sectionSize);
185
+ break;
186
+ }
187
+ }
188
+
189
+ if (!version || !versionNumber) {
190
+ throw new RwParseStructureNotFoundError('version');
191
+ }
192
+
193
+ let modelType = DffModelType.GENERIC;
194
+ if (geometryList?.geometries.some(g => g.skin)) {
195
+ modelType = DffModelType.SKIN;
196
+ } else if (dummies.some(d => d.toLowerCase().includes('wheel') || d.toLowerCase().includes('chassis'))) {
197
+ modelType = DffModelType.VEHICLE;
198
+ }
199
+
200
+ return {
201
+ modelType,
202
+ version: version,
203
+ versionNumber: versionNumber,
204
+ geometryList: geometryList,
205
+ frameList: frameList,
206
+ atomics: atomics,
207
+ dummies: dummies,
208
+ animNodes: animNodes,
209
+ };
210
+ }
211
+
212
+ public readClump(): RwClump {
213
+ const { versionNumber } = this.readSectionHeader();
214
+
215
+ const atomicCount = this.readUint32();
216
+
217
+ let lightCount;
218
+ let cameraCount;
219
+ if (versionNumber > 0x33000) {
220
+ lightCount = this.readUint32();
221
+ cameraCount = this.readUint32();
222
+ }
223
+
224
+ return { atomicCount, lightCount, cameraCount };
225
+ }
226
+
227
+ public readFrameList(): RwFrameList {
228
+ this.readSectionHeader();
229
+
230
+ const frameCount = this.readUint32();
231
+
232
+ let frames: RwFrame[] = [];
233
+
234
+ for (let i = 0; i < frameCount; i++) {
235
+ // All these could probably be moved to readFrameData()
236
+
237
+ const rotationMatrix: RwMatrix3 = {
238
+ right: { x: this.readFloat(), y: this.readFloat(), z: this.readFloat() },
239
+ up: { x: this.readFloat(), y: this.readFloat(), z: this.readFloat() },
240
+ at: { x: this.readFloat(), y: this.readFloat(), z: this.readFloat() },
241
+ }
242
+
243
+ const coordinatesOffset: RwVector3 = { x: this.readFloat(), y: this.readFloat(), z: this.readFloat() };
244
+
245
+ const parentFrame = this.readInt32();
246
+
247
+ // Skip matrix creation internal flags
248
+ // They are read by the game but are not used
249
+ this.skip(4);
250
+
251
+ frames.push({ rotationMatrix, coordinatesOffset, parentFrame });
252
+ }
253
+
254
+ return { frameCount, frames };
255
+ }
256
+
257
+ public readGeometryList(): RwGeometryList {
258
+ const header = this.readSectionHeader();
259
+
260
+ const geometricObjectCount = this.readUint32();
261
+
262
+ let geometries: RwGeometry[] = [];
263
+
264
+ for (let i = 0; i < geometricObjectCount; i++) {
265
+ this.readSectionHeader();
266
+ this.readSectionHeader();
267
+ const versionNumber = RwVersion.unpackVersion(header.versionNumber);
268
+ const geometryData = this.readGeometry(versionNumber);
269
+ geometries.push(geometryData);
270
+ }
271
+
272
+ return { geometricObjectCount, geometries };
273
+ }
274
+
275
+ public readGeometry(versionNumber: number): RwGeometry {
276
+ const flags = this.readUint16();
277
+ const textureCoordinatesCount = this.readUint8();
278
+ const _nativeGeometryFlags = this.readUint8();
279
+ const triangleCount = this.readUint32();
280
+ const vertexCount = this.readUint32();
281
+ const _morphTargetCount = this.readUint32();
282
+
283
+ // Surface properties
284
+ let _ambient;
285
+ let _specular;
286
+ let _diffuse;
287
+
288
+ if (versionNumber < 0x34000) {
289
+ _ambient = this.readFloat();
290
+ _specular = this.readFloat();
291
+ _diffuse = this.readFloat();
292
+ }
293
+
294
+ const _isTriangleStrip = (flags & (1 << 0)) !== 0;
295
+ const _vertexTranslation = (flags & (1 << 1)) !== 0;
296
+ const isTexturedUV1 = (flags & (1 << 2)) !== 0;
297
+ const isGeometryPrelit = (flags & (1 << 3)) !== 0;
298
+ const _hasNormals = (flags & (1 << 4)) !== 0;
299
+ const _isGeometryLit = (flags & (1 << 5)) !== 0;
300
+ const _shouldModulateMaterialColor = (flags & (1 << 6)) !== 0;
301
+ const isTexturedUV2 = (flags & (1 << 7)) !== 0;
302
+
303
+ const vertexColorInformation: RwColor[] = [];
304
+ const textureMappingInformation: RwTextureCoordinate[][] = [];
305
+ const triangleInformation: RwTriangle[] = [];
306
+
307
+ // Geometry is marked as prelit
308
+ if (isGeometryPrelit) {
309
+ for (let i = 0; i < vertexCount; i++) {
310
+ vertexColorInformation[i] = { r: this.readUint8(), g: this.readUint8(), b: this.readUint8(), a: this.readUint8() };
311
+ }
312
+ }
313
+
314
+ // Geometry either has first or second texture
315
+ if (isTexturedUV1 || isTexturedUV2) {
316
+ for (let textureCoordinateIndex = 0; textureCoordinateIndex < textureCoordinatesCount; textureCoordinateIndex++) {
317
+ textureMappingInformation[textureCoordinateIndex] = [];
318
+ for (let vertexIndex = 0; vertexIndex < vertexCount; vertexIndex++) {
319
+ textureMappingInformation[textureCoordinateIndex][vertexIndex] = { u: this.readFloat(), v: this.readFloat() };
320
+ }
321
+ }
322
+ }
323
+
324
+ for (let i = 0; i < triangleCount; i++) {
325
+ // Information is written in this order
326
+ const vertex2 = this.readUint16();
327
+ const vertex1 = this.readUint16();
328
+ const materialId = this.readUint16();
329
+ const vertex3 = this.readUint16();
330
+ triangleInformation[i] = { vector: { x: vertex1, y: vertex2, z: vertex3 }, materialId: materialId }
331
+ }
332
+
333
+ // We are sure that there's only one morph target, but if
334
+ // we are wrong, we have to loop these through morphTargetCount
335
+
336
+ const boundingSphere: RwSphere = {
337
+ vector: { x: this.readFloat(), y: this.readFloat(), z: this.readFloat() },
338
+ radius: this.readFloat(),
339
+ };
340
+
341
+ const hasVertices = !!this.readUint32();
342
+ const hasNormals = !!this.readUint32();
343
+
344
+ const vertexInformation = [];
345
+ if (hasVertices) {
346
+ for (let i = 0; i < vertexCount; i++) {
347
+ vertexInformation[i] = { x: this.readFloat(), y: this.readFloat(), z: this.readFloat() };
348
+ }
349
+ }
350
+
351
+ const normalInformation = [];
352
+ if (hasNormals) {
353
+ for (let i = 0; i < vertexCount; i++) {
354
+ normalInformation[i] = { x: this.readFloat(), y: this.readFloat(), z: this.readFloat() };
355
+ }
356
+ }
357
+
358
+ let materialList = this.readMaterialList();
359
+ let sectionSize = this.readSectionHeader().sectionSize;
360
+ let position = this.getPosition();
361
+ let binMesh = this.readBinMesh();
362
+ let skin = undefined;
363
+
364
+ if (this.readSectionHeader().sectionType == RwSections.RwSkin) {
365
+ skin = this.readSkin(vertexCount);
366
+ }
367
+
368
+ this.setPosition(position + sectionSize);
369
+
370
+ return {
371
+ textureCoordinatesCount,
372
+ textureMappingInformation,
373
+ boundingSphere,
374
+ hasVertices,
375
+ hasNormals,
376
+ vertexColorInformation,
377
+ vertexInformation,
378
+ normalInformation,
379
+ triangleInformation,
380
+ materialList,
381
+ binMesh,
382
+ skin,
383
+ };
384
+ }
385
+
386
+ public readBinMesh(): RwBinMesh {
387
+ this.readSectionHeader();
388
+
389
+ // Flags (0: triangle list, 1: triangle strip)
390
+ this.skip(4);
391
+
392
+ const meshCount = this.readUint32();
393
+
394
+ // Total number of indices
395
+ this.skip(4);
396
+
397
+ const meshes: RwMesh[] = [];
398
+
399
+ for (let i = 0; i < meshCount; i++) {
400
+ meshes.push(this.readMesh());
401
+ }
402
+
403
+ return {
404
+ meshCount, meshes
405
+ };
406
+ }
407
+
408
+ public readSkin(vertexCount : number): RwSkin {
409
+ const boneCount = this.readUint8();
410
+ const usedBoneCount = this.readUint8();
411
+ const maxWeightsPerVertex = this.readUint8();
412
+
413
+ this.skip(1); // Padding
414
+ this.skip(usedBoneCount); // Skipping special indices
415
+
416
+ const boneVertexIndices: number[][] = [];
417
+ const vertexWeights: number[][] = [];
418
+ const inverseBoneMatrices: RwMatrix4[] = [];
419
+
420
+ for (let i = 0; i < vertexCount; i++) {
421
+ const indices: number[] = [];
422
+ for (let j = 0; j < 4; j++) {
423
+ indices.push(this.readUint8());
424
+ }
425
+ boneVertexIndices.push(indices);
426
+ }
427
+
428
+ for (let i = 0; i < vertexCount; i++) {
429
+ const weights: number[] = [];
430
+ for (let j = 0; j < 4; j++) {
431
+ weights.push(this.readFloat());
432
+ }
433
+ vertexWeights.push(weights);
434
+ }
435
+
436
+ for (let i = 0; i < boneCount; i++) {
437
+ const matrix4x4: RwMatrix4 = {
438
+ right: { x: this.readFloat(), y: this.readFloat(), z: this.readFloat(), t: this.readFloat() },
439
+ up: { x: this.readFloat(), y: this.readFloat(), z: this.readFloat(), t: this.readFloat() },
440
+ at: { x: this.readFloat(), y: this.readFloat(), z: this.readFloat(), t: this.readFloat() },
441
+ transform: { x: this.readFloat(), y: this.readFloat(), z: this.readFloat(), t: this.readFloat() },
442
+ };
443
+
444
+ inverseBoneMatrices.push(matrix4x4);
445
+ }
446
+
447
+ return {
448
+ boneCount,
449
+ usedBoneCount,
450
+ maxWeightsPerVertex,
451
+ boneVertexIndices,
452
+ vertexWeights,
453
+ inverseBoneMatrices,
454
+ }
455
+ }
456
+
457
+ public readAnimNode() :RwAnimNode {
458
+ this.skip(4); // Skipping AnimVersion property (0x100)
459
+ const boneId = this.readInt32();
460
+ const boneCount = this.readInt32();
461
+ const bones :RwBone[] = [];
462
+
463
+ if (boneId == 0) {
464
+ this.skip(8); // Skipping flags and keyFrameSize properties
465
+ }
466
+
467
+ if (boneCount > 0) {
468
+ for (let i = 0; i < boneCount; i++){
469
+ bones.push({
470
+ boneId: this.readInt32(),
471
+ boneIndex: this.readInt32(),
472
+ flags: this.readInt32()
473
+ });
474
+ }
475
+ }
476
+
477
+ return {
478
+ boneId: boneId,
479
+ bonesCount: boneCount,
480
+ bones: bones
481
+ }
482
+ }
483
+
484
+ public readMesh(): RwMesh {
485
+ const indexCount = this.readUint32();
486
+ const materialIndex = this.readUint32();
487
+
488
+ const indices: number[] = [];
489
+
490
+ for (let i = 0; i < indexCount; i++) {
491
+ indices.push(this.readUint32());
492
+ }
493
+
494
+ return {
495
+ indexCount, materialIndex, indices
496
+ };
497
+ }
498
+
499
+ public readMaterialList(): RwMaterialList {
500
+ this.readSectionHeader();
501
+ this.readSectionHeader();
502
+
503
+ const materialInstanceCount = this.readUint32();
504
+ const materialIndices: number[] = [];
505
+
506
+ for (let i = 0; i < materialInstanceCount; i++) {
507
+ const materialIndex = this.readInt32();
508
+ materialIndices.push(materialIndex);
509
+ }
510
+
511
+ const materialData: RwMaterial[] = [];
512
+
513
+ for (let i = 0; i < materialInstanceCount; i++) {
514
+ let materialIndex = materialIndices[i];
515
+
516
+ if (materialIndex == -1) {
517
+ materialData.push(this.readMaterial());
518
+ } else {
519
+ materialData.push(materialData[materialIndex]);
520
+ }
521
+ }
522
+
523
+ return { materialInstanceCount, materialData };
524
+ }
525
+
526
+ public readMaterial(): RwMaterial {
527
+ this.readSectionHeader();
528
+ const header = this.readSectionHeader();
529
+
530
+ // Flags - not used
531
+ this.skip(4);
532
+
533
+ const color: RwColor = { r: this.readUint8(), g: this.readUint8(), b: this.readUint8(), a: this.readUint8() };
534
+
535
+ // Unknown - not used
536
+ this.skip(4);
537
+
538
+ const isTextured = this.readUint32() > 0;
539
+
540
+ // Surface properties
541
+ let ambient;
542
+ let specular;
543
+ let diffuse;
544
+
545
+ if (header.versionNumber > 0x30400) {
546
+ ambient = this.readFloat();
547
+ specular = this.readFloat();
548
+ diffuse = this.readFloat();
549
+ }
550
+
551
+ let texture;
552
+
553
+ if (isTextured) {
554
+ texture = this.readTexture();
555
+ }
556
+
557
+ // Skip various unused extensions
558
+ this.skip(this.readSectionHeader().sectionSize);
559
+
560
+ return { color, isTextured, ambient, specular, diffuse, texture };
561
+ }
562
+
563
+ public readTexture(): RwTexture {
564
+ this.readSectionHeader();
565
+ this.readSectionHeader();
566
+
567
+ const textureData = this.readUint32();
568
+
569
+ const textureFiltering = (textureData & 0xFF);
570
+ const uAddressing = (textureData & 0xF00) >> 8;
571
+ const vAddressing = (textureData & 0xF000) >> 12;
572
+ const usesMipLevels = (textureData & (1 << 16)) !== 0;
573
+
574
+ let textureNameSize = this.readSectionHeader().sectionSize;
575
+ const textureName = this.readString(textureNameSize);
576
+
577
+ // Skip various unused extensions
578
+ this.skip(this.readSectionHeader().sectionSize);
579
+ this.skip(this.readSectionHeader().sectionSize);
580
+
581
+ return { textureFiltering, uAddressing, vAddressing, usesMipLevels, textureName };
582
+ }
583
+
584
+ public readAtomic(): RwAtomic {
585
+ this.readSectionHeader();
586
+
587
+ const frameIndex = this.readUint32();
588
+ const geometryIndex = this.readUint32();
589
+ const flags = this.readUint32();
590
+
591
+ // Skip unused bytes
592
+ this.skip(4);
593
+
594
+ return { frameIndex, geometryIndex, flags };
595
+ }
596
+ }
@@ -0,0 +1,11 @@
1
+ export class RwParseError extends Error {
2
+ constructor(message?: string) {
3
+ super(message);
4
+ }
5
+ }
6
+
7
+ export class RwParseStructureNotFoundError extends RwParseError {
8
+ constructor(structureName: string) {
9
+ super(`Structure ${structureName} not found.`);
10
+ }
11
+ }
@@ -0,0 +1,33 @@
1
+ import { RwQuaternion, RwVector3 } from "../common/types";
2
+
3
+ export enum IfpVersion {
4
+ ANP3,
5
+ ANPK,
6
+ UNSUPPORTED,
7
+ }
8
+
9
+ export interface RwIfp {
10
+ version: IfpVersion,
11
+ name: string,
12
+ animations: RwIfpAnimation[],
13
+ }
14
+
15
+ export interface RwIfpAnimation {
16
+ name: string,
17
+ bones: RwIfpBone[],
18
+ }
19
+
20
+ export interface RwIfpBone {
21
+ name: string,
22
+ keyframeType: string,
23
+ useBoneId: boolean,
24
+ boneId: number,
25
+ keyframes: RwIfpKeyframe[],
26
+ }
27
+
28
+ export interface RwIfpKeyframe {
29
+ time: number,
30
+ position: RwVector3,
31
+ rotation: RwQuaternion,
32
+ scale: RwVector3,
33
+ }