t2-demo-parser 1.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.
- package/LICENSE +21 -0
- package/README.md +823 -0
- package/dist/BitStream.d.ts +143 -0
- package/dist/BitStream.d.ts.map +1 -0
- package/dist/BitStream.js +369 -0
- package/dist/BitStream.js.map +1 -0
- package/dist/BitStream.test.d.ts +2 -0
- package/dist/BitStream.test.d.ts.map +1 -0
- package/dist/BitStream.test.js +419 -0
- package/dist/BitStream.test.js.map +1 -0
- package/dist/ClassRegistry.d.ts +100 -0
- package/dist/ClassRegistry.d.ts.map +1 -0
- package/dist/ClassRegistry.js +118 -0
- package/dist/ClassRegistry.js.map +1 -0
- package/dist/ClassRegistry.test.d.ts +2 -0
- package/dist/ClassRegistry.test.d.ts.map +1 -0
- package/dist/ClassRegistry.test.js +83 -0
- package/dist/ClassRegistry.test.js.map +1 -0
- package/dist/DataBlockParsers.d.ts +3 -0
- package/dist/DataBlockParsers.d.ts.map +1 -0
- package/dist/DataBlockParsers.js +2041 -0
- package/dist/DataBlockParsers.js.map +1 -0
- package/dist/DemoParser.d.ts +155 -0
- package/dist/DemoParser.d.ts.map +1 -0
- package/dist/DemoParser.js +943 -0
- package/dist/DemoParser.js.map +1 -0
- package/dist/DemoParser.test.d.ts +2 -0
- package/dist/DemoParser.test.d.ts.map +1 -0
- package/dist/DemoParser.test.js +472 -0
- package/dist/DemoParser.test.js.map +1 -0
- package/dist/EventParsers.d.ts +3 -0
- package/dist/EventParsers.d.ts.map +1 -0
- package/dist/EventParsers.js +603 -0
- package/dist/EventParsers.js.map +1 -0
- package/dist/GhostManager.d.ts +13 -0
- package/dist/GhostManager.d.ts.map +1 -0
- package/dist/GhostManager.js +2002 -0
- package/dist/GhostManager.js.map +1 -0
- package/dist/GhostTracker.test.d.ts +2 -0
- package/dist/GhostTracker.test.d.ts.map +1 -0
- package/dist/GhostTracker.test.js +64 -0
- package/dist/GhostTracker.test.js.map +1 -0
- package/dist/HuffmanProcessor.d.ts +12 -0
- package/dist/HuffmanProcessor.d.ts.map +1 -0
- package/dist/HuffmanProcessor.js +163 -0
- package/dist/HuffmanProcessor.js.map +1 -0
- package/dist/LiveParser.d.ts +16 -0
- package/dist/LiveParser.d.ts.map +1 -0
- package/dist/LiveParser.js +29 -0
- package/dist/LiveParser.js.map +1 -0
- package/dist/PacketParser.d.ts +104 -0
- package/dist/PacketParser.d.ts.map +1 -0
- package/dist/PacketParser.js +713 -0
- package/dist/PacketParser.js.map +1 -0
- package/dist/Timeline.d.ts +101 -0
- package/dist/Timeline.d.ts.map +1 -0
- package/dist/Timeline.js +251 -0
- package/dist/Timeline.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +372 -0
- package/dist/cli.js.map +1 -0
- package/dist/dataBlockDataTypes.d.ts +877 -0
- package/dist/dataBlockDataTypes.d.ts.map +1 -0
- package/dist/dataBlockDataTypes.js +2 -0
- package/dist/dataBlockDataTypes.js.map +1 -0
- package/dist/dataTypes.d.ts +31 -0
- package/dist/dataTypes.d.ts.map +1 -0
- package/dist/dataTypes.js +5 -0
- package/dist/dataTypes.js.map +1 -0
- package/dist/dump.d.ts +3 -0
- package/dist/dump.d.ts.map +1 -0
- package/dist/dump.js +36 -0
- package/dist/dump.js.map +1 -0
- package/dist/eventDataTypes.d.ts +199 -0
- package/dist/eventDataTypes.d.ts.map +1 -0
- package/dist/eventDataTypes.js +2 -0
- package/dist/eventDataTypes.js.map +1 -0
- package/dist/ghostDataTypes.d.ts +573 -0
- package/dist/ghostDataTypes.d.ts.map +1 -0
- package/dist/ghostDataTypes.js +2 -0
- package/dist/ghostDataTypes.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +261 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +204 -0
- package/dist/types.js.map +1 -0
- package/package.json +46 -0
|
@@ -0,0 +1,2041 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// Helper functions
|
|
3
|
+
// ============================================================
|
|
4
|
+
/**
|
|
5
|
+
* Read a DataBlock reference: flag(1b) + readClassId (11 bits).
|
|
6
|
+
* From decompiled binary: uses FUN_00436d10 (readClassId) which reads
|
|
7
|
+
* getBitCount(getNextPow2(0x800)) = 11 bits.
|
|
8
|
+
* If flag is false, sets objectId to -1 (0xFFFFFFFF).
|
|
9
|
+
*/
|
|
10
|
+
function readDataBlockRef(bs) {
|
|
11
|
+
return bs.readFlag() ? bs.readInt(11) : null;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Read a ranged signed 32-bit integer.
|
|
15
|
+
* Encodes value as unsigned offset from min.
|
|
16
|
+
*/
|
|
17
|
+
function readRangedS32(bs, min, max) {
|
|
18
|
+
return bs.readRangedU32(0, max - min) + min;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Read a packed ColorF: 4 bytes (32 bits total), each converted to [0..1] float.
|
|
22
|
+
* From decompiled binary: FUN_0043f040 calls FUN_0043efe0 which reads 4 × stream->read(1, &byte).
|
|
23
|
+
* Each byte is multiplied by 1/255.0 to produce the float value.
|
|
24
|
+
* NOTE: This is NOT 4×F32 (128 bits) — it's 4×U8 (32 bits).
|
|
25
|
+
*/
|
|
26
|
+
function readColorF(bs) {
|
|
27
|
+
return {
|
|
28
|
+
r: bs.readInt(8) / 255,
|
|
29
|
+
g: bs.readInt(8) / 255,
|
|
30
|
+
b: bs.readInt(8) / 255,
|
|
31
|
+
a: bs.readInt(8) / 255,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Read a DataBlock boolean field: 8 bits (1 byte), as written by stream->write(sizeof(bool), &val).
|
|
36
|
+
* NOTE: This is different from readFlag() which reads 1 bit via BitStream::readFlag().
|
|
37
|
+
* Many DataBlock fields use write(sizeof(bool)) instead of writeFlag().
|
|
38
|
+
*/
|
|
39
|
+
function readBool(bs) {
|
|
40
|
+
return bs.readInt(8) !== 0;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Read a ranged float: min + readInt(bits) / ((1 << bits) - 1) * (max - min).
|
|
44
|
+
*/
|
|
45
|
+
function readRangedF32(bs, min, max, bits) {
|
|
46
|
+
return min + (bs.readInt(bits) / ((1 << bits) - 1)) * (max - min);
|
|
47
|
+
}
|
|
48
|
+
// ============================================================
|
|
49
|
+
// Base class parsers (inheritance chain)
|
|
50
|
+
// ============================================================
|
|
51
|
+
// SimDataBlock → GameBaseData: 0 bits on wire (no-op)
|
|
52
|
+
/**
|
|
53
|
+
* ShapeBaseData::unpackData — critical base class for most game objects.
|
|
54
|
+
*
|
|
55
|
+
* Verified against decompiled binary FUN_005e63e0 (Tribes2.exe).
|
|
56
|
+
* NOTE: The field order differs from V12 engine reference source.
|
|
57
|
+
* The Tribes 2 binary groups booleans and DataBlock refs differently,
|
|
58
|
+
* and the sensor color uses 4×U8 (not 4×F32), among other differences.
|
|
59
|
+
*/
|
|
60
|
+
function shapeBaseDataUnpack(bs) {
|
|
61
|
+
const result = {};
|
|
62
|
+
// computeCRC — flag; if true: U32 mCRC
|
|
63
|
+
// Binary: readFlag → this+0xc0; if true: read(4) → this+0xbc
|
|
64
|
+
if (bs.readFlag()) {
|
|
65
|
+
result.crc = bs.readU32();
|
|
66
|
+
}
|
|
67
|
+
// shapeName — readString → this+0x44
|
|
68
|
+
result.shapeName = bs.readString();
|
|
69
|
+
// 9 conditional F32 fields (flag + F32 if non-default)
|
|
70
|
+
// Binary: each is readFlag, if true: read(4) → field offset
|
|
71
|
+
if (bs.readFlag())
|
|
72
|
+
result.mass = bs.readF32(); // → 0x74
|
|
73
|
+
if (bs.readFlag())
|
|
74
|
+
result.drag = bs.readF32(); // → 0x78
|
|
75
|
+
if (bs.readFlag())
|
|
76
|
+
result.density = bs.readF32(); // → 0x7c
|
|
77
|
+
if (bs.readFlag())
|
|
78
|
+
result.maxEnergy = bs.readF32(); // → 0x80
|
|
79
|
+
if (bs.readFlag())
|
|
80
|
+
result.cameraMaxDist = bs.readF32(); // → 0xa4
|
|
81
|
+
if (bs.readFlag())
|
|
82
|
+
result.cameraMinDist = bs.readF32(); // → 0xa8
|
|
83
|
+
if (bs.readFlag())
|
|
84
|
+
result.cameraDefaultFov = bs.readF32(); // → 0xac
|
|
85
|
+
if (bs.readFlag())
|
|
86
|
+
result.cameraMinFov = bs.readF32(); // → 0xb0
|
|
87
|
+
if (bs.readFlag())
|
|
88
|
+
result.cameraMaxFov = bs.readF32(); // → 0xb4
|
|
89
|
+
// debrisShapeName — readString → this+0x48
|
|
90
|
+
result.debrisShapeName = bs.readString();
|
|
91
|
+
// sensorRadius — flag; if true: readInt(10) + 4×U8 (RGBA bytes)
|
|
92
|
+
// Binary: readInt(10) → this+0x15c (cast to float), then 4 × read(1) → 0x160-0x163
|
|
93
|
+
if (bs.readFlag()) {
|
|
94
|
+
result.sensorRadius = bs.readInt(10);
|
|
95
|
+
result.sensorColor = {
|
|
96
|
+
r: bs.readInt(8),
|
|
97
|
+
g: bs.readInt(8),
|
|
98
|
+
b: bs.readInt(8),
|
|
99
|
+
a: bs.readInt(8),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
// heat — flag; if true: F32 → this+0x50
|
|
103
|
+
if (bs.readFlag())
|
|
104
|
+
result.heat = bs.readF32();
|
|
105
|
+
// cmdCategory — readString → this+0x164
|
|
106
|
+
result.cmdCategory = bs.readString();
|
|
107
|
+
// cmdMiniIconName — readString → this+0x168
|
|
108
|
+
// NOTE: V12 has cmdIcon (DataBlockRef) here, but binary has readString.
|
|
109
|
+
result.cmdMiniIconName = bs.readString();
|
|
110
|
+
// 3 boolean flags: canControl, canObserve, observeThroughObject
|
|
111
|
+
// Binary: 3 readFlags → this+0x16c, 0x16d, 0x16e
|
|
112
|
+
result.canControl = bs.readFlag();
|
|
113
|
+
result.canObserve = bs.readFlag();
|
|
114
|
+
result.observeThroughObject = bs.readFlag();
|
|
115
|
+
// 3 more boolean flags (grouped together in binary, different from V12)
|
|
116
|
+
// Binary: this+0x325, 0x329, 0x32a
|
|
117
|
+
result.emap = bs.readFlag(); // 0x325
|
|
118
|
+
result.isInvincible = bs.readFlag(); // 0x329
|
|
119
|
+
result.renderWhenDestroyed = bs.readFlag(); // 0x32a
|
|
120
|
+
// 4 DataBlock refs using readClassId (FUN_00436d10 = readInt(11))
|
|
121
|
+
// Binary: each is readFlag + readClassId → this+0x70, 0x60, 0x68, 0x58
|
|
122
|
+
result.cmdIcon = readDataBlockRef(bs); // 0x6c/0x70
|
|
123
|
+
result.explosion = readDataBlockRef(bs); // 0x5c/0x60
|
|
124
|
+
result.underwaterExplosion = readDataBlockRef(bs); // 0x64/0x68
|
|
125
|
+
result.debris = readDataBlockRef(bs); // 0x54/0x58
|
|
126
|
+
// 3 more boolean flags
|
|
127
|
+
// Binary: this+0x32b, 0x326, 0x327
|
|
128
|
+
result.inheritEnergyFromMount = bs.readFlag(); // 0x32b
|
|
129
|
+
result.firstPersonOnly = bs.readFlag(); // 0x326
|
|
130
|
+
result.useEyePoint = bs.readFlag(); // 0x327
|
|
131
|
+
// shieldEffectLifetimeMS — U32 → this+0x94
|
|
132
|
+
result.shieldEffectLifetimeMS = bs.readU32();
|
|
133
|
+
// shieldEffectScale — flag; if true: 3×F32
|
|
134
|
+
// Binary: readFlag, if true: read(4) × 3 → this+0x98, 0x9c, 0xa0
|
|
135
|
+
// NOTE: This field is NOT in the V12 reference source — Tribes 2 addition.
|
|
136
|
+
if (bs.readFlag()) {
|
|
137
|
+
result.shieldEffectScale = {
|
|
138
|
+
x: bs.readF32(),
|
|
139
|
+
y: bs.readF32(),
|
|
140
|
+
z: bs.readF32(),
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
// HUD images loop (8 iterations, NumHudRenderImages=8)
|
|
144
|
+
// Binary: do-while uVar4 < 8 at offset this + uVar4*4 + 0x174
|
|
145
|
+
const hudImages = [];
|
|
146
|
+
for (let i = 0; i < 8; i++) {
|
|
147
|
+
const hasFriendly = bs.readFlag();
|
|
148
|
+
if (!hasFriendly)
|
|
149
|
+
continue;
|
|
150
|
+
const friendlyName = bs.readString(); // → this + i*4 + 0x174
|
|
151
|
+
const hasEnemy = bs.readFlag();
|
|
152
|
+
const enemyName = hasEnemy ? bs.readString() : undefined; // → this + i*4 + 0x194
|
|
153
|
+
const img = {
|
|
154
|
+
friendlyName,
|
|
155
|
+
enemyName,
|
|
156
|
+
renderCenter: bs.readFlag(), // → this + i + 0x1f4
|
|
157
|
+
renderModulated: bs.readFlag(), // → this + i + 0x1fc
|
|
158
|
+
renderAlways: bs.readFlag(), // → this + i + 0x204
|
|
159
|
+
renderDistance: bs.readFlag(), // → this + i + 0x20c
|
|
160
|
+
renderName: bs.readFlag(), // → this + i + 0x214
|
|
161
|
+
};
|
|
162
|
+
hudImages.push(img);
|
|
163
|
+
}
|
|
164
|
+
if (hudImages.length > 0)
|
|
165
|
+
result.hudImages = hudImages;
|
|
166
|
+
return result;
|
|
167
|
+
}
|
|
168
|
+
// ============================================================
|
|
169
|
+
// ShapeBaseImageData (weapon mount data — 31 states)
|
|
170
|
+
// ============================================================
|
|
171
|
+
function shapeBaseImageDataUnpack(bs) {
|
|
172
|
+
// Extends GameBaseData directly (0 parent bits)
|
|
173
|
+
const result = {};
|
|
174
|
+
// computeCRC — flag; if true: U32
|
|
175
|
+
if (bs.readFlag()) {
|
|
176
|
+
result.crc = bs.readU32();
|
|
177
|
+
}
|
|
178
|
+
// shapeName
|
|
179
|
+
result.shapeName = bs.readString();
|
|
180
|
+
// mountPoint — U32
|
|
181
|
+
result.mountPoint = bs.readU32();
|
|
182
|
+
// offsetTransform — flag(isIdentity); if NOT identity: readAffineTransform
|
|
183
|
+
if (!bs.readFlag()) {
|
|
184
|
+
result.offset = bs.readAffineTransform();
|
|
185
|
+
}
|
|
186
|
+
// firstPerson — flag
|
|
187
|
+
result.firstPerson = bs.readFlag();
|
|
188
|
+
// mass — F32
|
|
189
|
+
result.mass = bs.readF32();
|
|
190
|
+
// usesEnergy — flag
|
|
191
|
+
result.usesEnergy = bs.readFlag();
|
|
192
|
+
// minEnergy — F32
|
|
193
|
+
result.minEnergy = bs.readF32();
|
|
194
|
+
// hasFlash — flag
|
|
195
|
+
result.hasFlash = bs.readFlag();
|
|
196
|
+
// projectile — readDataBlockRef
|
|
197
|
+
result.projectile = readDataBlockRef(bs);
|
|
198
|
+
// muzzleFlash — readDataBlockRef (confirmed at offset 0xce4 in binary)
|
|
199
|
+
result.muzzleFlash = readDataBlockRef(bs);
|
|
200
|
+
// isSeeker — flag; if true: 4×F32 + flag + F32
|
|
201
|
+
result.isSeeker = bs.readFlag();
|
|
202
|
+
if (result.isSeeker) {
|
|
203
|
+
result.seekerRadius = bs.readF32();
|
|
204
|
+
result.maxSeekAngle = bs.readF32();
|
|
205
|
+
result.seekerLockTime = bs.readF32();
|
|
206
|
+
result.seekerFreeTime = bs.readF32();
|
|
207
|
+
result.isTargetLockRequired = bs.readFlag();
|
|
208
|
+
result.maxLockRange = bs.readF32();
|
|
209
|
+
}
|
|
210
|
+
// cloakable — flag
|
|
211
|
+
result.cloakable = bs.readFlag();
|
|
212
|
+
// lightType — readRangedU32(0, 3); if != 0: F32 + S32 + 4×writeFloat(7)
|
|
213
|
+
result.lightType = bs.readRangedU32(0, 3);
|
|
214
|
+
if (result.lightType !== 0) {
|
|
215
|
+
result.lightRadius = bs.readF32();
|
|
216
|
+
result.lightTime = bs.readS32();
|
|
217
|
+
result.lightColor = {
|
|
218
|
+
r: bs.readFloat(7),
|
|
219
|
+
g: bs.readFloat(7),
|
|
220
|
+
b: bs.readFloat(7),
|
|
221
|
+
a: bs.readFloat(7),
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
// shellExitDir — 3×F32 (mathWrite Point3F)
|
|
225
|
+
result.shellExitDir = {
|
|
226
|
+
x: bs.readF32(),
|
|
227
|
+
y: bs.readF32(),
|
|
228
|
+
z: bs.readF32(),
|
|
229
|
+
};
|
|
230
|
+
// shellExitVariance — F32
|
|
231
|
+
result.shellExitVariance = bs.readF32();
|
|
232
|
+
// shellVelocity — F32
|
|
233
|
+
result.shellVelocity = bs.readF32();
|
|
234
|
+
// casing — readDataBlockRef
|
|
235
|
+
result.casing = readDataBlockRef(bs);
|
|
236
|
+
// accuFire flag (confirmed at offset 0xc88 in binary, before state loop)
|
|
237
|
+
result.accuFire = bs.readFlag();
|
|
238
|
+
// State loop (31 iterations, MaxStates=31)
|
|
239
|
+
const states = [];
|
|
240
|
+
for (let i = 0; i < 31; i++) {
|
|
241
|
+
const hasName = bs.readFlag();
|
|
242
|
+
if (!hasName)
|
|
243
|
+
continue;
|
|
244
|
+
const name = bs.readString();
|
|
245
|
+
// 11 transition values: each readInt(5)
|
|
246
|
+
const transitionOnAmmo = bs.readInt(5);
|
|
247
|
+
const transitionOnNoAmmo = bs.readInt(5);
|
|
248
|
+
const transitionOnTarget = bs.readInt(5);
|
|
249
|
+
const transitionOnNoTarget = bs.readInt(5);
|
|
250
|
+
const transitionOnWet = bs.readInt(5);
|
|
251
|
+
const transitionOnNotWet = bs.readInt(5);
|
|
252
|
+
const transitionOnTriggerUp = bs.readInt(5);
|
|
253
|
+
const transitionOnTriggerDown = bs.readInt(5);
|
|
254
|
+
const transitionOnTimeout = bs.readInt(5);
|
|
255
|
+
const transitionGeneric0In = bs.readInt(5);
|
|
256
|
+
const transitionGeneric0Out = bs.readInt(5);
|
|
257
|
+
// timeoutValue — flag(!=default) then F32
|
|
258
|
+
const timeoutValue = bs.readFlag() ? bs.readF32() : undefined;
|
|
259
|
+
// waitForTimeout — flag
|
|
260
|
+
const waitForTimeout = bs.readFlag();
|
|
261
|
+
// fire — flag
|
|
262
|
+
const fire = bs.readFlag();
|
|
263
|
+
// ejectShell — flag
|
|
264
|
+
const ejectShell = bs.readFlag();
|
|
265
|
+
// scaleAnimation — flag
|
|
266
|
+
const scaleAnimation = bs.readFlag();
|
|
267
|
+
// direction — flag
|
|
268
|
+
const direction = bs.readFlag();
|
|
269
|
+
// reload — flag (confirmed at offset 0xe2a in binary, between direction and energyDrain)
|
|
270
|
+
const reload = bs.readFlag();
|
|
271
|
+
// energyDrain — flag(!=default) then F32
|
|
272
|
+
const energyDrain = bs.readFlag() ? bs.readF32() : undefined;
|
|
273
|
+
// loaded — readInt(3)
|
|
274
|
+
const loaded = bs.readInt(3);
|
|
275
|
+
// spin — readInt(3)
|
|
276
|
+
const spin = bs.readInt(3);
|
|
277
|
+
// recoil — readInt(3)
|
|
278
|
+
const recoil = bs.readInt(3);
|
|
279
|
+
// sequence — flag(!=default) then readSignedInt(16)
|
|
280
|
+
const sequence = bs.readFlag() ? bs.readSignedInt(16) : undefined;
|
|
281
|
+
// sequenceVis — flag(!=default) then readSignedInt(16)
|
|
282
|
+
const sequenceVis = bs.readFlag() ? bs.readSignedInt(16) : undefined;
|
|
283
|
+
// flashSequence — flag
|
|
284
|
+
const flashSequence = bs.readFlag();
|
|
285
|
+
// ignoreLoadedForReady — flag
|
|
286
|
+
const ignoreLoadedForReady = bs.readFlag();
|
|
287
|
+
// emitter — readDataBlockRef; if present: F32 + S32
|
|
288
|
+
const emitter = readDataBlockRef(bs);
|
|
289
|
+
const emitterTime = emitter !== null ? bs.readF32() : undefined;
|
|
290
|
+
const emitterNode = emitter !== null ? bs.readS32() : undefined;
|
|
291
|
+
// sound — readDataBlockRef
|
|
292
|
+
const sound = readDataBlockRef(bs);
|
|
293
|
+
const state = {
|
|
294
|
+
name,
|
|
295
|
+
transitionOnAmmo,
|
|
296
|
+
transitionOnNoAmmo,
|
|
297
|
+
transitionOnTarget,
|
|
298
|
+
transitionOnNoTarget,
|
|
299
|
+
transitionOnWet,
|
|
300
|
+
transitionOnNotWet,
|
|
301
|
+
transitionOnTriggerUp,
|
|
302
|
+
transitionOnTriggerDown,
|
|
303
|
+
transitionOnTimeout,
|
|
304
|
+
transitionGeneric0In,
|
|
305
|
+
transitionGeneric0Out,
|
|
306
|
+
timeoutValue,
|
|
307
|
+
waitForTimeout,
|
|
308
|
+
fire,
|
|
309
|
+
ejectShell,
|
|
310
|
+
scaleAnimation,
|
|
311
|
+
direction,
|
|
312
|
+
reload,
|
|
313
|
+
energyDrain,
|
|
314
|
+
loaded,
|
|
315
|
+
spin,
|
|
316
|
+
recoil,
|
|
317
|
+
sequence,
|
|
318
|
+
sequenceVis,
|
|
319
|
+
flashSequence,
|
|
320
|
+
ignoreLoadedForReady,
|
|
321
|
+
emitter,
|
|
322
|
+
emitterTime,
|
|
323
|
+
emitterNode,
|
|
324
|
+
sound,
|
|
325
|
+
};
|
|
326
|
+
states.push(state);
|
|
327
|
+
}
|
|
328
|
+
result.states = states;
|
|
329
|
+
return result;
|
|
330
|
+
}
|
|
331
|
+
// ============================================================
|
|
332
|
+
// PlayerData (extends ShapeBaseData)
|
|
333
|
+
// ============================================================
|
|
334
|
+
function playerDataUnpack(bs) {
|
|
335
|
+
const result = shapeBaseDataUnpack(bs);
|
|
336
|
+
// Verified against decompiled binary FUN_005cfa40 (Tribes2.exe).
|
|
337
|
+
// Binary layout: flag + 13 F32s + 2 optional DB refs + 9 F32s + 1 F32 +
|
|
338
|
+
// 8 F32s + readInt(7) + 6 F32s + 9 F32s + 1 F32 + 32 sounds +
|
|
339
|
+
// boxSize(3) + DB refs + F32s + 11 ground impact F32s
|
|
340
|
+
// 1. readFlag → 0x340 (renderFirstPerson — NOT F32!)
|
|
341
|
+
result.renderFirstPerson = bs.readFlag();
|
|
342
|
+
// 2. 13× F32 (offsets 0x334-0x360)
|
|
343
|
+
result.minLookAngle = bs.readF32(); // 0x334
|
|
344
|
+
result.maxLookAngle = bs.readF32(); // 0x338
|
|
345
|
+
result.maxFreelookAngle = bs.readF32(); // 0x33c
|
|
346
|
+
result.maxTimeScale = bs.readF32(); // 0x330
|
|
347
|
+
result.maxStepHeight = bs.readF32(); // 0x398
|
|
348
|
+
result.runForce = bs.readF32(); // 0x344
|
|
349
|
+
result.runEnergyDrain = bs.readF32(); // 0x348
|
|
350
|
+
result.minRunEnergy = bs.readF32(); // 0x34c
|
|
351
|
+
result.maxForwardSpeed = bs.readF32(); // 0x350
|
|
352
|
+
result.maxBackwardSpeed = bs.readF32(); // 0x354
|
|
353
|
+
result.maxSideSpeed = bs.readF32(); // 0x358
|
|
354
|
+
result.maxUnderwaterForwardSpeed = bs.readF32(); // 0x35c
|
|
355
|
+
result.maxUnderwaterBackwardSpeed = bs.readF32(); // 0x360
|
|
356
|
+
// [0x364 hardcoded to 0, not from stream]
|
|
357
|
+
// 3. 2 optional DataBlock refs (readClassId pattern)
|
|
358
|
+
result.maxUnderwaterSideSpeedRef = readDataBlockRef(bs); // 0x368
|
|
359
|
+
if (bs.readFlag()) {
|
|
360
|
+
result.runSurfaceAngleRef = bs.readInt(11); // 0x370 (no else/-1)
|
|
361
|
+
}
|
|
362
|
+
// 4. 9× F32 (offsets 0x374-0x394)
|
|
363
|
+
result.runSurfaceAngle = bs.readF32(); // 0x374
|
|
364
|
+
result.recoverDelay = bs.readF32(); // 0x378 (S32 on wire = 4 bytes)
|
|
365
|
+
result.recoverRunForceScale = bs.readF32(); // 0x37c
|
|
366
|
+
result.jumpForce = bs.readF32(); // 0x380
|
|
367
|
+
result.jumpEnergyDrain = bs.readF32(); // 0x384
|
|
368
|
+
result.minJumpEnergy = bs.readF32(); // 0x388
|
|
369
|
+
result.minJumpSpeed = bs.readF32(); // 0x38c
|
|
370
|
+
result.maxJumpSpeed = bs.readF32(); // 0x390
|
|
371
|
+
result.jumpSurfaceAngle = bs.readF32(); // 0x394
|
|
372
|
+
// 5. 1× F32 (offset 0x39c)
|
|
373
|
+
result.minJetEnergy = bs.readF32(); // 0x39c
|
|
374
|
+
// 6. 8× F32 (offsets 0x3b8-0x3d4)
|
|
375
|
+
result.splashVelocity = bs.readF32(); // 0x3b8
|
|
376
|
+
result.splashAngle = bs.readF32(); // 0x3bc
|
|
377
|
+
result.splashFreqMod = bs.readF32(); // 0x3c0
|
|
378
|
+
result.splashVelEpsilon = bs.readF32(); // 0x3c4
|
|
379
|
+
result.bubbleEmitTime = bs.readF32(); // 0x3c8
|
|
380
|
+
result.medSplashSoundVel = bs.readF32(); // 0x3cc
|
|
381
|
+
result.hardSplashSoundVel = bs.readF32(); // 0x3d0
|
|
382
|
+
result.exitSplashSoundVel = bs.readF32(); // 0x3d4
|
|
383
|
+
// 7. readInt(7) → jumpDelay (offset 0x3d8)
|
|
384
|
+
result.jumpDelay = bs.readInt(7);
|
|
385
|
+
// 8. 6× F32 (offsets 0x3a0-0x3b4)
|
|
386
|
+
result.horizMaxSpeed = bs.readF32(); // 0x3a0
|
|
387
|
+
result.horizResistSpeed = bs.readF32(); // 0x3a4
|
|
388
|
+
result.horizResistFactor = bs.readF32(); // 0x3a8
|
|
389
|
+
result.upMaxSpeed = bs.readF32(); // 0x3ac
|
|
390
|
+
result.upResistSpeed = bs.readF32(); // 0x3b0
|
|
391
|
+
result.upResistFactor = bs.readF32(); // 0x3b4
|
|
392
|
+
// 9. 9× F32 (offsets 0xd14-0xd34, far offsets = Tribes 2-specific fields)
|
|
393
|
+
result.jetEnergyDrain = bs.readF32(); // 0xd14
|
|
394
|
+
result.canJet = bs.readF32(); // 0xd18
|
|
395
|
+
result.maxJetHorizontalPercentage = bs.readF32(); // 0xd1c
|
|
396
|
+
result.maxJetForwardSpeed = bs.readF32(); // 0xd20
|
|
397
|
+
result.jetForce = bs.readF32(); // 0xd24
|
|
398
|
+
result.minJetSpeed = bs.readF32(); // 0xd28
|
|
399
|
+
result.maxDamage = bs.readF32(); // 0xd2c
|
|
400
|
+
result.minImpactDamageSpeed = bs.readF32(); // 0xd30
|
|
401
|
+
result.impactDamageScale = bs.readF32(); // 0xd34
|
|
402
|
+
// 10. 1× F32 (offset 0x3f4)
|
|
403
|
+
result.footSplashHeight = bs.readF32(); // 0x3f4
|
|
404
|
+
// 11. Sound loop: 32 iterations (MaxSounds=0x20 in binary)
|
|
405
|
+
// Binary: zeros 0x428+i*4, then flag + readClassId → 0x4a8+i*4
|
|
406
|
+
const sounds = [];
|
|
407
|
+
for (let i = 0; i < 32; i++) {
|
|
408
|
+
if (bs.readFlag()) {
|
|
409
|
+
sounds.push(bs.readInt(11));
|
|
410
|
+
}
|
|
411
|
+
else {
|
|
412
|
+
sounds.push(null);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
result.sounds = sounds;
|
|
416
|
+
// 12. boxSize 3×F32 (offsets 0x528, 0x52c, 0x530)
|
|
417
|
+
result.boxSize = {
|
|
418
|
+
x: bs.readF32(),
|
|
419
|
+
y: bs.readF32(),
|
|
420
|
+
z: bs.readF32(),
|
|
421
|
+
};
|
|
422
|
+
// 13. footPuffEmitter + 2 F32s (offset 0xcf0, 0xcf4, 0xcf8)
|
|
423
|
+
result.footPuffEmitter = readDataBlockRef(bs);
|
|
424
|
+
result.footPuffNumParts = bs.readF32();
|
|
425
|
+
result.footPuffRadius = bs.readF32();
|
|
426
|
+
// 14. decalData + 1 F32 (offset 0xd00, 0x3f8)
|
|
427
|
+
result.decalData = readDataBlockRef(bs);
|
|
428
|
+
result.decalOffset = bs.readF32();
|
|
429
|
+
// 15. dustEmitter + splash (offsets 0xd08, 0xd10)
|
|
430
|
+
result.dustEmitter = readDataBlockRef(bs);
|
|
431
|
+
result.splash = readDataBlockRef(bs);
|
|
432
|
+
// 16. 3 splashEmitters (offsets 0xd44 + i*4)
|
|
433
|
+
const splashEmitters = [];
|
|
434
|
+
for (let i = 0; i < 3; i++) {
|
|
435
|
+
splashEmitters.push(readDataBlockRef(bs));
|
|
436
|
+
}
|
|
437
|
+
result.splashEmitters = splashEmitters;
|
|
438
|
+
// 17. 11× F32 for ground impact shake (offsets 0x3fc-0x424)
|
|
439
|
+
// V12 has 9 fields; binary has 11 (2 extra Tribes 2-specific)
|
|
440
|
+
result.groundImpactMinSpeed = bs.readF32(); // 0x3fc
|
|
441
|
+
result.groundImpactShakeFreq = {
|
|
442
|
+
x: bs.readF32(), // 0x400
|
|
443
|
+
y: bs.readF32(), // 0x404
|
|
444
|
+
z: bs.readF32(), // 0x408
|
|
445
|
+
};
|
|
446
|
+
result.groundImpactShakeAmp = {
|
|
447
|
+
x: bs.readF32(), // 0x40c
|
|
448
|
+
y: bs.readF32(), // 0x410
|
|
449
|
+
z: bs.readF32(), // 0x414
|
|
450
|
+
};
|
|
451
|
+
result.groundImpactShakeDuration = bs.readF32(); // 0x418
|
|
452
|
+
result.groundImpactShakeFalloff = bs.readF32(); // 0x41c
|
|
453
|
+
result.boundingRadius = bs.readF32(); // 0x420 (Tribes 2 extra)
|
|
454
|
+
result.moveBubbleSize = bs.readF32(); // 0x424 (Tribes 2 extra)
|
|
455
|
+
return result;
|
|
456
|
+
}
|
|
457
|
+
// ============================================================
|
|
458
|
+
// VehicleData (extends ShapeBaseData)
|
|
459
|
+
// ============================================================
|
|
460
|
+
function vehicleDataUnpack(bs) {
|
|
461
|
+
const result = shapeBaseDataUnpack(bs);
|
|
462
|
+
// Verified against decompiled binary FUN_00609450 (Tribes2.exe).
|
|
463
|
+
// 1. 2×F32 (body restitution/friction) → offsets 0x33c, 0x340
|
|
464
|
+
result.bodyRestitution = bs.readF32();
|
|
465
|
+
result.bodyFriction = bs.readF32();
|
|
466
|
+
// 2. Loop of 2: impact sound DataBlock refs → offsets 0x334+i*4
|
|
467
|
+
const impactSounds = [];
|
|
468
|
+
for (let i = 0; i < 2; i++) {
|
|
469
|
+
impactSounds.push(readDataBlockRef(bs));
|
|
470
|
+
}
|
|
471
|
+
result.impactSounds = impactSounds;
|
|
472
|
+
// 3. ~20 F32s for vehicle physics
|
|
473
|
+
result.minImpactSpeed = bs.readF32(); // 0x37c
|
|
474
|
+
result.softImpactSpeed = bs.readF32(); // 0x380
|
|
475
|
+
result.hardImpactSpeed = bs.readF32(); // 0x384 (900 dec)
|
|
476
|
+
result.minRollSpeed = bs.readF32(); // 0x388
|
|
477
|
+
result.maxSteeringAngle = bs.readF32(); // 0x38c
|
|
478
|
+
result.maxDrag = bs.readF32(); // 0x3a4
|
|
479
|
+
result.minDrag = bs.readF32(); // 0x3a0
|
|
480
|
+
result.jetForce = bs.readF32(); // 0x3a8
|
|
481
|
+
result.jetEnergyDrain = bs.readF32(); // 0x3ac
|
|
482
|
+
result.minJetEnergy = bs.readF32(); // 0x3b0
|
|
483
|
+
result.cameraOffset = bs.readF32(); // 0x39c
|
|
484
|
+
result.cameraLag = bs.readF32(); // 0x398
|
|
485
|
+
result.triggerDustHeight = bs.readF32(); // 0x3c8
|
|
486
|
+
result.dustHeight = bs.readF32(); // 0x3cc
|
|
487
|
+
result.numDmgEmitterAreas = bs.readF32(); // 0x408
|
|
488
|
+
result.exitSplashSoundVelocity = bs.readF32(); // 0x36c
|
|
489
|
+
result.softSplashSoundVelocity = bs.readF32(); // 0x370
|
|
490
|
+
result.mediumSplashSoundVelocity = bs.readF32(); // 0x374
|
|
491
|
+
result.hardSplashSoundVelocity = bs.readF32(); // 0x378
|
|
492
|
+
// 4. Loop of 5: water impact sound DataBlock refs → offsets 0x358+i*4
|
|
493
|
+
const waterSounds = [];
|
|
494
|
+
for (let i = 0; i < 5; i++) {
|
|
495
|
+
waterSounds.push(readDataBlockRef(bs));
|
|
496
|
+
}
|
|
497
|
+
result.waterSounds = waterSounds;
|
|
498
|
+
// 5. dustEmitter DataBlock ref → offset 0x3c4
|
|
499
|
+
result.dustEmitter = readDataBlockRef(bs);
|
|
500
|
+
// 6. Loop of 3: damage emitter DataBlock refs → offsets 0x3dc+i*4
|
|
501
|
+
const damageEmitters = [];
|
|
502
|
+
for (let i = 0; i < 3; i++) {
|
|
503
|
+
damageEmitters.push(readDataBlockRef(bs));
|
|
504
|
+
}
|
|
505
|
+
result.damageEmitters = damageEmitters;
|
|
506
|
+
// 7. Loop of 2: splash emitter DataBlock refs → offsets 0x414+i*4
|
|
507
|
+
const splashEmitters = [];
|
|
508
|
+
for (let i = 0; i < 2; i++) {
|
|
509
|
+
splashEmitters.push(readDataBlockRef(bs));
|
|
510
|
+
}
|
|
511
|
+
result.splashEmitters = splashEmitters;
|
|
512
|
+
// 8. 2×3 F32 damage emitter offsets (2 sets of x,y,z) → offsets 0x3e8-0x3f4
|
|
513
|
+
result.damageEmitterOffset0 = {
|
|
514
|
+
x: bs.readF32(),
|
|
515
|
+
y: bs.readF32(),
|
|
516
|
+
z: bs.readF32(),
|
|
517
|
+
};
|
|
518
|
+
result.damageEmitterOffset1 = {
|
|
519
|
+
x: bs.readF32(),
|
|
520
|
+
y: bs.readF32(),
|
|
521
|
+
z: bs.readF32(),
|
|
522
|
+
};
|
|
523
|
+
// 9. 2 F32 damage level tolerance → offsets 0x400, 0x404
|
|
524
|
+
result.damageLevelTolerance0 = bs.readF32();
|
|
525
|
+
result.damageLevelTolerance1 = bs.readF32();
|
|
526
|
+
// 10. 2 F32 splash params → offsets 0x41c, 0x420
|
|
527
|
+
result.splashFreqMod = bs.readF32();
|
|
528
|
+
result.splashVelEpsilon = bs.readF32();
|
|
529
|
+
// 11. 2 F32 collision damage → offsets 0x390, 0x394
|
|
530
|
+
result.collDamageThresholdVel = bs.readF32();
|
|
531
|
+
result.collDamageMultiplier = bs.readF32();
|
|
532
|
+
return result;
|
|
533
|
+
}
|
|
534
|
+
// ============================================================
|
|
535
|
+
// FlyingVehicleData (extends VehicleData)
|
|
536
|
+
// ============================================================
|
|
537
|
+
function flyingVehicleDataUnpack(bs) {
|
|
538
|
+
const result = vehicleDataUnpack(bs);
|
|
539
|
+
// 2 sound refs
|
|
540
|
+
result.jetActivateSound = readDataBlockRef(bs);
|
|
541
|
+
result.jetDeactivateSound = readDataBlockRef(bs);
|
|
542
|
+
// 4 jet emitter refs
|
|
543
|
+
const jetEmitters = [];
|
|
544
|
+
for (let i = 0; i < 4; i++) {
|
|
545
|
+
jetEmitters.push(readDataBlockRef(bs));
|
|
546
|
+
}
|
|
547
|
+
result.jetEmitters = jetEmitters;
|
|
548
|
+
// Verified against decompiled binary FUN_0060fc40 — 16 F32s
|
|
549
|
+
result.maneuveringForce = bs.readF32();
|
|
550
|
+
result.horizontalSurfaceForce = bs.readF32();
|
|
551
|
+
result.verticalSurfaceForce = bs.readF32();
|
|
552
|
+
result.autoInputDamping = bs.readF32();
|
|
553
|
+
result.steeringForce = bs.readF32();
|
|
554
|
+
result.steeringRollForce = bs.readF32();
|
|
555
|
+
result.rollForce = bs.readF32();
|
|
556
|
+
result.autoAngularForce = bs.readF32();
|
|
557
|
+
result.rotationalDrag = bs.readF32();
|
|
558
|
+
result.maxAutoSpeed = bs.readF32();
|
|
559
|
+
result.autoLinearForce = bs.readF32();
|
|
560
|
+
result.hoverHeight = bs.readF32();
|
|
561
|
+
result.createHoverHeight = bs.readF32();
|
|
562
|
+
result.minTrailSpeed = bs.readF32();
|
|
563
|
+
result.vertThrustMultiple = bs.readF32();
|
|
564
|
+
result.maxForwardSpeed = bs.readF32();
|
|
565
|
+
return result;
|
|
566
|
+
}
|
|
567
|
+
// ============================================================
|
|
568
|
+
// HoverVehicleData (extends VehicleData)
|
|
569
|
+
// ============================================================
|
|
570
|
+
function hoverVehicleDataUnpack(bs) {
|
|
571
|
+
const result = vehicleDataUnpack(bs);
|
|
572
|
+
// 17 F32s
|
|
573
|
+
result.dragForce = bs.readF32();
|
|
574
|
+
result.mainThrustForce = bs.readF32();
|
|
575
|
+
result.reverseThrustForce = bs.readF32();
|
|
576
|
+
result.strafeThrustForce = bs.readF32();
|
|
577
|
+
result.turboFactor = bs.readF32();
|
|
578
|
+
result.stabLenMin = bs.readF32();
|
|
579
|
+
result.stabLenMax = bs.readF32();
|
|
580
|
+
result.stabSpringConstant = bs.readF32();
|
|
581
|
+
result.stabDampingConstant = bs.readF32();
|
|
582
|
+
result.gyroDrag = bs.readF32();
|
|
583
|
+
result.normalForce = bs.readF32();
|
|
584
|
+
result.restorativeForce = bs.readF32();
|
|
585
|
+
result.steeringForce = bs.readF32();
|
|
586
|
+
result.rollForce = bs.readF32();
|
|
587
|
+
result.pitchForce = bs.readF32();
|
|
588
|
+
result.floatingThrustFactor = bs.readF32();
|
|
589
|
+
result.brakingForce = bs.readF32();
|
|
590
|
+
// 3×F32 (dustTrailOffset)
|
|
591
|
+
result.dustTrailOffset = {
|
|
592
|
+
x: bs.readF32(),
|
|
593
|
+
y: bs.readF32(),
|
|
594
|
+
z: bs.readF32(),
|
|
595
|
+
};
|
|
596
|
+
// 2 F32s
|
|
597
|
+
result.dustTrailFreqMod = bs.readF32();
|
|
598
|
+
result.triggerTrailHeight = bs.readF32();
|
|
599
|
+
// 3 sound refs
|
|
600
|
+
result.floatSound = readDataBlockRef(bs);
|
|
601
|
+
result.thrustSound = readDataBlockRef(bs);
|
|
602
|
+
result.turboSound = readDataBlockRef(bs);
|
|
603
|
+
// 3 jet emitter refs
|
|
604
|
+
const jetEmitters = [];
|
|
605
|
+
for (let i = 0; i < 3; i++) {
|
|
606
|
+
jetEmitters.push(readDataBlockRef(bs));
|
|
607
|
+
}
|
|
608
|
+
result.jetEmitters = jetEmitters;
|
|
609
|
+
// dustTrailEmitter ref
|
|
610
|
+
result.dustTrailEmitter = readDataBlockRef(bs);
|
|
611
|
+
// 3 F32s
|
|
612
|
+
result.mainThrustEmitterFactor = bs.readF32();
|
|
613
|
+
result.strafeThrustEmitterFactor = bs.readF32();
|
|
614
|
+
result.reverseThrustEmitterFactor = bs.readF32();
|
|
615
|
+
return result;
|
|
616
|
+
}
|
|
617
|
+
// ============================================================
|
|
618
|
+
// WheeledVehicleData (extends VehicleData)
|
|
619
|
+
// ============================================================
|
|
620
|
+
function wheeledVehicleDataUnpack(bs) {
|
|
621
|
+
const result = vehicleDataUnpack(bs);
|
|
622
|
+
// 9 tire F32s
|
|
623
|
+
result.tireRadius = bs.readF32();
|
|
624
|
+
result.tireStaticFriction = bs.readF32();
|
|
625
|
+
result.tireKineticFriction = bs.readF32();
|
|
626
|
+
result.tireRestitution = bs.readF32();
|
|
627
|
+
result.tireLateralForce = bs.readF32();
|
|
628
|
+
result.tireLateralDamping = bs.readF32();
|
|
629
|
+
result.tireLateralRelaxation = bs.readF32();
|
|
630
|
+
result.tireLongitudinalForce = bs.readF32();
|
|
631
|
+
result.tireLongitudinalDamping = bs.readF32();
|
|
632
|
+
// tire emitter ref
|
|
633
|
+
result.tireEmitter = readDataBlockRef(bs);
|
|
634
|
+
// 4 sound refs
|
|
635
|
+
result.jetSound = readDataBlockRef(bs);
|
|
636
|
+
result.engineSound = readDataBlockRef(bs);
|
|
637
|
+
result.squealSound = readDataBlockRef(bs);
|
|
638
|
+
result.wadeSound = readDataBlockRef(bs);
|
|
639
|
+
// 11 F32s
|
|
640
|
+
result.spring = bs.readF32();
|
|
641
|
+
result.springDamping = bs.readF32();
|
|
642
|
+
result.springLength = bs.readF32();
|
|
643
|
+
result.brakeTorque = bs.readF32();
|
|
644
|
+
result.engineTorque = bs.readF32();
|
|
645
|
+
result.engineBrake = bs.readF32();
|
|
646
|
+
result.maxWheelSpeed = bs.readF32();
|
|
647
|
+
result.steeringAngle = bs.readF32();
|
|
648
|
+
result.steeringReturn = bs.readF32();
|
|
649
|
+
result.steeringDamping = bs.readF32();
|
|
650
|
+
result.powerSteeringFactor = bs.readF32();
|
|
651
|
+
return result;
|
|
652
|
+
}
|
|
653
|
+
// ============================================================
|
|
654
|
+
// StaticShapeData (extends ShapeBaseData)
|
|
655
|
+
// ============================================================
|
|
656
|
+
function staticShapeDataUnpack(bs) {
|
|
657
|
+
const result = shapeBaseDataUnpack(bs);
|
|
658
|
+
result.noIndividualDamage = bs.readFlag();
|
|
659
|
+
result.dynamicTypeField = bs.readS32();
|
|
660
|
+
return result;
|
|
661
|
+
}
|
|
662
|
+
// ============================================================
|
|
663
|
+
// TurretData (extends StaticShapeData)
|
|
664
|
+
// ============================================================
|
|
665
|
+
function turretDataUnpack(bs) {
|
|
666
|
+
const result = staticShapeDataUnpack(bs);
|
|
667
|
+
// Verified against decompiled binary FUN_00654190
|
|
668
|
+
result.thetaMin = bs.readF32(); // 0x33c
|
|
669
|
+
result.thetaMax = bs.readF32(); // 0x340
|
|
670
|
+
result.thetaNull = bs.readF32(); // 0x344
|
|
671
|
+
result.neverUpdateControl = bs.readFlag(); // 0x34c
|
|
672
|
+
result.primaryAxis = bs.readRangedU32(0, 3); // 0x348 (FUN_0043f120(4) = 2 bits)
|
|
673
|
+
result.maxCapacitorEnergy = bs.readF32(); // 0x35c
|
|
674
|
+
result.capacitorRechargeRate = bs.readF32(); // 0x360
|
|
675
|
+
return result;
|
|
676
|
+
}
|
|
677
|
+
// ============================================================
|
|
678
|
+
// TurretImageData (extends ShapeBaseImageData)
|
|
679
|
+
// ============================================================
|
|
680
|
+
function turretImageDataUnpack(bs) {
|
|
681
|
+
const result = shapeBaseImageDataUnpack(bs);
|
|
682
|
+
// Verified against decompiled binary FUN_00654850
|
|
683
|
+
result.activationMS = bs.readInt(8); // << 5 on store (0x1c04)
|
|
684
|
+
result.deactivateDelayMS = bs.readInt(8); // << 5 on store (0x1c08)
|
|
685
|
+
result.degPerSecTheta = bs.readRangedU32(0, 1080); // 0x1c10 (FUN_0043f120(0x439))
|
|
686
|
+
result.degPerSecPhi = bs.readRangedU32(0, 1080); // 0x1c14
|
|
687
|
+
result.dontFireInsideDamageRadius = bs.readFlag(); // 0x1c1c
|
|
688
|
+
result.damageRadius = bs.readF32(); // 0x1c20
|
|
689
|
+
result.useCapacitor = bs.readFlag(); // 0x1c24 (NOT a DataBlockRef)
|
|
690
|
+
return result;
|
|
691
|
+
}
|
|
692
|
+
// ============================================================
|
|
693
|
+
// ItemData (extends ShapeBaseData)
|
|
694
|
+
// ============================================================
|
|
695
|
+
function itemDataUnpack(bs) {
|
|
696
|
+
const result = shapeBaseDataUnpack(bs);
|
|
697
|
+
// 2×readFloat(10)
|
|
698
|
+
result.friction = bs.readFloat(10);
|
|
699
|
+
result.elasticity = bs.readFloat(10);
|
|
700
|
+
// flag(sticky)
|
|
701
|
+
result.sticky = bs.readFlag();
|
|
702
|
+
// flag(gravityMod!=1) then readFloat(10)
|
|
703
|
+
if (bs.readFlag())
|
|
704
|
+
result.gravityMod = bs.readFloat(10);
|
|
705
|
+
// flag(maxVelocity!=-1) then F32
|
|
706
|
+
if (bs.readFlag())
|
|
707
|
+
result.maxVelocity = bs.readF32();
|
|
708
|
+
// flag(hasLight); if true: readInt(2) + 4×readFloat(7) + S32 + F32 + flag
|
|
709
|
+
if (bs.readFlag()) {
|
|
710
|
+
result.lightType = bs.readInt(2);
|
|
711
|
+
result.lightColor = {
|
|
712
|
+
r: bs.readFloat(7),
|
|
713
|
+
g: bs.readFloat(7),
|
|
714
|
+
b: bs.readFloat(7),
|
|
715
|
+
a: bs.readFloat(7),
|
|
716
|
+
};
|
|
717
|
+
result.lightTime = bs.readS32();
|
|
718
|
+
result.lightRadius = bs.readF32();
|
|
719
|
+
result.lightOnlyStatic = bs.readFlag();
|
|
720
|
+
}
|
|
721
|
+
return result;
|
|
722
|
+
}
|
|
723
|
+
// ============================================================
|
|
724
|
+
// ProjectileData (extends GameBaseData — 0 parent bits)
|
|
725
|
+
// ============================================================
|
|
726
|
+
function projectileDataUnpack(bs) {
|
|
727
|
+
const result = {};
|
|
728
|
+
// readString (projectileName)
|
|
729
|
+
result.projectileShapeName = bs.readString();
|
|
730
|
+
// 2×S32
|
|
731
|
+
result.faceViewerLinkTime = bs.readS32();
|
|
732
|
+
result.lifetime = bs.readS32();
|
|
733
|
+
// flag(faceViewer)
|
|
734
|
+
result.faceViewer = bs.readFlag();
|
|
735
|
+
// flag(nonDefaultScale); if true: 3×F32
|
|
736
|
+
if (bs.readFlag()) {
|
|
737
|
+
result.scale = {
|
|
738
|
+
x: bs.readF32(),
|
|
739
|
+
y: bs.readF32(),
|
|
740
|
+
z: bs.readF32(),
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
// 9 readDataBlockRef fields in pack order (verified against decompiled
|
|
744
|
+
// Tribes2.exe FUN_00631010 / FUN_006303f0 struct offsets).
|
|
745
|
+
result.baseEmitter = readDataBlockRef(bs); // 0xe8 ParticleEmitterData
|
|
746
|
+
result.delayEmitter = readDataBlockRef(bs); // 0xec ParticleEmitterData
|
|
747
|
+
result.bubbleEmitter = readDataBlockRef(bs); // 0xf0 ParticleEmitterData
|
|
748
|
+
result.explosion = readDataBlockRef(bs); // 0xf4 ExplosionData
|
|
749
|
+
result.underwaterExplosion = readDataBlockRef(bs); // 0xf8 ExplosionData
|
|
750
|
+
result.splash = readDataBlockRef(bs); // 0xfc SplashData
|
|
751
|
+
result.sound = readDataBlockRef(bs); // 0x100 AudioProfile (in-flight)
|
|
752
|
+
result.wetFireSound = readDataBlockRef(bs); // 0x104 AudioProfile
|
|
753
|
+
result.fireSound = readDataBlockRef(bs); // 0x108 AudioProfile
|
|
754
|
+
// 6 decal refs (loop)
|
|
755
|
+
const decals = [];
|
|
756
|
+
for (let i = 0; i < 6; i++) {
|
|
757
|
+
decals.push(readDataBlockRef(bs));
|
|
758
|
+
}
|
|
759
|
+
result.decals = decals;
|
|
760
|
+
// flag(hasLight); if true: readFloat(8) + 3×readFloat(7)
|
|
761
|
+
if (bs.readFlag()) {
|
|
762
|
+
result.lightRadius = bs.readFloat(8);
|
|
763
|
+
result.lightColor = {
|
|
764
|
+
r: bs.readFloat(7),
|
|
765
|
+
g: bs.readFloat(7),
|
|
766
|
+
b: bs.readFloat(7),
|
|
767
|
+
};
|
|
768
|
+
}
|
|
769
|
+
// flag(hasUnderwaterColor); if true: 3×readFloat(7)
|
|
770
|
+
if (bs.readFlag()) {
|
|
771
|
+
result.underwaterLightColor = {
|
|
772
|
+
r: bs.readFloat(7),
|
|
773
|
+
g: bs.readFloat(7),
|
|
774
|
+
b: bs.readFloat(7),
|
|
775
|
+
};
|
|
776
|
+
}
|
|
777
|
+
// explodeOnWaterImpact: 8-bit bool via stream->write(sizeof(bool))
|
|
778
|
+
// (confirmed in decompiled binary FUN_00631360: read(1,&var) at offset 0xcc)
|
|
779
|
+
result.explodeOnWaterImpact = readBool(bs);
|
|
780
|
+
// depthTolerance: F32
|
|
781
|
+
result.depthTolerance = bs.readF32();
|
|
782
|
+
return result;
|
|
783
|
+
}
|
|
784
|
+
// ============================================================
|
|
785
|
+
// LinearProjectileData (extends ProjectileData)
|
|
786
|
+
// ============================================================
|
|
787
|
+
function linearProjectileDataUnpack(bs) {
|
|
788
|
+
const result = projectileDataUnpack(bs);
|
|
789
|
+
// 2×F32
|
|
790
|
+
result.dryVelocity = bs.readF32();
|
|
791
|
+
result.wetVelocity = bs.readF32();
|
|
792
|
+
// 2×U32
|
|
793
|
+
result.fizzleTime = bs.readU32();
|
|
794
|
+
result.fizzleType = bs.readU32();
|
|
795
|
+
// flag
|
|
796
|
+
result.hardRetarget = bs.readFlag();
|
|
797
|
+
// 2×readRangedU32(0,90)
|
|
798
|
+
result.inheritedVelocityScale = bs.readRangedU32(0, 90);
|
|
799
|
+
result.lifetimeMS = bs.readRangedU32(0, 90);
|
|
800
|
+
// 2×U32
|
|
801
|
+
result.collideWithOwnerTimeMS = bs.readU32();
|
|
802
|
+
result.proximityRadius = bs.readU32();
|
|
803
|
+
// flag
|
|
804
|
+
result.tracerProjectile = bs.readFlag();
|
|
805
|
+
return result;
|
|
806
|
+
}
|
|
807
|
+
// ============================================================
|
|
808
|
+
// GrenadeProjectileData (extends ProjectileData)
|
|
809
|
+
// ============================================================
|
|
810
|
+
function grenadeProjectileDataUnpack(bs) {
|
|
811
|
+
const result = projectileDataUnpack(bs);
|
|
812
|
+
result.armingDelayMS = bs.readS32();
|
|
813
|
+
result.muzzleVelocity = bs.readF32();
|
|
814
|
+
result.grenadeElasticity = bs.readF32();
|
|
815
|
+
result.grenadeFriction = bs.readF32();
|
|
816
|
+
result.drag = bs.readF32();
|
|
817
|
+
result.density = bs.readF32();
|
|
818
|
+
result.gravityMod = bs.readF32();
|
|
819
|
+
result.lifetimeMS = bs.readS32();
|
|
820
|
+
return result;
|
|
821
|
+
}
|
|
822
|
+
// ============================================================
|
|
823
|
+
// SeekerProjectileData (extends ProjectileData)
|
|
824
|
+
// ============================================================
|
|
825
|
+
function seekerProjectileDataUnpack(bs) {
|
|
826
|
+
const result = projectileDataUnpack(bs);
|
|
827
|
+
// Verified against decompiled binary (lines 430494-430580)
|
|
828
|
+
// 10 F32/S32 (offsets 0x138-0x164)
|
|
829
|
+
result.lifetimeMS = bs.readS32(); // 0x138
|
|
830
|
+
result.muzzleVelocity = bs.readF32(); // 0x13c
|
|
831
|
+
result.turningSpeed = bs.readF32(); // 0x140
|
|
832
|
+
result.proximityRadius = bs.readF32(); // 0x14c
|
|
833
|
+
result.terrainAvoidanceSpeed = bs.readF32(); // 0x150
|
|
834
|
+
result.terrainScanAhead = bs.readF32(); // 0x154
|
|
835
|
+
result.terrainHeightFail = bs.readF32(); // 0x158
|
|
836
|
+
result.terrainAvoidanceRadius = bs.readF32(); // 0x15c
|
|
837
|
+
result.flareDistance = bs.readF32(); // 0x160
|
|
838
|
+
result.flareAngle = bs.readF32(); // 0x164
|
|
839
|
+
// useFlechette — 8-bit bool via stream->read(1, &var) at offset 0x168
|
|
840
|
+
// (confirmed: read(1, &cStack_21) = 1 BYTE = 8 bits, NOT readF32)
|
|
841
|
+
result.useFlechette = readBool(bs);
|
|
842
|
+
// 2 F32 (offsets 0x144, 0x148)
|
|
843
|
+
result.maxVelocity = bs.readF32();
|
|
844
|
+
result.acceleration = bs.readF32();
|
|
845
|
+
// 2 S32 (offsets 0x16c, 0x180)
|
|
846
|
+
result.flechetteDelayMs = bs.readS32();
|
|
847
|
+
result.exhaustTimeMs = bs.readS32();
|
|
848
|
+
// 2 readString (offsets 0x184, 0x190)
|
|
849
|
+
result.exhaustNodeName = bs.readString();
|
|
850
|
+
result.casingShapeName = bs.readString();
|
|
851
|
+
// 3 conditional DataBlock refs (offsets 0x18c, 0x174, 0x17c)
|
|
852
|
+
result.casingDebris = readDataBlockRef(bs);
|
|
853
|
+
result.puffEmitter = readDataBlockRef(bs);
|
|
854
|
+
result.exhaustEmitter = readDataBlockRef(bs);
|
|
855
|
+
return result;
|
|
856
|
+
}
|
|
857
|
+
// ============================================================
|
|
858
|
+
// SniperProjectileData (extends ProjectileData)
|
|
859
|
+
// ============================================================
|
|
860
|
+
function sniperProjectileDataUnpack(bs) {
|
|
861
|
+
const result = projectileDataUnpack(bs);
|
|
862
|
+
// Verified against decompiled binary FUN_00641d40
|
|
863
|
+
// 2×F32 (offsets 0x138, 0x13c)
|
|
864
|
+
result.maxRifleRange = bs.readF32();
|
|
865
|
+
result.rifleHeadMultiplier = bs.readF32();
|
|
866
|
+
// ColorF via FUN_0043f040 (packed 4×U8 = 32 bits at offset 0x140)
|
|
867
|
+
result.beamColor = readColorF(bs);
|
|
868
|
+
// 9×F32 (offsets 0x150, 0x154, 0x158, 0x168, 0x15c, 0x160, 0x164, 0x16c, 0x170)
|
|
869
|
+
result.fadeTime = bs.readF32();
|
|
870
|
+
result.startBeamWidth = bs.readF32();
|
|
871
|
+
result.endBeamWidth = bs.readF32();
|
|
872
|
+
result.pulseBeamWidth = bs.readF32();
|
|
873
|
+
result.beamFlareAngle = bs.readF32();
|
|
874
|
+
result.minFlareSize = bs.readF32();
|
|
875
|
+
result.maxFlareSize = bs.readF32();
|
|
876
|
+
result.pulseSpeed = bs.readF32();
|
|
877
|
+
result.pulseLength = bs.readF32();
|
|
878
|
+
// ColorF via FUN_0043f040 (packed 4×U8 = 32 bits at offset 0x178)
|
|
879
|
+
result.lightColor = readColorF(bs);
|
|
880
|
+
// F32 (offset 0x174)
|
|
881
|
+
result.lightRadius = bs.readF32();
|
|
882
|
+
// 12 readString (ST_NUM_TEX=0xc)
|
|
883
|
+
const textures = [];
|
|
884
|
+
for (let i = 0; i < 12; i++) {
|
|
885
|
+
textures.push(bs.readString());
|
|
886
|
+
}
|
|
887
|
+
result.textures = textures;
|
|
888
|
+
return result;
|
|
889
|
+
}
|
|
890
|
+
// ============================================================
|
|
891
|
+
// ShockLanceProjectileData (extends ProjectileData)
|
|
892
|
+
// ============================================================
|
|
893
|
+
function shockLanceProjectileDataUnpack(bs) {
|
|
894
|
+
// Verified against decompiled binary FUN_0064e840
|
|
895
|
+
const result = projectileDataUnpack(bs);
|
|
896
|
+
// 7 F32 (offsets 0x138, 0x13c, 0x160, 0x164, 0x168, 0x16c, 0x170)
|
|
897
|
+
result.zapDuration = bs.readF32();
|
|
898
|
+
result.boltLength = bs.readF32();
|
|
899
|
+
result.numParts = bs.readF32();
|
|
900
|
+
result.lightningFreq = bs.readF32();
|
|
901
|
+
result.lightningDensity = bs.readF32();
|
|
902
|
+
result.lightningAmp = bs.readF32();
|
|
903
|
+
result.lightningWidth = bs.readF32();
|
|
904
|
+
// DataBlockRef (shockwave at offset 0x190)
|
|
905
|
+
result.shockwave = readDataBlockRef(bs);
|
|
906
|
+
// 2×4 loop: startWidth[2], endWidth[2], boltSpeed[2], texWrap[2]
|
|
907
|
+
// (offsets 0x140+i*4, 0x148+i*4, 0x150+i*4, 0x158+i*4)
|
|
908
|
+
const startWidth = [];
|
|
909
|
+
const endWidth = [];
|
|
910
|
+
const boltSpeed = [];
|
|
911
|
+
const texWrap = [];
|
|
912
|
+
for (let i = 0; i < 2; i++) {
|
|
913
|
+
startWidth.push(bs.readF32());
|
|
914
|
+
endWidth.push(bs.readF32());
|
|
915
|
+
boltSpeed.push(bs.readF32());
|
|
916
|
+
texWrap.push(bs.readF32());
|
|
917
|
+
}
|
|
918
|
+
result.startWidth = startWidth;
|
|
919
|
+
result.endWidth = endWidth;
|
|
920
|
+
result.boltSpeed = boltSpeed;
|
|
921
|
+
result.texWrap = texWrap;
|
|
922
|
+
// 4 readString (texture[4] at offset 0x174)
|
|
923
|
+
const slTextures = [];
|
|
924
|
+
for (let i = 0; i < 4; i++) {
|
|
925
|
+
slTextures.push(bs.readString());
|
|
926
|
+
}
|
|
927
|
+
result.textures = slTextures;
|
|
928
|
+
// DataBlockRef (emitter at offset 0x188)
|
|
929
|
+
result.emitter = readDataBlockRef(bs);
|
|
930
|
+
return result;
|
|
931
|
+
}
|
|
932
|
+
// ============================================================
|
|
933
|
+
// ELFProjectileData (extends ProjectileData)
|
|
934
|
+
// ============================================================
|
|
935
|
+
function elfProjectileDataUnpack(bs) {
|
|
936
|
+
const result = projectileDataUnpack(bs);
|
|
937
|
+
// 6×F32
|
|
938
|
+
result.beamRange = bs.readF32();
|
|
939
|
+
result.beamDrainRate = bs.readF32();
|
|
940
|
+
result.muzzleVelocity = bs.readF32();
|
|
941
|
+
result.proximityRadius = bs.readF32();
|
|
942
|
+
result.startWidth = bs.readF32();
|
|
943
|
+
result.endWidth = bs.readF32();
|
|
944
|
+
// 3 readString
|
|
945
|
+
result.mainBeamTexture = bs.readString();
|
|
946
|
+
result.innerBeamTexture = bs.readString();
|
|
947
|
+
result.flareTexture = bs.readString();
|
|
948
|
+
// 1 readDataBlockRef
|
|
949
|
+
result.hitEmitter = readDataBlockRef(bs);
|
|
950
|
+
return result;
|
|
951
|
+
}
|
|
952
|
+
// ============================================================
|
|
953
|
+
// RepairProjectileData (extends ProjectileData)
|
|
954
|
+
// ============================================================
|
|
955
|
+
function repairProjectileDataUnpack(bs) {
|
|
956
|
+
const result = projectileDataUnpack(bs);
|
|
957
|
+
// 8×F32
|
|
958
|
+
result.beamRange = bs.readF32();
|
|
959
|
+
result.beamRepairRate = bs.readF32();
|
|
960
|
+
result.muzzleVelocity = bs.readF32();
|
|
961
|
+
result.proximityRadius = bs.readF32();
|
|
962
|
+
result.startWidth = bs.readF32();
|
|
963
|
+
result.endWidth = bs.readF32();
|
|
964
|
+
result.startBeamWidth = bs.readF32();
|
|
965
|
+
result.endBeamWidth = bs.readF32();
|
|
966
|
+
// 2 readString
|
|
967
|
+
result.mainBeamTexture = bs.readString();
|
|
968
|
+
result.innerBeamTexture = bs.readString();
|
|
969
|
+
return result;
|
|
970
|
+
}
|
|
971
|
+
// ============================================================
|
|
972
|
+
// TargetProjectileData (extends ProjectileData)
|
|
973
|
+
// ============================================================
|
|
974
|
+
function targetProjectileDataUnpack(bs) {
|
|
975
|
+
const result = projectileDataUnpack(bs);
|
|
976
|
+
// Verified against decompiled binary FUN_006477f0
|
|
977
|
+
// F32 (offset 0x138)
|
|
978
|
+
result.maxRifleRange = bs.readF32();
|
|
979
|
+
// ColorF via FUN_0043f040 (packed 4×U8 = 32 bits at offset 0x13c)
|
|
980
|
+
result.beamColor = readColorF(bs);
|
|
981
|
+
// 7×F32 (offsets 0x14c, 0x15c, 0x150, 0x154, 0x158, 0x160, 0x164)
|
|
982
|
+
result.startBeamWidth = bs.readF32();
|
|
983
|
+
result.pulseBeamWidth = bs.readF32();
|
|
984
|
+
result.beamFlareAngle = bs.readF32();
|
|
985
|
+
result.minFlareSize = bs.readF32();
|
|
986
|
+
result.maxFlareSize = bs.readF32();
|
|
987
|
+
result.pulseSpeed = bs.readF32();
|
|
988
|
+
result.pulseLength = bs.readF32();
|
|
989
|
+
// 4 readString (textureName[4] at offset 0x16c)
|
|
990
|
+
const targetTextures = [];
|
|
991
|
+
for (let i = 0; i < 4; i++) {
|
|
992
|
+
targetTextures.push(bs.readString());
|
|
993
|
+
}
|
|
994
|
+
result.textures = targetTextures;
|
|
995
|
+
return result;
|
|
996
|
+
}
|
|
997
|
+
// ============================================================
|
|
998
|
+
// TracerProjectileData (extends LinearProjectileData)
|
|
999
|
+
// ============================================================
|
|
1000
|
+
function tracerProjectileDataUnpack(bs) {
|
|
1001
|
+
// Verified against decompiled binary FUN_00640160
|
|
1002
|
+
const result = linearProjectileDataUnpack(bs);
|
|
1003
|
+
// 3 F32 (offsets 0x168, 0x184, 0x170)
|
|
1004
|
+
result.tracerLength = bs.readF32();
|
|
1005
|
+
result.tracerAlpha = bs.readF32();
|
|
1006
|
+
result.tracerMinPixels = bs.readF32();
|
|
1007
|
+
// readBool 8-bit (offset 0x16c)
|
|
1008
|
+
result.crossViewFraction = readBool(bs);
|
|
1009
|
+
// ColorF via FUN_0043f040 (packed 4×U8 at offset 0x174)
|
|
1010
|
+
result.tracerColor = readColorF(bs);
|
|
1011
|
+
// 2 F32 (offsets 0x188, 0x18c)
|
|
1012
|
+
result.tracerWidth = bs.readF32();
|
|
1013
|
+
result.muzzleVelocity = bs.readF32();
|
|
1014
|
+
// readBool 8-bit (offset 0x190)
|
|
1015
|
+
result.proximityRadius = readBool(bs);
|
|
1016
|
+
// 2 readString (loop count=2 at offset 0x194)
|
|
1017
|
+
result.textureName0 = bs.readString();
|
|
1018
|
+
result.textureName1 = bs.readString();
|
|
1019
|
+
return result;
|
|
1020
|
+
}
|
|
1021
|
+
// ============================================================
|
|
1022
|
+
// EnergyProjectileData (extends GrenadeProjectileData)
|
|
1023
|
+
// ============================================================
|
|
1024
|
+
function energyProjectileDataUnpack(bs) {
|
|
1025
|
+
// Verified against decompiled binary FUN_00694d80 — parent is FUN_00633cf0 (GrenadeProjectileData)
|
|
1026
|
+
const result = grenadeProjectileDataUnpack(bs);
|
|
1027
|
+
// 7×F32
|
|
1028
|
+
result.energyDrainPerSecond = bs.readF32();
|
|
1029
|
+
result.energyMinDrain = bs.readF32();
|
|
1030
|
+
result.beamWidth = bs.readF32();
|
|
1031
|
+
result.beamRange = bs.readF32();
|
|
1032
|
+
result.numSegments = bs.readF32();
|
|
1033
|
+
result.texRepeat = bs.readF32();
|
|
1034
|
+
result.beamFlareAngle = bs.readF32();
|
|
1035
|
+
// 2 readString
|
|
1036
|
+
result.beamTexture = bs.readString();
|
|
1037
|
+
result.flareTexture = bs.readString();
|
|
1038
|
+
return result;
|
|
1039
|
+
}
|
|
1040
|
+
// ============================================================
|
|
1041
|
+
// LinearFlareProjectileData (extends LinearProjectileData)
|
|
1042
|
+
// ============================================================
|
|
1043
|
+
function linearFlareProjectileDataUnpack(bs) {
|
|
1044
|
+
// Verified against decompiled binary FUN_0063dc80
|
|
1045
|
+
const result = linearProjectileDataUnpack(bs);
|
|
1046
|
+
// F32 (offset 0x168)
|
|
1047
|
+
result.numFlares = bs.readF32();
|
|
1048
|
+
// ColorF via FUN_0043f040 (packed 4×U8 at offset 0x178)
|
|
1049
|
+
result.flareColor = readColorF(bs);
|
|
1050
|
+
// 2 readString (offsets 0x188, 0x18c)
|
|
1051
|
+
result.flareTexture = bs.readString();
|
|
1052
|
+
result.smokeTexture = bs.readString();
|
|
1053
|
+
// 3×F32 loop (offset 0x16c, count=3)
|
|
1054
|
+
result.size = bs.readF32();
|
|
1055
|
+
result.flareModTexture = bs.readF32();
|
|
1056
|
+
result.smokeSize = bs.readF32();
|
|
1057
|
+
return result;
|
|
1058
|
+
}
|
|
1059
|
+
// ============================================================
|
|
1060
|
+
// BombProjectileData (extends GrenadeProjectileData)
|
|
1061
|
+
// ============================================================
|
|
1062
|
+
function bombProjectileDataUnpack(bs) {
|
|
1063
|
+
const result = grenadeProjectileDataUnpack(bs);
|
|
1064
|
+
// 6×F32
|
|
1065
|
+
result.smokeDist = bs.readF32();
|
|
1066
|
+
result.noSmoke = bs.readF32();
|
|
1067
|
+
result.boomTime = bs.readF32();
|
|
1068
|
+
result.casingDist = bs.readF32();
|
|
1069
|
+
result.smokeCushion = bs.readF32();
|
|
1070
|
+
result.noSmokeCounter = bs.readF32();
|
|
1071
|
+
// 2 readString
|
|
1072
|
+
result.smokeTexture = bs.readString();
|
|
1073
|
+
result.bombTexture = bs.readString();
|
|
1074
|
+
return result;
|
|
1075
|
+
}
|
|
1076
|
+
// ============================================================
|
|
1077
|
+
// FlareProjectileData (extends GrenadeProjectileData)
|
|
1078
|
+
// ============================================================
|
|
1079
|
+
function flareProjectileDataUnpack(bs) {
|
|
1080
|
+
// Verified against decompiled binary FUN_006872f0
|
|
1081
|
+
const result = grenadeProjectileDataUnpack(bs);
|
|
1082
|
+
// F32 (offset 0x158)
|
|
1083
|
+
result.size = bs.readF32();
|
|
1084
|
+
// readBool 8-bit (offset 0x160, read(1,...))
|
|
1085
|
+
result.useLensFlare = readBool(bs);
|
|
1086
|
+
// 2 readString loop (offset 0x164, count=2)
|
|
1087
|
+
result.flareTexture = bs.readString();
|
|
1088
|
+
result.lensFlareTexture = bs.readString();
|
|
1089
|
+
return result;
|
|
1090
|
+
}
|
|
1091
|
+
// ============================================================
|
|
1092
|
+
// ExplosionData (extends GameBaseData)
|
|
1093
|
+
// ============================================================
|
|
1094
|
+
function explosionDataUnpack(bs) {
|
|
1095
|
+
const result = {};
|
|
1096
|
+
// readString
|
|
1097
|
+
result.dtsFileName = bs.readString();
|
|
1098
|
+
// 2 readDataBlockRef
|
|
1099
|
+
result.soundProfile = readDataBlockRef(bs);
|
|
1100
|
+
result.particleEmitter = readDataBlockRef(bs);
|
|
1101
|
+
// readInt(14) + F32 + flag
|
|
1102
|
+
result.particleDensity = bs.readInt(14);
|
|
1103
|
+
result.particleRadius = bs.readF32();
|
|
1104
|
+
result.faceViewer = bs.readFlag();
|
|
1105
|
+
// flag(scale); if true: 3×readInt(16)
|
|
1106
|
+
if (bs.readFlag()) {
|
|
1107
|
+
result.explosionScale = {
|
|
1108
|
+
x: bs.readInt(16),
|
|
1109
|
+
y: bs.readInt(16),
|
|
1110
|
+
z: bs.readInt(16),
|
|
1111
|
+
};
|
|
1112
|
+
}
|
|
1113
|
+
// readInt(14) + 4×readRangedU32 + 2×readRangedU32(0,1000) + readInt(14) + readRangedU32(0,10000)
|
|
1114
|
+
result.playSpeed = bs.readInt(14);
|
|
1115
|
+
result.debrisThetaMin = bs.readRangedU32(0, 180);
|
|
1116
|
+
result.debrisThetaMax = bs.readRangedU32(0, 180);
|
|
1117
|
+
result.debrisPhiMin = bs.readRangedU32(0, 360);
|
|
1118
|
+
result.debrisPhiMax = bs.readRangedU32(0, 360);
|
|
1119
|
+
result.debrisMinVelocity = bs.readRangedU32(0, 1000);
|
|
1120
|
+
result.debrisMaxVelocity = bs.readRangedU32(0, 1000);
|
|
1121
|
+
result.debrisNum = bs.readInt(14);
|
|
1122
|
+
result.debrisVariance = bs.readRangedU32(0, 10000);
|
|
1123
|
+
// 4×readInt(16) + F32 + 2 flags
|
|
1124
|
+
result.delayMS = bs.readInt(16);
|
|
1125
|
+
result.delayVariance = bs.readInt(16);
|
|
1126
|
+
result.lifetimeMS = bs.readInt(16);
|
|
1127
|
+
result.lifetimeVariance = bs.readInt(16);
|
|
1128
|
+
result.offset = bs.readF32();
|
|
1129
|
+
result.shakeCamera = bs.readFlag();
|
|
1130
|
+
result.hasLight = bs.readFlag();
|
|
1131
|
+
// 9×F32
|
|
1132
|
+
result.camShakeFreq = {
|
|
1133
|
+
x: bs.readF32(),
|
|
1134
|
+
y: bs.readF32(),
|
|
1135
|
+
z: bs.readF32(),
|
|
1136
|
+
};
|
|
1137
|
+
result.camShakeAmp = {
|
|
1138
|
+
x: bs.readF32(),
|
|
1139
|
+
y: bs.readF32(),
|
|
1140
|
+
z: bs.readF32(),
|
|
1141
|
+
};
|
|
1142
|
+
result.camShakeDuration = bs.readF32();
|
|
1143
|
+
result.camShakeRadius = bs.readF32();
|
|
1144
|
+
result.camShakeFalloff = bs.readF32();
|
|
1145
|
+
// readDataBlockRef(shockwave) + 1 debris ref + 4 emitter refs + 5 sub-explosion refs
|
|
1146
|
+
result.shockwave = readDataBlockRef(bs);
|
|
1147
|
+
result.debris = readDataBlockRef(bs);
|
|
1148
|
+
const emitters = [];
|
|
1149
|
+
for (let i = 0; i < 4; i++) {
|
|
1150
|
+
emitters.push(readDataBlockRef(bs));
|
|
1151
|
+
}
|
|
1152
|
+
result.emitters = emitters;
|
|
1153
|
+
const subExplosions = [];
|
|
1154
|
+
for (let i = 0; i < 5; i++) {
|
|
1155
|
+
subExplosions.push(readDataBlockRef(bs));
|
|
1156
|
+
}
|
|
1157
|
+
result.subExplosions = subExplosions;
|
|
1158
|
+
// readRangedU32(0,4) count; loop: readFloat(8) times + 3×readRangedU32(0,16000) sizes
|
|
1159
|
+
const timeCount = bs.readRangedU32(0, 4);
|
|
1160
|
+
const times = [];
|
|
1161
|
+
for (let i = 0; i < timeCount; i++) {
|
|
1162
|
+
times.push(bs.readFloat(8));
|
|
1163
|
+
}
|
|
1164
|
+
result.times = times;
|
|
1165
|
+
const sizes = [];
|
|
1166
|
+
for (let i = 0; i < timeCount; i++) {
|
|
1167
|
+
sizes.push({
|
|
1168
|
+
x: bs.readRangedU32(0, 16000),
|
|
1169
|
+
y: bs.readRangedU32(0, 16000),
|
|
1170
|
+
z: bs.readRangedU32(0, 16000),
|
|
1171
|
+
});
|
|
1172
|
+
}
|
|
1173
|
+
result.sizes = sizes;
|
|
1174
|
+
return result;
|
|
1175
|
+
}
|
|
1176
|
+
// ============================================================
|
|
1177
|
+
// DebrisData (extends GameBaseData)
|
|
1178
|
+
// ============================================================
|
|
1179
|
+
function debrisDataUnpack(bs) {
|
|
1180
|
+
// Field order confirmed from decompiled binary FUN_006844d0
|
|
1181
|
+
const result = {};
|
|
1182
|
+
result.elasticity = bs.readF32(); // 0x50
|
|
1183
|
+
result.friction = bs.readF32(); // 0x4c
|
|
1184
|
+
result.numBounces = bs.readS32(); // 0x5c
|
|
1185
|
+
result.bounceVariance = bs.readS32(); // 0x60
|
|
1186
|
+
result.minSpinSpeed = bs.readF32(); // 0x64
|
|
1187
|
+
result.maxSpinSpeed = bs.readF32(); // 0x68
|
|
1188
|
+
// 4 bools via stream->write(sizeof(bool)) = 8 bits each (0x6c-0x6f)
|
|
1189
|
+
result.render2D = readBool(bs);
|
|
1190
|
+
result.explodeOnMaxBounce = readBool(bs);
|
|
1191
|
+
result.staticOnMaxBounce = readBool(bs);
|
|
1192
|
+
result.snapOnMaxBounce = readBool(bs);
|
|
1193
|
+
result.lifetime = bs.readF32(); // 0x54
|
|
1194
|
+
result.lifetimeVariance = bs.readF32(); // 0x58
|
|
1195
|
+
// "Written twice" bug: same offsets 0x64/0x68 written again
|
|
1196
|
+
result.minSpinSpeed_dup = bs.readF32();
|
|
1197
|
+
result.maxSpinSpeed_dup = bs.readF32();
|
|
1198
|
+
result.velocity = bs.readF32(); // 0x44
|
|
1199
|
+
result.velocityVariance = bs.readF32(); // 0x48
|
|
1200
|
+
// 2 bools via stream->write(sizeof(bool)) = 8 bits each (0x70, 0x71)
|
|
1201
|
+
result.useRadiusMass = readBool(bs);
|
|
1202
|
+
result.fade = readBool(bs);
|
|
1203
|
+
result.baseRadius = bs.readF32(); // 0x74
|
|
1204
|
+
result.gravModifier = bs.readF32(); // 0x78
|
|
1205
|
+
result.terminalVelocity = bs.readF32(); // 0x7c
|
|
1206
|
+
result.ignoreWater = readBool(bs); // 0x80 (8-bit bool)
|
|
1207
|
+
// 2 readString
|
|
1208
|
+
result.shapeFileName = bs.readString(); // 0x8c
|
|
1209
|
+
result.skinName = bs.readString(); // 0x84
|
|
1210
|
+
// 2 emitter refs (loop at 0xa4, 0xa8) + 1 explosion ref (0x94)
|
|
1211
|
+
result.emitter0 = readDataBlockRef(bs);
|
|
1212
|
+
result.emitter1 = readDataBlockRef(bs);
|
|
1213
|
+
result.explosion = readDataBlockRef(bs);
|
|
1214
|
+
return result;
|
|
1215
|
+
}
|
|
1216
|
+
// ============================================================
|
|
1217
|
+
// SplashData (extends GameBaseData)
|
|
1218
|
+
// ============================================================
|
|
1219
|
+
function splashDataUnpack(bs) {
|
|
1220
|
+
const result = {};
|
|
1221
|
+
// 3×F32 (scale)
|
|
1222
|
+
result.scale = {
|
|
1223
|
+
x: bs.readF32(),
|
|
1224
|
+
y: bs.readF32(),
|
|
1225
|
+
z: bs.readF32(),
|
|
1226
|
+
};
|
|
1227
|
+
// ~15 F32/S32 values
|
|
1228
|
+
result.delayMS = bs.readS32();
|
|
1229
|
+
result.delayVariance = bs.readS32();
|
|
1230
|
+
result.lifetimeMS = bs.readS32();
|
|
1231
|
+
result.lifetimeVariance = bs.readS32();
|
|
1232
|
+
result.width = bs.readF32();
|
|
1233
|
+
result.numSegments = bs.readS32();
|
|
1234
|
+
result.velocity = bs.readF32();
|
|
1235
|
+
result.height = bs.readF32();
|
|
1236
|
+
result.acceleration = bs.readF32();
|
|
1237
|
+
result.texWrap = bs.readF32();
|
|
1238
|
+
result.texFactor = bs.readF32();
|
|
1239
|
+
result.ejectionFreq = bs.readF32();
|
|
1240
|
+
result.ejectionAngle = bs.readF32();
|
|
1241
|
+
result.ringLifetime = bs.readF32();
|
|
1242
|
+
result.startRadius = bs.readF32();
|
|
1243
|
+
// explosion ref + 3 emitter refs
|
|
1244
|
+
result.explosion = readDataBlockRef(bs);
|
|
1245
|
+
const emitters = [];
|
|
1246
|
+
for (let i = 0; i < 3; i++) {
|
|
1247
|
+
emitters.push(readDataBlockRef(bs));
|
|
1248
|
+
}
|
|
1249
|
+
result.emitters = emitters;
|
|
1250
|
+
// 4× ColorF (4×U8 = 32 bits each, via FUN_0043f040)
|
|
1251
|
+
const colors = [];
|
|
1252
|
+
for (let i = 0; i < 4; i++) {
|
|
1253
|
+
colors.push(readColorF(bs));
|
|
1254
|
+
}
|
|
1255
|
+
result.colors = colors;
|
|
1256
|
+
// 4×F32 times
|
|
1257
|
+
const times = [];
|
|
1258
|
+
for (let i = 0; i < 4; i++) {
|
|
1259
|
+
times.push(bs.readF32());
|
|
1260
|
+
}
|
|
1261
|
+
result.times = times;
|
|
1262
|
+
// 2 readString
|
|
1263
|
+
result.textureName = bs.readString();
|
|
1264
|
+
result.foamTexture = bs.readString();
|
|
1265
|
+
return result;
|
|
1266
|
+
}
|
|
1267
|
+
// ============================================================
|
|
1268
|
+
// ShockwaveData (extends GameBaseData)
|
|
1269
|
+
// ============================================================
|
|
1270
|
+
function shockwaveDataUnpack(bs) {
|
|
1271
|
+
const result = {};
|
|
1272
|
+
// 3×F32 (scale)
|
|
1273
|
+
result.scale = {
|
|
1274
|
+
x: bs.readF32(),
|
|
1275
|
+
y: bs.readF32(),
|
|
1276
|
+
z: bs.readF32(),
|
|
1277
|
+
};
|
|
1278
|
+
// ~17 F32/S32/bool values
|
|
1279
|
+
result.delayMS = bs.readS32();
|
|
1280
|
+
result.delayVariance = bs.readS32();
|
|
1281
|
+
result.lifetimeMS = bs.readS32();
|
|
1282
|
+
result.lifetimeVariance = bs.readS32();
|
|
1283
|
+
result.width = bs.readF32();
|
|
1284
|
+
result.numSegments = bs.readS32();
|
|
1285
|
+
result.numVertSegments = bs.readS32();
|
|
1286
|
+
result.velocity = bs.readF32();
|
|
1287
|
+
result.height = bs.readF32();
|
|
1288
|
+
result.verticalCurve = bs.readF32();
|
|
1289
|
+
result.acceleration = bs.readF32();
|
|
1290
|
+
result.texWrap = bs.readF32();
|
|
1291
|
+
// 5 bools via stream->write(sizeof(bool)) = 8 bits each
|
|
1292
|
+
// (confirmed at offsets 0x98-0x9c in binary, using read(1,&var) pattern)
|
|
1293
|
+
result.is2D = readBool(bs);
|
|
1294
|
+
result.orientToNormal = readBool(bs);
|
|
1295
|
+
result.mapToTerrain = readBool(bs);
|
|
1296
|
+
result.renderBottom = readBool(bs);
|
|
1297
|
+
result.renderSquare = readBool(bs);
|
|
1298
|
+
// 3 emitter refs
|
|
1299
|
+
const emitters = [];
|
|
1300
|
+
for (let i = 0; i < 3; i++) {
|
|
1301
|
+
emitters.push(readDataBlockRef(bs));
|
|
1302
|
+
}
|
|
1303
|
+
result.emitters = emitters;
|
|
1304
|
+
// 4× ColorF (4×U8 = 32 bits each, via FUN_0043f040)
|
|
1305
|
+
const colors = [];
|
|
1306
|
+
for (let i = 0; i < 4; i++) {
|
|
1307
|
+
colors.push(readColorF(bs));
|
|
1308
|
+
}
|
|
1309
|
+
result.colors = colors;
|
|
1310
|
+
// 4×F32 times
|
|
1311
|
+
const times = [];
|
|
1312
|
+
for (let i = 0; i < 4; i++) {
|
|
1313
|
+
times.push(bs.readF32());
|
|
1314
|
+
}
|
|
1315
|
+
result.times = times;
|
|
1316
|
+
// 2 readString
|
|
1317
|
+
result.textureName = bs.readString();
|
|
1318
|
+
result.mapToTexture = bs.readString();
|
|
1319
|
+
return result;
|
|
1320
|
+
}
|
|
1321
|
+
// ============================================================
|
|
1322
|
+
// ParticleEmitterData (extends GameBaseData)
|
|
1323
|
+
// ============================================================
|
|
1324
|
+
function particleEmitterDataUnpack(bs) {
|
|
1325
|
+
const result = {};
|
|
1326
|
+
// 4 readInt(various)
|
|
1327
|
+
result.ejectionPeriodMS = bs.readInt(10);
|
|
1328
|
+
result.periodVarianceMS = bs.readInt(10);
|
|
1329
|
+
result.ejectionVelocity = bs.readInt(16);
|
|
1330
|
+
result.velocityVariance = bs.readInt(14);
|
|
1331
|
+
// flag then readInt(16)
|
|
1332
|
+
if (bs.readFlag()) {
|
|
1333
|
+
result.ejectionOffset = bs.readInt(16);
|
|
1334
|
+
}
|
|
1335
|
+
// 2×readRangedU32(0,180)
|
|
1336
|
+
result.thetaMin = bs.readRangedU32(0, 180);
|
|
1337
|
+
result.thetaMax = bs.readRangedU32(0, 180);
|
|
1338
|
+
// 2 conditional readRangedU32(0,360)
|
|
1339
|
+
if (bs.readFlag())
|
|
1340
|
+
result.phiReferenceVel = bs.readRangedU32(0, 360);
|
|
1341
|
+
if (bs.readFlag())
|
|
1342
|
+
result.phiVariance = bs.readRangedU32(0, 360);
|
|
1343
|
+
// 3 flags
|
|
1344
|
+
result.overrideAdvances = bs.readFlag();
|
|
1345
|
+
result.orientParticles = bs.readFlag();
|
|
1346
|
+
result.orientOnVelocity = bs.readFlag();
|
|
1347
|
+
// 2×readInt(10)
|
|
1348
|
+
result.lifetimeMS = bs.readInt(10);
|
|
1349
|
+
result.lifetimeVarianceMS = bs.readInt(10);
|
|
1350
|
+
// 2 flags
|
|
1351
|
+
result.useEmitterSizes = bs.readFlag();
|
|
1352
|
+
result.useEmitterColors = bs.readFlag();
|
|
1353
|
+
// U32 count (4 bytes) + per-particle: flag(1b) + conditional readClassId(11b)
|
|
1354
|
+
// Verified against decompiled binary FUN_006222a0:
|
|
1355
|
+
// read(4, &count) → U32 count
|
|
1356
|
+
// for each: readFlag → if true: FUN_00436d10 (readClassId, 11 bits); else: 0xFFFFFFFF
|
|
1357
|
+
const particleCount = bs.readU32();
|
|
1358
|
+
const particles = [];
|
|
1359
|
+
for (let i = 0; i < particleCount && i < 16; i++) {
|
|
1360
|
+
particles.push(readDataBlockRef(bs));
|
|
1361
|
+
}
|
|
1362
|
+
result.particles = particles;
|
|
1363
|
+
return result;
|
|
1364
|
+
}
|
|
1365
|
+
// ============================================================
|
|
1366
|
+
// ParticleData (extends GameBaseData)
|
|
1367
|
+
// ============================================================
|
|
1368
|
+
function particleDataUnpack(bs) {
|
|
1369
|
+
const result = {};
|
|
1370
|
+
// readFloat(10) + flag then F32
|
|
1371
|
+
result.dragCoefficient = bs.readFloat(10);
|
|
1372
|
+
if (bs.readFlag())
|
|
1373
|
+
result.windCoefficient = bs.readF32();
|
|
1374
|
+
// readSignedFloat(12)
|
|
1375
|
+
result.gravityCoefficient = bs.readSignedFloat(12);
|
|
1376
|
+
// readFloat(9) + flag then F32
|
|
1377
|
+
result.inheritedVelFactor = bs.readFloat(9);
|
|
1378
|
+
if (bs.readFlag())
|
|
1379
|
+
result.constantAcceleration = bs.readF32();
|
|
1380
|
+
// 2×readInt(10)
|
|
1381
|
+
result.lifetimeMS = bs.readInt(10);
|
|
1382
|
+
result.lifetimeVarianceMS = bs.readInt(10);
|
|
1383
|
+
// flag then F32 (spinSpeed at offset 0x58, conditional)
|
|
1384
|
+
if (bs.readFlag())
|
|
1385
|
+
result.spinSpeed = bs.readF32();
|
|
1386
|
+
// flag then 2×readInt(11) (spinRandomMin/Max at 0x5c/0x60)
|
|
1387
|
+
// Verified against decompiled binary FUN_006232d0:
|
|
1388
|
+
// spinRandom conditional checks BOTH defaults, reads 11-bit values (subtract 1000)
|
|
1389
|
+
if (bs.readFlag()) {
|
|
1390
|
+
result.spinRandomMin = bs.readInt(11);
|
|
1391
|
+
result.spinRandomMax = bs.readInt(11);
|
|
1392
|
+
}
|
|
1393
|
+
// useInvAlpha — always read (1-bit flag at offset 0x64)
|
|
1394
|
+
// NOT inside the spinRandom conditional (confirmed in binary: separate readFlag at line 416229)
|
|
1395
|
+
result.useInvAlpha = bs.readFlag();
|
|
1396
|
+
// readInt(2) count(+1); per key: 4×readFloat(7) + readFloat(14) + readFloat(8)
|
|
1397
|
+
const numKeys = bs.readInt(2) + 1;
|
|
1398
|
+
const keys = [];
|
|
1399
|
+
for (let i = 0; i < numKeys; i++) {
|
|
1400
|
+
keys.push({
|
|
1401
|
+
r: bs.readFloat(7),
|
|
1402
|
+
g: bs.readFloat(7),
|
|
1403
|
+
b: bs.readFloat(7),
|
|
1404
|
+
a: bs.readFloat(7),
|
|
1405
|
+
size: bs.readFloat(14),
|
|
1406
|
+
time: bs.readFloat(8),
|
|
1407
|
+
});
|
|
1408
|
+
}
|
|
1409
|
+
result.keys = keys;
|
|
1410
|
+
// readInt(6) texCount; per tex: readString
|
|
1411
|
+
const texCount = bs.readInt(6);
|
|
1412
|
+
const textures = [];
|
|
1413
|
+
for (let i = 0; i < texCount && i < 50; i++) {
|
|
1414
|
+
textures.push(bs.readString());
|
|
1415
|
+
}
|
|
1416
|
+
result.textures = textures;
|
|
1417
|
+
return result;
|
|
1418
|
+
}
|
|
1419
|
+
// ============================================================
|
|
1420
|
+
// AudioDescription (extends SimDataBlock — 0 parent bits)
|
|
1421
|
+
// ============================================================
|
|
1422
|
+
function audioDescriptionUnpack(bs) {
|
|
1423
|
+
const result = {};
|
|
1424
|
+
// readFloat(6)
|
|
1425
|
+
result.volume = bs.readFloat(6);
|
|
1426
|
+
// flag(looping); if true: 3×S32
|
|
1427
|
+
result.isLooping = bs.readFlag();
|
|
1428
|
+
if (result.isLooping) {
|
|
1429
|
+
result.loopCount = bs.readS32();
|
|
1430
|
+
result.minLoopGap = bs.readS32();
|
|
1431
|
+
result.maxLoopGap = bs.readS32();
|
|
1432
|
+
}
|
|
1433
|
+
// flag(is3D); if true: 2×F32 + 2×readInt(9) + readFloat(6) + readNormalVector(8) + F32
|
|
1434
|
+
// Verified: decompiled FUN_0040c1a0 has NO isStreaming flag between isLooping and is3D.
|
|
1435
|
+
// Offsets: isLooping=0x40, is3D=0x41 — consecutive with no gap.
|
|
1436
|
+
result.is3D = bs.readFlag();
|
|
1437
|
+
if (result.is3D) {
|
|
1438
|
+
result.referenceDistance = bs.readF32();
|
|
1439
|
+
result.maxDistance = bs.readF32();
|
|
1440
|
+
result.coneInsideAngle = bs.readInt(9);
|
|
1441
|
+
result.coneOutsideAngle = bs.readInt(9);
|
|
1442
|
+
result.coneOutsideVolume = bs.readFloat(6);
|
|
1443
|
+
result.coneVector = bs.readNormalVector(8);
|
|
1444
|
+
result.environmentLevel = bs.readF32();
|
|
1445
|
+
}
|
|
1446
|
+
// readInt(3) — type
|
|
1447
|
+
result.type = bs.readInt(3);
|
|
1448
|
+
return result;
|
|
1449
|
+
}
|
|
1450
|
+
// ============================================================
|
|
1451
|
+
// AudioProfile (extends SimDataBlock — 0 parent bits)
|
|
1452
|
+
// ============================================================
|
|
1453
|
+
function audioProfileUnpack(bs) {
|
|
1454
|
+
const result = {};
|
|
1455
|
+
// Verified: decompiled FUN_0040c7f0 reads 3 DataBlock refs + 1 string.
|
|
1456
|
+
// Offsets: 0x48 (description), 0x4c (effect/environment), 0x50 (sampleEnvironment)
|
|
1457
|
+
result.description = readDataBlockRef(bs);
|
|
1458
|
+
result.environment = readDataBlockRef(bs);
|
|
1459
|
+
result.sampleEnvironment = readDataBlockRef(bs);
|
|
1460
|
+
// readString (filename, .wav appended on read)
|
|
1461
|
+
result.filename = bs.readString();
|
|
1462
|
+
return result;
|
|
1463
|
+
}
|
|
1464
|
+
// ============================================================
|
|
1465
|
+
// AudioEnvironment (extends SimDataBlock — 0 parent bits)
|
|
1466
|
+
// ============================================================
|
|
1467
|
+
function audioEnvironmentUnpack(bs) {
|
|
1468
|
+
// Verified against decompiled binary FUN_0040b530
|
|
1469
|
+
const result = {};
|
|
1470
|
+
// flag(useRoom) → offset 0x3c
|
|
1471
|
+
result.useRoom = bs.readFlag();
|
|
1472
|
+
if (result.useRoom) {
|
|
1473
|
+
// readRangedU32(0, 26) → offset 0x40 (FUN_0043f120(0x1b=27), rangeSize=27, max=26)
|
|
1474
|
+
result.room = bs.readRangedU32(0, 26);
|
|
1475
|
+
}
|
|
1476
|
+
else {
|
|
1477
|
+
// 13 fields inside !useRoom branch (binary offsets 0x44-0x7c)
|
|
1478
|
+
result.roomHF = readRangedS32(bs, -10000, 0); // 0x44
|
|
1479
|
+
result.reflections = readRangedS32(bs, -10000, 10000); // 0x48
|
|
1480
|
+
result.reverb = readRangedS32(bs, -10000, 2000); // 0x4c
|
|
1481
|
+
result.roomRolloffFactor = readRangedF32(bs, 0.1, 10, 8); // 0x50
|
|
1482
|
+
result.decayTime = readRangedF32(bs, 0.1, 20, 8); // 0x54
|
|
1483
|
+
result.decayHFRatio = readRangedF32(bs, 0.1, 20, 8); // 0x58
|
|
1484
|
+
result.reflectionsDelay = readRangedF32(bs, 0, 0.3, 9); // 0x5c
|
|
1485
|
+
result.reverbDelay = readRangedF32(bs, 0, 0.1, 7); // 0x60
|
|
1486
|
+
result.roomVolume = readRangedS32(bs, -10000, 0); // 0x64
|
|
1487
|
+
result.effectVolume = readRangedF32(bs, 0, 1, 9); // 0x6c (9 bits, confirmed in binary)
|
|
1488
|
+
result.damping = readRangedF32(bs, 0, 2, 10); // 0x70 (10 bits, confirmed in binary)
|
|
1489
|
+
result.environmentSize = readRangedF32(bs, 1, 100, 8); // 0x74 (8 bits, confirmed in binary)
|
|
1490
|
+
result.environmentDiffusion = readRangedF32(bs, 0, 1, 10); // 0x78 (10 bits, confirmed in binary)
|
|
1491
|
+
// NOTE: No airAbsorption field in binary (V12 has it, Tribes 2 binary does not)
|
|
1492
|
+
result.flags = bs.readInt(6); // 0x7c
|
|
1493
|
+
}
|
|
1494
|
+
// Trailing field: always present after both branches (binary line 23471, offset 0x68)
|
|
1495
|
+
result.effectVolumeHF = readRangedF32(bs, 0, 1, 8);
|
|
1496
|
+
return result;
|
|
1497
|
+
}
|
|
1498
|
+
// ============================================================
|
|
1499
|
+
// AudioSampleEnvironment (extends SimDataBlock — 0 parent bits)
|
|
1500
|
+
// ============================================================
|
|
1501
|
+
function audioSampleEnvironmentUnpack(bs) {
|
|
1502
|
+
const result = {};
|
|
1503
|
+
// Series of rangedS32 and rangedF32 values + readInt(3)
|
|
1504
|
+
result.direct = readRangedS32(bs, -10000, 1000);
|
|
1505
|
+
result.directHF = readRangedS32(bs, -10000, 0);
|
|
1506
|
+
result.room = readRangedS32(bs, -10000, 1000);
|
|
1507
|
+
result.roomHF = readRangedS32(bs, -10000, 0);
|
|
1508
|
+
result.obstruction = readRangedF32(bs, 0, 1, 9);
|
|
1509
|
+
result.obstructionLFRatio = readRangedF32(bs, 0, 1, 8);
|
|
1510
|
+
result.occlusion = readRangedF32(bs, 0, 1, 9);
|
|
1511
|
+
result.occlusionLFRatio = readRangedF32(bs, 0, 1, 8);
|
|
1512
|
+
result.occlusionRoomRatio = readRangedF32(bs, 0, 10, 9);
|
|
1513
|
+
result.roomRolloff = readRangedF32(bs, 0, 10, 9);
|
|
1514
|
+
result.airAbsorption = readRangedF32(bs, 0, 10, 9);
|
|
1515
|
+
result.outsideVolumeHF = readRangedS32(bs, -10000, 0);
|
|
1516
|
+
result.flags = bs.readInt(3);
|
|
1517
|
+
return result;
|
|
1518
|
+
}
|
|
1519
|
+
// ============================================================
|
|
1520
|
+
// DecalData (extends SimDataBlock — 0 parent bits)
|
|
1521
|
+
// ============================================================
|
|
1522
|
+
function decalDataUnpack(bs) {
|
|
1523
|
+
return {
|
|
1524
|
+
sizeX: bs.readF32(),
|
|
1525
|
+
sizeY: bs.readF32(),
|
|
1526
|
+
textureName: bs.readString(),
|
|
1527
|
+
};
|
|
1528
|
+
}
|
|
1529
|
+
// ============================================================
|
|
1530
|
+
// CameraData (extends ShapeBaseData) — no additional fields
|
|
1531
|
+
// ============================================================
|
|
1532
|
+
function cameraDataUnpack(bs) {
|
|
1533
|
+
return shapeBaseDataUnpack(bs);
|
|
1534
|
+
}
|
|
1535
|
+
// ============================================================
|
|
1536
|
+
// SensorData — empty body
|
|
1537
|
+
// ============================================================
|
|
1538
|
+
function sensorDataUnpack(_bs) {
|
|
1539
|
+
return {};
|
|
1540
|
+
}
|
|
1541
|
+
// ============================================================
|
|
1542
|
+
// TriggerData — S32(tickPeriodMS)
|
|
1543
|
+
// ============================================================
|
|
1544
|
+
function triggerDataUnpack(bs) {
|
|
1545
|
+
return { tickPeriodMS: bs.readS32() };
|
|
1546
|
+
}
|
|
1547
|
+
// ============================================================
|
|
1548
|
+
// ForceFieldBareData
|
|
1549
|
+
// ============================================================
|
|
1550
|
+
function forceFieldBareDataUnpack(bs) {
|
|
1551
|
+
// Verified against decompiled binary FUN_00675580
|
|
1552
|
+
const result = {};
|
|
1553
|
+
// 3 S32/F32 (offsets 0x44, 0x48, 0x4c)
|
|
1554
|
+
result.fadeMS = bs.readS32();
|
|
1555
|
+
result.baseTranslucency = bs.readF32();
|
|
1556
|
+
result.powerOffTranslucency = bs.readF32();
|
|
1557
|
+
// 2 readFlag (offsets 0x8c, 0x8d — 1-bit flags, inline bit extraction)
|
|
1558
|
+
result.fadeInOnly = bs.readFlag();
|
|
1559
|
+
result.triggerEnable = bs.readFlag();
|
|
1560
|
+
// 2 ColorF via FUN_0043f040 (packed 4×U8 = 32 bits each, offsets 0x90, 0xa0)
|
|
1561
|
+
result.color1 = readColorF(bs);
|
|
1562
|
+
result.color2 = readColorF(bs);
|
|
1563
|
+
// Offsets 0x84, 0x88, 0x80, 0x78, 0x7c — named to match TorqueScript fields.
|
|
1564
|
+
// framesPerSec and numFrames are S32 (integers); the rest are F32.
|
|
1565
|
+
result.framesPerSec = bs.readS32(); // 0x84
|
|
1566
|
+
result.numFrames = bs.readS32(); // 0x88
|
|
1567
|
+
result.scrollSpeed = bs.readF32(); // 0x80
|
|
1568
|
+
result.umapping = bs.readF32(); // 0x78
|
|
1569
|
+
result.vmapping = bs.readF32(); // 0x7c
|
|
1570
|
+
// 5 readString (loop at 0x50+i*4)
|
|
1571
|
+
result.texture0 = bs.readString();
|
|
1572
|
+
result.texture1 = bs.readString();
|
|
1573
|
+
result.texture2 = bs.readString();
|
|
1574
|
+
result.texture3 = bs.readString();
|
|
1575
|
+
result.texture4 = bs.readString();
|
|
1576
|
+
return result;
|
|
1577
|
+
}
|
|
1578
|
+
// ============================================================
|
|
1579
|
+
// ParticleEmissionDummyData — F32(timeMultiple)
|
|
1580
|
+
// ============================================================
|
|
1581
|
+
function particleEmissionDummyDataUnpack(bs) {
|
|
1582
|
+
return { timeMultiple: bs.readF32() };
|
|
1583
|
+
}
|
|
1584
|
+
// ============================================================
|
|
1585
|
+
// CommanderIconData — 5 readString (NumImages=5)
|
|
1586
|
+
// ============================================================
|
|
1587
|
+
function commanderIconDataUnpack(bs) {
|
|
1588
|
+
return {
|
|
1589
|
+
baseImage: bs.readString(),
|
|
1590
|
+
activeImage: bs.readString(),
|
|
1591
|
+
inactiveImage: bs.readString(),
|
|
1592
|
+
selectImage: bs.readString(),
|
|
1593
|
+
hilightImage: bs.readString(),
|
|
1594
|
+
};
|
|
1595
|
+
}
|
|
1596
|
+
// ============================================================
|
|
1597
|
+
// PrecipitationData
|
|
1598
|
+
// ============================================================
|
|
1599
|
+
function precipitationDataUnpack(bs) {
|
|
1600
|
+
// Verified against decompiled binary FUN_006805d0
|
|
1601
|
+
const result = {};
|
|
1602
|
+
// DataBlockRef (soundProfile at offset 0x48)
|
|
1603
|
+
result.soundProfile = readDataBlockRef(bs);
|
|
1604
|
+
// S32 (offset 0x4c)
|
|
1605
|
+
result.numDrops = bs.readS32();
|
|
1606
|
+
// F32 (maxSize at offset 0x50 — before string)
|
|
1607
|
+
result.maxSize = bs.readF32();
|
|
1608
|
+
// readString (materialList at offset 0x5c)
|
|
1609
|
+
result.materialList = bs.readString();
|
|
1610
|
+
// 13×F32 (offsets 0x54, 0x58, 0x60, 0x64, 0x68, 0x6c, 0x70, 0x74, 0x78, 0x7c, 0x80, 0x84, 0x88)
|
|
1611
|
+
result.sizeX = bs.readF32();
|
|
1612
|
+
result.sizeY = bs.readF32();
|
|
1613
|
+
result.movingBoxPer = bs.readF32();
|
|
1614
|
+
result.divHeightVal = bs.readF32();
|
|
1615
|
+
result.sizeBigBox = bs.readF32();
|
|
1616
|
+
result.topBoxSpeed = bs.readF32();
|
|
1617
|
+
result.frontBoxSpeed = bs.readF32();
|
|
1618
|
+
result.topBoxDrawPer = bs.readF32();
|
|
1619
|
+
result.bottomDrawHeight = bs.readF32();
|
|
1620
|
+
result.skipIfPer = bs.readF32();
|
|
1621
|
+
result.bottomSpeedPer = bs.readF32();
|
|
1622
|
+
result.frontSpeedPer = bs.readF32();
|
|
1623
|
+
result.frontRadiusPer = bs.readF32();
|
|
1624
|
+
return result;
|
|
1625
|
+
}
|
|
1626
|
+
// ============================================================
|
|
1627
|
+
// FireballAtmosphereData
|
|
1628
|
+
// ============================================================
|
|
1629
|
+
function fireballAtmosphereDataUnpack(bs) {
|
|
1630
|
+
return { emitter: readDataBlockRef(bs) };
|
|
1631
|
+
}
|
|
1632
|
+
// ============================================================
|
|
1633
|
+
// LightningData
|
|
1634
|
+
// ============================================================
|
|
1635
|
+
function lightningDataUnpack(bs) {
|
|
1636
|
+
const result = {};
|
|
1637
|
+
// 8 readDataBlockRef (strikeSound profiles)
|
|
1638
|
+
const strikeSounds = [];
|
|
1639
|
+
for (let i = 0; i < 8; i++) {
|
|
1640
|
+
strikeSounds.push(readDataBlockRef(bs));
|
|
1641
|
+
}
|
|
1642
|
+
result.strikeSounds = strikeSounds;
|
|
1643
|
+
// 8 readString (strikeTextures)
|
|
1644
|
+
const strikeTextures = [];
|
|
1645
|
+
for (let i = 0; i < 8; i++) {
|
|
1646
|
+
strikeTextures.push(bs.readString());
|
|
1647
|
+
}
|
|
1648
|
+
result.strikeTextures = strikeTextures;
|
|
1649
|
+
// readDataBlockRef (thunder sound)
|
|
1650
|
+
result.thunderSound = readDataBlockRef(bs);
|
|
1651
|
+
return result;
|
|
1652
|
+
}
|
|
1653
|
+
// ============================================================
|
|
1654
|
+
// StationFXVehicleData
|
|
1655
|
+
// ============================================================
|
|
1656
|
+
function stationFXVehicleDataUnpack(bs) {
|
|
1657
|
+
// Field order confirmed from decompiled binary FUN_0069d570
|
|
1658
|
+
const result = {};
|
|
1659
|
+
// 11 F32 fields (offsets 0x44-0x6c)
|
|
1660
|
+
result.glowTopHeight = bs.readF32(); // 0x44
|
|
1661
|
+
result.glowBottomHeight = bs.readF32(); // 0x48
|
|
1662
|
+
result.glowTopRadius = bs.readF32(); // 0x4c
|
|
1663
|
+
result.glowBottomRadius = bs.readF32(); // 0x50
|
|
1664
|
+
result.numGlowSegments = bs.readF32(); // 0x54
|
|
1665
|
+
result.glowFadeTime = bs.readF32(); // 0x58
|
|
1666
|
+
result.armLightDelay = bs.readF32(); // 0x5c
|
|
1667
|
+
result.armLightLifetime = bs.readF32(); // 0x60
|
|
1668
|
+
result.armLightFadeTime = bs.readF32(); // 0x64
|
|
1669
|
+
result.lifetime = bs.readF32(); // 0x68
|
|
1670
|
+
result.numArcSegments = bs.readF32(); // 0x6c
|
|
1671
|
+
// ColorF via FUN_0043f040 (4×U8 = 32 bits at offset 0x70)
|
|
1672
|
+
result.sphereColor = readColorF(bs);
|
|
1673
|
+
// 6 F32 fields (offsets 0x80-0x94)
|
|
1674
|
+
result.spherePhiSegments = bs.readF32(); // 0x80
|
|
1675
|
+
result.sphereThetaSegments = bs.readF32(); // 0x84
|
|
1676
|
+
result.sphereRadius = bs.readF32(); // 0x88
|
|
1677
|
+
result.scale = {
|
|
1678
|
+
x: bs.readF32(), // 0x8c
|
|
1679
|
+
y: bs.readF32(), // 0x90
|
|
1680
|
+
z: bs.readF32(), // 0x94
|
|
1681
|
+
};
|
|
1682
|
+
// 11 strings
|
|
1683
|
+
result.glowTexture = bs.readString(); // 0x98
|
|
1684
|
+
// 4×(2 readString) — pad textures
|
|
1685
|
+
result.padTexture00 = bs.readString();
|
|
1686
|
+
result.padTexture01 = bs.readString();
|
|
1687
|
+
result.padTexture10 = bs.readString();
|
|
1688
|
+
result.padTexture11 = bs.readString();
|
|
1689
|
+
result.padTexture20 = bs.readString();
|
|
1690
|
+
result.padTexture21 = bs.readString();
|
|
1691
|
+
result.padTexture30 = bs.readString();
|
|
1692
|
+
result.padTexture31 = bs.readString();
|
|
1693
|
+
// 2 readString
|
|
1694
|
+
result.lightStartColor = bs.readString();
|
|
1695
|
+
result.lightEndColor = bs.readString();
|
|
1696
|
+
return result;
|
|
1697
|
+
}
|
|
1698
|
+
// ============================================================
|
|
1699
|
+
// StationFXPersonalData
|
|
1700
|
+
// ============================================================
|
|
1701
|
+
function stationFXPersonalDataUnpack(bs) {
|
|
1702
|
+
const result = {};
|
|
1703
|
+
// ~10 F32
|
|
1704
|
+
result.glowTopRadius = bs.readF32();
|
|
1705
|
+
result.glowBottomRadius = bs.readF32();
|
|
1706
|
+
result.glowTopHeight = bs.readF32();
|
|
1707
|
+
result.glowBottomHeight = bs.readF32();
|
|
1708
|
+
result.numGlowSegments = bs.readF32();
|
|
1709
|
+
result.numGlowPanels = bs.readF32();
|
|
1710
|
+
result.topAlpha = bs.readF32();
|
|
1711
|
+
result.bottomAlpha = bs.readF32();
|
|
1712
|
+
result.glowSpeed = bs.readF32();
|
|
1713
|
+
result.scrollSpeed = bs.readF32();
|
|
1714
|
+
// 2 readString
|
|
1715
|
+
result.glowTexture = bs.readString();
|
|
1716
|
+
result.padTexture = bs.readString();
|
|
1717
|
+
// 2 readString
|
|
1718
|
+
result.lightStartColor = bs.readString();
|
|
1719
|
+
result.lightEndColor = bs.readString();
|
|
1720
|
+
return result;
|
|
1721
|
+
}
|
|
1722
|
+
// ============================================================
|
|
1723
|
+
// CannedChatItem (game-specific, Tribes 2 only)
|
|
1724
|
+
// From Tribes 2 script reference: single readString (chatText)
|
|
1725
|
+
// ============================================================
|
|
1726
|
+
function cannedChatItemUnpack(bs) {
|
|
1727
|
+
return { chatText: bs.readString() };
|
|
1728
|
+
}
|
|
1729
|
+
// ============================================================
|
|
1730
|
+
// MissionMarkerData (extends ShapeBaseData — no additional fields)
|
|
1731
|
+
// ============================================================
|
|
1732
|
+
function missionMarkerDataUnpack(bs) {
|
|
1733
|
+
return shapeBaseDataUnpack(bs);
|
|
1734
|
+
}
|
|
1735
|
+
// ============================================================
|
|
1736
|
+
// GameBaseData (extends SimDataBlock — 0 bits on wire)
|
|
1737
|
+
// ============================================================
|
|
1738
|
+
function gameBaseDataUnpack(_bs) {
|
|
1739
|
+
return {};
|
|
1740
|
+
}
|
|
1741
|
+
// ============================================================
|
|
1742
|
+
// SimDataBlock — 0 bits on wire (base class)
|
|
1743
|
+
// ============================================================
|
|
1744
|
+
function simDataBlockUnpack(_bs) {
|
|
1745
|
+
return {};
|
|
1746
|
+
}
|
|
1747
|
+
// ============================================================
|
|
1748
|
+
// TSShapeConstructor (extends SimDataBlock)
|
|
1749
|
+
// readString(shape) + readInt(7) count + count × readString
|
|
1750
|
+
// NumSequenceBits = 7
|
|
1751
|
+
// ============================================================
|
|
1752
|
+
function tsShapeConstructorUnpack(bs) {
|
|
1753
|
+
const result = {};
|
|
1754
|
+
result.shape = bs.readString();
|
|
1755
|
+
const count = bs.readInt(7);
|
|
1756
|
+
const sequences = [];
|
|
1757
|
+
for (let i = 0; i < count && i < 128; i++) {
|
|
1758
|
+
sequences.push(bs.readString());
|
|
1759
|
+
}
|
|
1760
|
+
result.sequences = sequences;
|
|
1761
|
+
return result;
|
|
1762
|
+
}
|
|
1763
|
+
// ============================================================
|
|
1764
|
+
// EffectProfile (classId 139)
|
|
1765
|
+
// From decompiled binary FUN_0050dde0.
|
|
1766
|
+
// Parent: SimDataBlock (no-op)
|
|
1767
|
+
// ============================================================
|
|
1768
|
+
function effectProfileUnpack(bs) {
|
|
1769
|
+
const result = {};
|
|
1770
|
+
result.minDistance = bs.readF32(); // 0x40
|
|
1771
|
+
result.maxDistance = bs.readF32(); // 0x44
|
|
1772
|
+
result.audioScale = bs.readF32(); // 0x48
|
|
1773
|
+
result.directional = readBool(bs); // 0x4c, _read(1) = 8-bit bool
|
|
1774
|
+
result.effectName = bs.readString(); // 0x3c
|
|
1775
|
+
return result;
|
|
1776
|
+
}
|
|
1777
|
+
// ============================================================
|
|
1778
|
+
// JetEffectData (classId 150)
|
|
1779
|
+
// From decompiled binary FUN_0069fe60.
|
|
1780
|
+
// Parent: GameBaseData → SimDataBlock (no-op chain)
|
|
1781
|
+
// ============================================================
|
|
1782
|
+
function jetEffectDataUnpack(bs) {
|
|
1783
|
+
const result = {};
|
|
1784
|
+
result.coolColor = readColorF(bs); // 0x44, FUN_0043f040 = packed 4×U8
|
|
1785
|
+
result.hotColor = readColorF(bs); // 0x54, FUN_0043f040 = packed 4×U8
|
|
1786
|
+
result.activateTime = bs.readF32(); // 0x64
|
|
1787
|
+
result.deactivateTime = bs.readF32(); // 0x68
|
|
1788
|
+
result.length = bs.readF32(); // 0x6c
|
|
1789
|
+
result.width = bs.readF32(); // 0x70
|
|
1790
|
+
result.speed = bs.readF32(); // 0x74
|
|
1791
|
+
result.stretch = bs.readF32(); // 0x78
|
|
1792
|
+
result.yOffset = bs.readF32(); // 0x7c
|
|
1793
|
+
// texture[0]: loop of 1 iteration, flag + conditional string
|
|
1794
|
+
if (bs.readFlag()) {
|
|
1795
|
+
result.texture = bs.readString(); // 0x80
|
|
1796
|
+
}
|
|
1797
|
+
return result;
|
|
1798
|
+
}
|
|
1799
|
+
// ============================================================
|
|
1800
|
+
// RunningLightData (classId 162)
|
|
1801
|
+
// From decompiled binary FUN_006a0af0.
|
|
1802
|
+
// Parent: GameBaseData → SimDataBlock (no-op chain)
|
|
1803
|
+
// ============================================================
|
|
1804
|
+
function runningLightDataUnpack(bs) {
|
|
1805
|
+
const result = {};
|
|
1806
|
+
result.radius = bs.readF32(); // 0x48
|
|
1807
|
+
result.color = readColorF(bs); // 0x4c, FUN_0043f040 = packed 4×U8
|
|
1808
|
+
result.type = bs.readF32(); // 0x44, stored as F32 (bool/int on wire)
|
|
1809
|
+
result.length = bs.readF32(); // 0x78
|
|
1810
|
+
result.nodeName = bs.readString(); // 0x5c
|
|
1811
|
+
result.direction = {
|
|
1812
|
+
x: bs.readF32(),
|
|
1813
|
+
y: bs.readF32(),
|
|
1814
|
+
z: bs.readF32(),
|
|
1815
|
+
};
|
|
1816
|
+
result.offset = {
|
|
1817
|
+
x: bs.readF32(),
|
|
1818
|
+
y: bs.readF32(),
|
|
1819
|
+
z: bs.readF32(),
|
|
1820
|
+
};
|
|
1821
|
+
// texture[0]: loop of 1 iteration, flag + conditional string
|
|
1822
|
+
if (bs.readFlag()) {
|
|
1823
|
+
result.texture = bs.readString(); // 0x7c
|
|
1824
|
+
}
|
|
1825
|
+
return result;
|
|
1826
|
+
}
|
|
1827
|
+
// ============================================================
|
|
1828
|
+
// Registration
|
|
1829
|
+
// ============================================================
|
|
1830
|
+
export function registerDataBlockParsers(registry) {
|
|
1831
|
+
// Base types
|
|
1832
|
+
registry.catalogDataBlock({
|
|
1833
|
+
name: "ShapeBaseData",
|
|
1834
|
+
unpackData: shapeBaseDataUnpack,
|
|
1835
|
+
});
|
|
1836
|
+
registry.catalogDataBlock({
|
|
1837
|
+
name: "ShapeBaseImageData",
|
|
1838
|
+
unpackData: shapeBaseImageDataUnpack,
|
|
1839
|
+
});
|
|
1840
|
+
// Player/Vehicle types
|
|
1841
|
+
registry.catalogDataBlock({ name: "PlayerData", unpackData: playerDataUnpack });
|
|
1842
|
+
registry.catalogDataBlock({
|
|
1843
|
+
name: "VehicleData",
|
|
1844
|
+
unpackData: vehicleDataUnpack,
|
|
1845
|
+
});
|
|
1846
|
+
registry.catalogDataBlock({
|
|
1847
|
+
name: "FlyingVehicleData",
|
|
1848
|
+
unpackData: flyingVehicleDataUnpack,
|
|
1849
|
+
});
|
|
1850
|
+
registry.catalogDataBlock({
|
|
1851
|
+
name: "HoverVehicleData",
|
|
1852
|
+
unpackData: hoverVehicleDataUnpack,
|
|
1853
|
+
});
|
|
1854
|
+
registry.catalogDataBlock({
|
|
1855
|
+
name: "WheeledVehicleData",
|
|
1856
|
+
unpackData: wheeledVehicleDataUnpack,
|
|
1857
|
+
});
|
|
1858
|
+
// Static/Turret types
|
|
1859
|
+
registry.catalogDataBlock({
|
|
1860
|
+
name: "StaticShapeData",
|
|
1861
|
+
unpackData: staticShapeDataUnpack,
|
|
1862
|
+
});
|
|
1863
|
+
registry.catalogDataBlock({ name: "TurretData", unpackData: turretDataUnpack });
|
|
1864
|
+
registry.catalogDataBlock({
|
|
1865
|
+
name: "TurretImageData",
|
|
1866
|
+
unpackData: turretImageDataUnpack,
|
|
1867
|
+
});
|
|
1868
|
+
// Item
|
|
1869
|
+
registry.catalogDataBlock({ name: "ItemData", unpackData: itemDataUnpack });
|
|
1870
|
+
// Projectile types
|
|
1871
|
+
registry.catalogDataBlock({
|
|
1872
|
+
name: "ProjectileData",
|
|
1873
|
+
unpackData: projectileDataUnpack,
|
|
1874
|
+
});
|
|
1875
|
+
registry.catalogDataBlock({
|
|
1876
|
+
name: "LinearProjectileData",
|
|
1877
|
+
unpackData: linearProjectileDataUnpack,
|
|
1878
|
+
});
|
|
1879
|
+
registry.catalogDataBlock({
|
|
1880
|
+
name: "GrenadeProjectileData",
|
|
1881
|
+
unpackData: grenadeProjectileDataUnpack,
|
|
1882
|
+
});
|
|
1883
|
+
registry.catalogDataBlock({
|
|
1884
|
+
name: "SeekerProjectileData",
|
|
1885
|
+
unpackData: seekerProjectileDataUnpack,
|
|
1886
|
+
});
|
|
1887
|
+
registry.catalogDataBlock({
|
|
1888
|
+
name: "SniperProjectileData",
|
|
1889
|
+
unpackData: sniperProjectileDataUnpack,
|
|
1890
|
+
});
|
|
1891
|
+
registry.catalogDataBlock({
|
|
1892
|
+
name: "ShockLanceProjectileData",
|
|
1893
|
+
unpackData: shockLanceProjectileDataUnpack,
|
|
1894
|
+
});
|
|
1895
|
+
registry.catalogDataBlock({
|
|
1896
|
+
name: "ELFProjectileData",
|
|
1897
|
+
unpackData: elfProjectileDataUnpack,
|
|
1898
|
+
});
|
|
1899
|
+
registry.catalogDataBlock({
|
|
1900
|
+
name: "RepairProjectileData",
|
|
1901
|
+
unpackData: repairProjectileDataUnpack,
|
|
1902
|
+
});
|
|
1903
|
+
registry.catalogDataBlock({
|
|
1904
|
+
name: "TargetProjectileData",
|
|
1905
|
+
unpackData: targetProjectileDataUnpack,
|
|
1906
|
+
});
|
|
1907
|
+
registry.catalogDataBlock({
|
|
1908
|
+
name: "TracerProjectileData",
|
|
1909
|
+
unpackData: tracerProjectileDataUnpack,
|
|
1910
|
+
});
|
|
1911
|
+
registry.catalogDataBlock({
|
|
1912
|
+
name: "EnergyProjectileData",
|
|
1913
|
+
unpackData: energyProjectileDataUnpack,
|
|
1914
|
+
});
|
|
1915
|
+
registry.catalogDataBlock({
|
|
1916
|
+
name: "LinearFlareProjectileData",
|
|
1917
|
+
unpackData: linearFlareProjectileDataUnpack,
|
|
1918
|
+
});
|
|
1919
|
+
registry.catalogDataBlock({
|
|
1920
|
+
name: "BombProjectileData",
|
|
1921
|
+
unpackData: bombProjectileDataUnpack,
|
|
1922
|
+
});
|
|
1923
|
+
registry.catalogDataBlock({
|
|
1924
|
+
name: "FlareProjectileData",
|
|
1925
|
+
unpackData: flareProjectileDataUnpack,
|
|
1926
|
+
});
|
|
1927
|
+
// Effects
|
|
1928
|
+
registry.catalogDataBlock({
|
|
1929
|
+
name: "ExplosionData",
|
|
1930
|
+
unpackData: explosionDataUnpack,
|
|
1931
|
+
});
|
|
1932
|
+
registry.catalogDataBlock({ name: "DebrisData", unpackData: debrisDataUnpack });
|
|
1933
|
+
registry.catalogDataBlock({ name: "SplashData", unpackData: splashDataUnpack });
|
|
1934
|
+
registry.catalogDataBlock({
|
|
1935
|
+
name: "ShockwaveData",
|
|
1936
|
+
unpackData: shockwaveDataUnpack,
|
|
1937
|
+
});
|
|
1938
|
+
registry.catalogDataBlock({
|
|
1939
|
+
name: "ParticleEmitterData",
|
|
1940
|
+
unpackData: particleEmitterDataUnpack,
|
|
1941
|
+
});
|
|
1942
|
+
registry.catalogDataBlock({
|
|
1943
|
+
name: "ParticleData",
|
|
1944
|
+
unpackData: particleDataUnpack,
|
|
1945
|
+
});
|
|
1946
|
+
// Audio
|
|
1947
|
+
registry.catalogDataBlock({
|
|
1948
|
+
name: "AudioDescription",
|
|
1949
|
+
unpackData: audioDescriptionUnpack,
|
|
1950
|
+
});
|
|
1951
|
+
registry.catalogDataBlock({
|
|
1952
|
+
name: "AudioProfile",
|
|
1953
|
+
unpackData: audioProfileUnpack,
|
|
1954
|
+
});
|
|
1955
|
+
registry.catalogDataBlock({
|
|
1956
|
+
name: "AudioEnvironment",
|
|
1957
|
+
unpackData: audioEnvironmentUnpack,
|
|
1958
|
+
});
|
|
1959
|
+
registry.catalogDataBlock({
|
|
1960
|
+
name: "AudioSampleEnvironment",
|
|
1961
|
+
unpackData: audioSampleEnvironmentUnpack,
|
|
1962
|
+
});
|
|
1963
|
+
// Misc
|
|
1964
|
+
registry.catalogDataBlock({ name: "DecalData", unpackData: decalDataUnpack });
|
|
1965
|
+
registry.catalogDataBlock({
|
|
1966
|
+
name: "CameraData",
|
|
1967
|
+
unpackData: cameraDataUnpack,
|
|
1968
|
+
});
|
|
1969
|
+
registry.catalogDataBlock({ name: "SensorData", unpackData: sensorDataUnpack });
|
|
1970
|
+
registry.catalogDataBlock({
|
|
1971
|
+
name: "TriggerData",
|
|
1972
|
+
unpackData: triggerDataUnpack,
|
|
1973
|
+
});
|
|
1974
|
+
registry.catalogDataBlock({
|
|
1975
|
+
name: "ForceFieldBareData",
|
|
1976
|
+
unpackData: forceFieldBareDataUnpack,
|
|
1977
|
+
});
|
|
1978
|
+
registry.catalogDataBlock({
|
|
1979
|
+
name: "ParticleEmissionDummyData",
|
|
1980
|
+
unpackData: particleEmissionDummyDataUnpack,
|
|
1981
|
+
});
|
|
1982
|
+
registry.catalogDataBlock({
|
|
1983
|
+
name: "CommanderIconData",
|
|
1984
|
+
unpackData: commanderIconDataUnpack,
|
|
1985
|
+
});
|
|
1986
|
+
registry.catalogDataBlock({
|
|
1987
|
+
name: "PrecipitationData",
|
|
1988
|
+
unpackData: precipitationDataUnpack,
|
|
1989
|
+
});
|
|
1990
|
+
registry.catalogDataBlock({
|
|
1991
|
+
name: "FireballAtmosphereData",
|
|
1992
|
+
unpackData: fireballAtmosphereDataUnpack,
|
|
1993
|
+
});
|
|
1994
|
+
registry.catalogDataBlock({
|
|
1995
|
+
name: "LightningData",
|
|
1996
|
+
unpackData: lightningDataUnpack,
|
|
1997
|
+
});
|
|
1998
|
+
registry.catalogDataBlock({
|
|
1999
|
+
name: "StationFXVehicleData",
|
|
2000
|
+
unpackData: stationFXVehicleDataUnpack,
|
|
2001
|
+
});
|
|
2002
|
+
registry.catalogDataBlock({
|
|
2003
|
+
name: "StationFXPersonalData",
|
|
2004
|
+
unpackData: stationFXPersonalDataUnpack,
|
|
2005
|
+
});
|
|
2006
|
+
// Classes added from game-specific analysis
|
|
2007
|
+
registry.catalogDataBlock({
|
|
2008
|
+
name: "CannedChatItem",
|
|
2009
|
+
unpackData: cannedChatItemUnpack,
|
|
2010
|
+
});
|
|
2011
|
+
registry.catalogDataBlock({
|
|
2012
|
+
name: "MissionMarkerData",
|
|
2013
|
+
unpackData: missionMarkerDataUnpack,
|
|
2014
|
+
});
|
|
2015
|
+
registry.catalogDataBlock({
|
|
2016
|
+
name: "GameBaseData",
|
|
2017
|
+
unpackData: gameBaseDataUnpack,
|
|
2018
|
+
});
|
|
2019
|
+
registry.catalogDataBlock({
|
|
2020
|
+
name: "SimDataBlock",
|
|
2021
|
+
unpackData: simDataBlockUnpack,
|
|
2022
|
+
});
|
|
2023
|
+
registry.catalogDataBlock({
|
|
2024
|
+
name: "TSShapeConstructor",
|
|
2025
|
+
unpackData: tsShapeConstructorUnpack,
|
|
2026
|
+
});
|
|
2027
|
+
// Previously missing parsers (verified against decompiled binary)
|
|
2028
|
+
registry.catalogDataBlock({
|
|
2029
|
+
name: "EffectProfile",
|
|
2030
|
+
unpackData: effectProfileUnpack,
|
|
2031
|
+
});
|
|
2032
|
+
registry.catalogDataBlock({
|
|
2033
|
+
name: "JetEffectData",
|
|
2034
|
+
unpackData: jetEffectDataUnpack,
|
|
2035
|
+
});
|
|
2036
|
+
registry.catalogDataBlock({
|
|
2037
|
+
name: "RunningLightData",
|
|
2038
|
+
unpackData: runningLightDataUnpack,
|
|
2039
|
+
});
|
|
2040
|
+
}
|
|
2041
|
+
//# sourceMappingURL=DataBlockParsers.js.map
|