rw-parser-ng 2.1.0 → 2.3.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 (142) hide show
  1. package/README.md +252 -52
  2. package/lib/codecs/bitmap.d.ts +6 -0
  3. package/lib/codecs/bitmap.js +77 -0
  4. package/lib/codecs/color.d.ts +6 -0
  5. package/lib/codecs/color.js +43 -0
  6. package/lib/codecs/dxt.d.ts +3 -0
  7. package/lib/codecs/dxt.js +212 -0
  8. package/lib/codecs/formats.d.ts +25 -0
  9. package/lib/codecs/formats.js +32 -0
  10. package/lib/codecs/index.d.ts +5 -0
  11. package/lib/codecs/index.js +21 -0
  12. package/lib/codecs/palette.d.ts +6 -0
  13. package/lib/codecs/palette.js +86 -0
  14. package/lib/common/types.d.ts +50 -0
  15. package/lib/common/types.js +2 -0
  16. package/lib/core/byte-stream.d.ts +20 -0
  17. package/lib/core/byte-stream.js +71 -0
  18. package/lib/core/errors.d.ts +5 -0
  19. package/lib/core/errors.js +34 -0
  20. package/lib/core/index.d.ts +5 -0
  21. package/lib/core/index.js +21 -0
  22. package/lib/core/rw-file.d.ts +9 -0
  23. package/lib/core/rw-file.js +36 -0
  24. package/lib/core/rw-sections.d.ts +24 -0
  25. package/lib/core/rw-sections.js +28 -0
  26. package/lib/core/rw-version.d.ts +3 -0
  27. package/lib/core/rw-version.js +27 -0
  28. package/lib/dff/dff-model-type.d.ts +5 -0
  29. package/lib/dff/dff-model-type.js +9 -0
  30. package/lib/dff/index.d.ts +4 -0
  31. package/lib/dff/index.js +20 -0
  32. package/lib/dff/parser.d.ts +5 -0
  33. package/lib/dff/parser.js +154 -0
  34. package/lib/dff/readers/2d-effect.d.ts +3 -0
  35. package/lib/dff/readers/2d-effect.js +211 -0
  36. package/lib/dff/readers/geometry.d.ts +4 -0
  37. package/lib/dff/readers/geometry.js +229 -0
  38. package/lib/dff/readers/index.d.ts +5 -0
  39. package/lib/dff/readers/index.js +21 -0
  40. package/lib/dff/readers/material.d.ts +5 -0
  41. package/lib/dff/readers/material.js +72 -0
  42. package/lib/dff/readers/mesh.d.ts +5 -0
  43. package/lib/dff/readers/mesh.js +84 -0
  44. package/lib/dff/readers/structure.d.ts +6 -0
  45. package/lib/dff/readers/structure.js +67 -0
  46. package/lib/dff/types.d.ts +237 -0
  47. package/lib/dff/types.js +44 -0
  48. package/lib/fxp/index.d.ts +2 -0
  49. package/lib/fxp/index.js +18 -0
  50. package/lib/fxp/info-parser.d.ts +2 -0
  51. package/lib/fxp/info-parser.js +169 -0
  52. package/lib/fxp/parser.d.ts +3 -0
  53. package/lib/fxp/parser.js +285 -0
  54. package/lib/fxp/types/common.d.ts +36 -0
  55. package/lib/fxp/types/common.js +2 -0
  56. package/lib/fxp/types/index.d.ts +3 -0
  57. package/lib/fxp/types/index.js +19 -0
  58. package/lib/fxp/types/info.d.ts +186 -0
  59. package/lib/fxp/types/info.js +2 -0
  60. package/lib/fxp/types/system.d.ts +27 -0
  61. package/lib/fxp/types/system.js +2 -0
  62. package/lib/ifp/index.d.ts +4 -0
  63. package/lib/ifp/index.js +20 -0
  64. package/lib/ifp/parser.d.ts +5 -0
  65. package/lib/ifp/parser.js +38 -0
  66. package/lib/ifp/readers/anp3.d.ts +3 -0
  67. package/lib/ifp/readers/anp3.js +56 -0
  68. package/lib/ifp/readers/anpk.d.ts +3 -0
  69. package/lib/ifp/readers/anpk.js +86 -0
  70. package/lib/ifp/types.d.ts +28 -0
  71. package/lib/ifp/types.js +9 -0
  72. package/lib/index.d.ts +6 -9
  73. package/lib/index.js +6 -13
  74. package/lib/renderware/fxp/FxpData.d.ts +246 -0
  75. package/lib/renderware/fxp/FxpData.js +2 -0
  76. package/lib/renderware/fxp/FxpParser.d.ts +3 -0
  77. package/lib/renderware/fxp/FxpParser.js +512 -0
  78. package/lib/renderware/fxp/index.d.ts +2 -0
  79. package/lib/renderware/fxp/index.js +18 -0
  80. package/lib/renderware/txd/TxdParser.js +3 -2
  81. package/lib/txd/bitmap-decoder.d.ts +3 -0
  82. package/lib/txd/bitmap-decoder.js +57 -0
  83. package/lib/txd/index.d.ts +3 -0
  84. package/lib/txd/index.js +19 -0
  85. package/lib/txd/parser.d.ts +8 -0
  86. package/lib/txd/parser.js +122 -0
  87. package/lib/txd/types.d.ts +27 -0
  88. package/lib/txd/types.js +2 -0
  89. package/package.json +16 -9
  90. package/src/codecs/bitmap.ts +75 -0
  91. package/src/codecs/color.ts +40 -0
  92. package/src/codecs/dxt.ts +243 -0
  93. package/src/codecs/formats.ts +28 -0
  94. package/src/codecs/index.ts +5 -0
  95. package/src/codecs/palette.ts +106 -0
  96. package/src/common/types.ts +59 -0
  97. package/src/core/byte-stream.ts +87 -0
  98. package/src/core/errors.ts +7 -0
  99. package/src/core/index.ts +5 -0
  100. package/src/core/rw-file.ts +23 -0
  101. package/src/core/rw-sections.ts +24 -0
  102. package/src/core/rw-version.ts +24 -0
  103. package/src/dff/dff-model-type.ts +5 -0
  104. package/src/dff/index.ts +4 -0
  105. package/src/dff/parser.ts +142 -0
  106. package/src/dff/readers/2d-effect.ts +265 -0
  107. package/src/dff/readers/geometry.ts +264 -0
  108. package/src/dff/readers/index.ts +5 -0
  109. package/src/dff/readers/material.ts +89 -0
  110. package/src/dff/readers/mesh.ts +93 -0
  111. package/src/dff/readers/structure.ts +79 -0
  112. package/src/dff/types.ts +278 -0
  113. package/src/fxp/index.ts +2 -0
  114. package/src/fxp/info-parser.ts +166 -0
  115. package/src/fxp/parser.ts +323 -0
  116. package/src/fxp/types/common.ts +23 -0
  117. package/src/fxp/types/index.ts +3 -0
  118. package/src/fxp/types/info.ts +251 -0
  119. package/src/fxp/types/system.ts +30 -0
  120. package/src/ifp/index.ts +4 -0
  121. package/src/ifp/parser.ts +16 -0
  122. package/src/ifp/readers/anp3.ts +75 -0
  123. package/src/ifp/readers/anpk.ts +110 -0
  124. package/src/ifp/types.ts +33 -0
  125. package/src/index.ts +6 -12
  126. package/src/txd/bitmap-decoder.ts +73 -0
  127. package/src/txd/index.ts +3 -0
  128. package/src/txd/parser.ts +148 -0
  129. package/src/txd/types.ts +29 -0
  130. package/src/renderware/RwFile.ts +0 -26
  131. package/src/renderware/RwSections.ts +0 -27
  132. package/src/renderware/common/types.ts +0 -59
  133. package/src/renderware/dff/DffModelType.ts +0 -5
  134. package/src/renderware/dff/DffParser.ts +0 -611
  135. package/src/renderware/errors/RwParseError.ts +0 -11
  136. package/src/renderware/ifp/IfpData.ts +0 -33
  137. package/src/renderware/ifp/IfpParser.ts +0 -203
  138. package/src/renderware/txd/TxdParser.ts +0 -234
  139. package/src/renderware/utils/ImageDecoder.ts +0 -571
  140. package/src/renderware/utils/ImageFormatEnums.ts +0 -28
  141. package/src/renderware/utils/RwVersion.ts +0 -28
  142. package/src/utils/ByteStream.ts +0 -80
@@ -0,0 +1,106 @@
1
+ export function lum8(data: Uint8Array, width: number, height: number): Uint8Array {
2
+ const rgba = new Uint8Array(4 * width * height);
3
+ for (let i = 0; i < data.length; i++) {
4
+ const offset = i * 4;
5
+ const lum = data[i];
6
+ rgba[offset] = lum;
7
+ rgba[offset + 1] = lum;
8
+ rgba[offset + 2] = lum;
9
+ rgba[offset + 3] = 0xff;
10
+ }
11
+ return rgba;
12
+ }
13
+
14
+ export function lum8a8(data: Uint8Array, width: number, height: number): Uint8Array {
15
+ const rgba = new Uint8Array(4 * width * height);
16
+ let offset = 0;
17
+ for (let i = 0; i < data.length; i += 2) {
18
+ const lum = data[i],
19
+ alpha = data[i + 1];
20
+ rgba[offset++] = lum;
21
+ rgba[offset++] = lum;
22
+ rgba[offset++] = lum;
23
+ rgba[offset++] = alpha;
24
+ }
25
+ return rgba;
26
+ }
27
+
28
+ export function pal4(
29
+ data: Uint8Array,
30
+ palette: Uint8Array,
31
+ width: number,
32
+ height: number,
33
+ ): Uint8Array {
34
+ const rgba = new Uint8Array(4 * width * height);
35
+ let offset = 0;
36
+ for (let i = 0; i < data.length; i++) {
37
+ const idx1 = (data[i] >> 4) & 0xf,
38
+ idx2 = data[i] & 0xf;
39
+ rgba[offset++] = palette[idx1 * 4];
40
+ rgba[offset++] = palette[idx1 * 4 + 1];
41
+ rgba[offset++] = palette[idx1 * 4 + 2];
42
+ rgba[offset++] = palette[idx1 * 4 + 3];
43
+ rgba[offset++] = palette[idx2 * 4];
44
+ rgba[offset++] = palette[idx2 * 4 + 1];
45
+ rgba[offset++] = palette[idx2 * 4 + 2];
46
+ rgba[offset++] = palette[idx2 * 4 + 3];
47
+ }
48
+ return rgba;
49
+ }
50
+
51
+ export function pal4NoAlpha(
52
+ data: Uint8Array,
53
+ palette: Uint8Array,
54
+ width: number,
55
+ height: number,
56
+ ): Uint8Array {
57
+ const rgba = new Uint8Array(4 * width * height);
58
+ let offset = 0;
59
+ for (let i = 0; i < data.length; i++) {
60
+ const idx1 = (data[i] >> 4) & 0xf,
61
+ idx2 = data[i] & 0xf;
62
+ rgba[offset++] = palette[idx1 * 4];
63
+ rgba[offset++] = palette[idx1 * 4 + 1];
64
+ rgba[offset++] = palette[idx1 * 4 + 2];
65
+ rgba[offset++] = 0xff;
66
+ rgba[offset++] = palette[idx2 * 4];
67
+ rgba[offset++] = palette[idx2 * 4 + 1];
68
+ rgba[offset++] = palette[idx2 * 4 + 2];
69
+ rgba[offset++] = 0xff;
70
+ }
71
+ return rgba;
72
+ }
73
+
74
+ export function pal8(
75
+ data: Uint8Array,
76
+ palette: Uint8Array,
77
+ width: number,
78
+ height: number,
79
+ ): Uint8Array {
80
+ const rgba = new Uint8Array(4 * width * height);
81
+ for (let i = 0; i < data.length; i++) {
82
+ const idx = data[i];
83
+ rgba[i * 4] = palette[idx * 4];
84
+ rgba[i * 4 + 1] = palette[idx * 4 + 1];
85
+ rgba[i * 4 + 2] = palette[idx * 4 + 2];
86
+ rgba[i * 4 + 3] = palette[idx * 4 + 3];
87
+ }
88
+ return rgba;
89
+ }
90
+
91
+ export function pal8NoAlpha(
92
+ data: Uint8Array,
93
+ palette: Uint8Array,
94
+ width: number,
95
+ height: number,
96
+ ): Uint8Array {
97
+ const rgba = new Uint8Array(4 * width * height);
98
+ for (let i = 0; i < data.length; i++) {
99
+ const idx = data[i];
100
+ rgba[i * 4] = palette[idx * 4];
101
+ rgba[i * 4 + 1] = palette[idx * 4 + 1];
102
+ rgba[i * 4 + 2] = palette[idx * 4 + 2];
103
+ rgba[i * 4 + 3] = 0xff;
104
+ }
105
+ return rgba;
106
+ }
@@ -0,0 +1,59 @@
1
+ export interface RwVector2 {
2
+ x: number;
3
+ y: number;
4
+ }
5
+
6
+ export interface RwVector3 {
7
+ x: number;
8
+ y: number;
9
+ z: number;
10
+ }
11
+
12
+ export interface RwVector4 {
13
+ x: number;
14
+ y: number;
15
+ z: number;
16
+ t: number;
17
+ }
18
+
19
+ export interface RwQuaternion {
20
+ x: number;
21
+ y: number;
22
+ z: number;
23
+ w: number;
24
+ }
25
+
26
+ export interface RwMatrix3 {
27
+ right: RwVector3;
28
+ up: RwVector3;
29
+ at: RwVector3;
30
+ }
31
+
32
+ export interface RwMatrix4 {
33
+ right: RwVector4;
34
+ up: RwVector4;
35
+ at: RwVector4;
36
+ transform: RwVector4;
37
+ }
38
+
39
+ export interface RwColor {
40
+ r: number;
41
+ g: number;
42
+ b: number;
43
+ a: number;
44
+ }
45
+
46
+ export interface RwTextureCoordinate {
47
+ u: number;
48
+ v: number;
49
+ }
50
+
51
+ export interface RwTriangle {
52
+ vector: RwVector3;
53
+ materialId: number;
54
+ }
55
+
56
+ export interface RwSphere {
57
+ vector: RwVector3;
58
+ radius: number;
59
+ }
@@ -0,0 +1,87 @@
1
+ export class ByteStream {
2
+ private _cursor = 0;
3
+ private _stream: Buffer;
4
+
5
+ constructor(stream: Buffer) {
6
+ this._stream = stream;
7
+ }
8
+
9
+ public readUint8(): number {
10
+ return this._stream.readUInt8(this._cursor++);
11
+ }
12
+
13
+ public readUint16(): number {
14
+ const val = this._stream.readUInt16LE(this._cursor);
15
+ this._cursor += 2;
16
+ return val;
17
+ }
18
+
19
+ public readUint32(): number {
20
+ const val = this._stream.readUInt32LE(this._cursor);
21
+ this._cursor += 4;
22
+ return val;
23
+ }
24
+
25
+ public readInt16(): number {
26
+ const val = this._stream.readInt16LE(this._cursor);
27
+ this._cursor += 2;
28
+ return val;
29
+ }
30
+
31
+ public readInt8(): number {
32
+ return this._stream.readInt8(this._cursor++);
33
+ }
34
+
35
+ public readInt32(): number {
36
+ const val = this._stream.readInt32LE(this._cursor);
37
+ this._cursor += 4;
38
+ return val;
39
+ }
40
+
41
+ public readFloat(): number {
42
+ const val = this._stream.readFloatLE(this._cursor);
43
+ this._cursor += 4;
44
+ return val;
45
+ }
46
+
47
+ public readString(size: number): string {
48
+ const str = this._stream.toString(
49
+ "ascii",
50
+ this._cursor,
51
+ this._cursor + size,
52
+ );
53
+ this._cursor += size;
54
+ return str.split(/\0/g).shift() || "";
55
+ }
56
+
57
+ public readBytes(size: number): Uint8Array {
58
+ const data = this._stream.subarray(this._cursor, this._cursor + size);
59
+ this._cursor += size;
60
+ return new Uint8Array(data);
61
+ }
62
+
63
+ public read(size: number): Uint8Array {
64
+ return this.readBytes(size);
65
+ }
66
+
67
+ public getSize(): number {
68
+ return this._stream.byteLength;
69
+ }
70
+
71
+ public getPosition(): number {
72
+ return this._cursor;
73
+ }
74
+
75
+ public setPosition(position: number): void {
76
+ this._cursor = position;
77
+ }
78
+
79
+ public skip(size: number): void {
80
+ this._cursor += size;
81
+ }
82
+
83
+ public dispose(): void {
84
+ this._stream = Buffer.alloc(0);
85
+ this._cursor = 0;
86
+ }
87
+ }
@@ -0,0 +1,7 @@
1
+ export class RwParseError extends Error {}
2
+
3
+ export class RwParseStructureNotFoundError extends RwParseError {
4
+ constructor(structureName: string) {
5
+ super(`Structure ${structureName} not found.`);
6
+ }
7
+ }
@@ -0,0 +1,5 @@
1
+ export * from "./byte-stream";
2
+ export * from "./errors";
3
+ export * from "./rw-file";
4
+ export * from "./rw-sections";
5
+ export * from "./rw-version";
@@ -0,0 +1,23 @@
1
+ import { ByteStream } from "./byte-stream";
2
+
3
+ export interface RwSectionHeader {
4
+ sectionType: number;
5
+ sectionSize: number;
6
+ versionNumber: number;
7
+ }
8
+
9
+ export class RwFile extends ByteStream {
10
+ public readSectionHeader(): RwSectionHeader {
11
+ if (this.getPosition() + 12 > this.getSize()) {
12
+ throw new Error(
13
+ `Cannot read section header at offset ${this.getPosition().toString(16)}: need 12 bytes but only ${this.getSize() - this.getPosition()} bytes remaining`,
14
+ );
15
+ }
16
+
17
+ const sectionType = this.readUint32();
18
+ const sectionSize = this.readUint32();
19
+ const versionNumber = this.readUint32();
20
+
21
+ return { sectionType, sectionSize, versionNumber };
22
+ }
23
+ }
@@ -0,0 +1,24 @@
1
+ export enum RwSections {
2
+ RwStruct = 0x0001,
3
+ RwString = 0x0002,
4
+ RwExtension = 0x0003,
5
+ RwTexture = 0x0006,
6
+ RwMaterial = 0x0007,
7
+ RwMaterialList = 0x0008,
8
+ RwFrameList = 0x000e,
9
+ RwGeometry = 0x000f,
10
+ RwClump = 0x0010,
11
+ RwAtomic = 0x0014,
12
+ RwTextureNative = 0x0015,
13
+ RwTextureDictionary = 0x0016,
14
+ RwGeometryList = 0x001a,
15
+ RwBinMesh = 0x50e,
16
+ RwSkin = 0x116,
17
+ RwAnim = 0x11e,
18
+ RwMaterialEffectsPLG = 0x0120,
19
+ RwExtraVertColour = 0x0253f2f9,
20
+ RwBreakable = 0x0253f2fd,
21
+ RwReflectionMaterial = 0x0253f2fc,
22
+ Rw2dEffect = 0x0253f2f8,
23
+ RwNodeName = 0x0253f2fe,
24
+ }
@@ -0,0 +1,24 @@
1
+ const VERSIONS: Record<number, string> = {
2
+ 200704: "RenderWare 3.1.0.0 (III on PS2)",
3
+ 204800: "RenderWare 3.2.0.0 (III on PC)",
4
+ 208898: "RenderWare 3.3.0.2 (III on PC, VC on PS2)",
5
+ 212995: "RenderWare 3.4.0.3 (VC on PC)",
6
+ 212997: "RenderWare 3.4.0.5 (III on PS2, VC on Android/PC)",
7
+ 217088: "RenderWare 3.5.0.0 (III/VC on Xbox)",
8
+ 221187: "RenderWare 3.6.0.3 (SA)",
9
+ };
10
+
11
+ export function getVersionString(version: number): string {
12
+ return VERSIONS[version] ?? "";
13
+ }
14
+
15
+ export function unpackVersion(version: number): number {
16
+ if (version & 0xffff0000) {
17
+ return (((version >> 14) & 0x3ff00) + 0x30000) | ((version >> 16) & 0x3f);
18
+ }
19
+ return version;
20
+ }
21
+
22
+ export function unpackBuild(version: number): number {
23
+ return version & 0xffff0000 ? version & 0xffff : 0;
24
+ }
@@ -0,0 +1,5 @@
1
+ export enum DffModelType {
2
+ GENERIC,
3
+ SKIN,
4
+ VEHICLE,
5
+ }
@@ -0,0 +1,4 @@
1
+ export * from "./dff-model-type";
2
+ export * from "./parser";
3
+ export * from "./readers";
4
+ export * from "./types";
@@ -0,0 +1,142 @@
1
+ import { RwParseStructureNotFoundError } from "../core/errors";
2
+ import { RwFile } from "../core/rw-file";
3
+ import { RwSections } from "../core/rw-sections";
4
+ import { getVersionString, unpackVersion } from "../core/rw-version";
5
+ import { DffModelType } from "./dff-model-type";
6
+ import { readAnimNode, readAtomic, readFrameList, readGeometryList } from "./readers";
7
+ import type { RwAnimNode, RwDff, RwFrameList, RwGeometryList } from "./types";
8
+
9
+ export class DffParser extends RwFile {
10
+ parse(): RwDff {
11
+ let version: string | undefined;
12
+ let versionNumber: number | undefined;
13
+ const atomics: number[] = [];
14
+ const dummies: string[] = [];
15
+ const animNodes: RwAnimNode[] = [];
16
+ let geometryList: RwGeometryList | null = null;
17
+ let frameList: RwFrameList | null = null;
18
+
19
+ while (this.getPosition() < this.getSize()) {
20
+ let header;
21
+ try {
22
+ header = this.readSectionHeader();
23
+ } catch (error) {
24
+ console.warn(
25
+ `Failed to read section header at offset ${this.getPosition().toString(16)}: ${error instanceof Error ? error.message : error}. Truncating file.`,
26
+ );
27
+ break;
28
+ }
29
+
30
+ if (header.sectionType === 0) break;
31
+ if (header.sectionSize === 0) continue;
32
+
33
+ if (this.getPosition() + header.sectionSize > this.getSize()) {
34
+ console.warn(
35
+ `Section at offset ${this.getPosition().toString(16)} claims size ${header.sectionSize} but only ${this.getSize() - this.getPosition()} bytes remaining. Truncating file.`,
36
+ );
37
+ break;
38
+ }
39
+
40
+ switch (header.sectionType) {
41
+ case RwSections.RwClump:
42
+ versionNumber = unpackVersion(header.versionNumber);
43
+ version = getVersionString(versionNumber);
44
+ break;
45
+ case RwSections.RwFrameList:
46
+ frameList = readFrameList(this);
47
+ break;
48
+ case RwSections.RwExtension: {
49
+ const extHeader = this.readSectionHeader();
50
+ switch (extHeader.sectionType) {
51
+ case RwSections.RwNodeName:
52
+ dummies.push(this.readString(extHeader.sectionSize));
53
+ break;
54
+ case RwSections.RwAnim:
55
+ animNodes.push(readAnimNode(this));
56
+ break;
57
+ default:
58
+ this.skip(extHeader.sectionSize);
59
+ break;
60
+ }
61
+ break;
62
+ }
63
+ case RwSections.RwGeometryList:
64
+ geometryList = readGeometryList(this, header);
65
+ break;
66
+ case RwSections.RwAtomic: {
67
+ const atomic = readAtomic(this);
68
+ atomics[atomic.geometryIndex] = atomic.frameIndex;
69
+ break;
70
+ }
71
+ case RwSections.RwNodeName:
72
+ dummies.push(this.readString(header.sectionSize));
73
+ break;
74
+ case RwSections.RwAnim:
75
+ animNodes.push(readAnimNode(this));
76
+ break;
77
+ default:
78
+ this.skip(header.sectionSize);
79
+ break;
80
+ }
81
+ }
82
+
83
+ if (!version || !versionNumber) {
84
+ throw new RwParseStructureNotFoundError("version");
85
+ }
86
+
87
+ if (!geometryList?.geometries?.length) {
88
+ throw new RwParseStructureNotFoundError("geometry list");
89
+ }
90
+
91
+ // Heuristic model type detection (not authoritative - GTA uses IDE/DAT files)
92
+ // Based on gta-reversed: eCarNodes.h, AtomicModelInfo.cpp, VehicleModelInfo.cpp
93
+ const vehiclePrefixes = [
94
+ "chassis",
95
+ "wheel_rf",
96
+ "wheel_lf",
97
+ "wheel_rb",
98
+ "wheel_lb",
99
+ "wheel_rm",
100
+ "wheel_lm",
101
+ "door_lf",
102
+ "door_rf",
103
+ "door_lr",
104
+ "door_rr",
105
+ "bonnet",
106
+ "boot",
107
+ "bump_front",
108
+ "bump_rear",
109
+ "wing_lf",
110
+ "wing_rf",
111
+ "windscreen",
112
+ "exhaust",
113
+ "boat_hi",
114
+ "toprotor",
115
+ "rearrotor",
116
+ "moving_rotor",
117
+ ];
118
+
119
+ let modelType = DffModelType.GENERIC;
120
+ if (geometryList.geometries.some((g) => g.skin)) {
121
+ modelType = DffModelType.SKIN;
122
+ } else if (
123
+ dummies.some((d) => {
124
+ const lower = d.toLowerCase();
125
+ return vehiclePrefixes.some((p) => lower.startsWith(p) || lower.includes(`_${p}`));
126
+ })
127
+ ) {
128
+ modelType = DffModelType.VEHICLE;
129
+ }
130
+
131
+ return {
132
+ modelType,
133
+ version,
134
+ versionNumber,
135
+ geometryList,
136
+ frameList,
137
+ atomics,
138
+ dummies,
139
+ animNodes,
140
+ };
141
+ }
142
+ }