@carbonenginejs/runtime-resource 0.12.0 → 0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (170) hide show
  1. package/dist/CjsResMan.js +4 -4
  2. package/dist/CjsResMan.js.map +1 -1
  3. package/dist/_virtual/_rollupPluginBabelHelpers.js +1 -4
  4. package/dist/_virtual/_rollupPluginBabelHelpers.js.map +1 -1
  5. package/dist/format/CjsByteReader.js +310 -0
  6. package/dist/format/CjsByteReader.js.map +1 -0
  7. package/dist/format/CjsByteWriter.js +242 -0
  8. package/dist/format/CjsByteWriter.js.map +1 -0
  9. package/dist/format/CjsFormatError.js +41 -0
  10. package/dist/format/CjsFormatError.js.map +1 -0
  11. package/dist/format/CjsStringTable.js +268 -0
  12. package/dist/format/CjsStringTable.js.map +1 -0
  13. package/dist/format/carbonEffect/CjsCarbonEffectReader.js +361 -0
  14. package/dist/format/carbonEffect/CjsCarbonEffectReader.js.map +1 -0
  15. package/dist/format/carbonEffect/CjsCarbonEffectWriter.js +373 -0
  16. package/dist/format/carbonEffect/CjsCarbonEffectWriter.js.map +1 -0
  17. package/dist/format/carbonEffect/carbonDescriptionFromPortable.js +372 -0
  18. package/dist/format/carbonEffect/carbonDescriptionFromPortable.js.map +1 -0
  19. package/dist/format/carbonEffect/carbonEffectBackendBlock.js +427 -0
  20. package/dist/format/carbonEffect/carbonEffectBackendBlock.js.map +1 -0
  21. package/dist/format/carbonEffect/carbonEffectRecords.js +955 -0
  22. package/dist/format/carbonEffect/carbonEffectRecords.js.map +1 -0
  23. package/dist/format/compareUtf8.js +36 -0
  24. package/dist/format/compareUtf8.js.map +1 -0
  25. package/dist/format/index.js +11 -0
  26. package/dist/format/index.js.map +1 -0
  27. package/dist/formats/bnk/CjsBnkFormat.js +10 -4
  28. package/dist/formats/bnk/CjsBnkFormat.js.map +1 -1
  29. package/dist/formats/bnk/core/eventAction.js +305 -0
  30. package/dist/formats/bnk/core/eventAction.js.map +1 -0
  31. package/dist/formats/bnk/core/helpers.js +11 -2
  32. package/dist/formats/bnk/core/helpers.js.map +1 -1
  33. package/dist/formats/bnk/core/nodeBase.js +532 -0
  34. package/dist/formats/bnk/core/nodeBase.js.map +1 -0
  35. package/dist/formats/bnk/core/sfxNodes.js +252 -152
  36. package/dist/formats/bnk/core/sfxNodes.js.map +1 -1
  37. package/dist/formats/hlsl/core/HlslReader.js +7 -257
  38. package/dist/formats/hlsl/core/HlslReader.js.map +1 -1
  39. package/dist/formats/hlsl/core/carbonDescriptionToRuntime.js +781 -0
  40. package/dist/formats/hlsl/core/carbonDescriptionToRuntime.js.map +1 -0
  41. package/dist/formats/hlsl/core/tr2/resources/HlslEffectRes.js +21 -1
  42. package/dist/formats/hlsl/core/tr2/resources/HlslEffectRes.js.map +1 -1
  43. package/dist/formats/index.js +1 -1
  44. package/dist/formats/webgl/core/cewg/CewgPackage.js +2 -1
  45. package/dist/formats/webgl/core/cewg/CewgPackage.js.map +1 -1
  46. package/dist/formats/webgl/core/cewg/binary.js +11 -93
  47. package/dist/formats/webgl/core/cewg/binary.js.map +1 -1
  48. package/dist/formats/webgl/core/effectPackage.js +1 -1
  49. package/dist/formats/webgpu/CjsWebgpuFormat.js +4 -23
  50. package/dist/formats/webgpu/CjsWebgpuFormat.js.map +1 -1
  51. package/dist/formats/webgpu/core/buildCarbonEffectContainer.js +197 -0
  52. package/dist/formats/webgpu/core/buildCarbonEffectContainer.js.map +1 -0
  53. package/dist/formats/webgpu/core/cewgpu/CewgpuContainer.js +368 -0
  54. package/dist/formats/webgpu/core/cewgpu/CewgpuContainer.js.map +1 -0
  55. package/dist/formats/webgpu/core/cewgpu/containerViews.js +355 -0
  56. package/dist/formats/webgpu/core/cewgpu/containerViews.js.map +1 -0
  57. package/dist/formats/webgpu/core/cewgpu/validateContainer.js +90 -0
  58. package/dist/formats/webgpu/core/cewgpu/validateContainer.js.map +1 -0
  59. package/dist/formats/webgpu/core/effectBackendBodySet.js +21 -20
  60. package/dist/formats/webgpu/core/effectBackendBodySet.js.map +1 -1
  61. package/dist/formats/webgpu/core/helpers.js +87 -92
  62. package/dist/formats/webgpu/core/helpers.js.map +1 -1
  63. package/dist/formats/webgpu/core/ir/analyzeRegisterValues.js +14 -13
  64. package/dist/formats/webgpu/core/ir/analyzeRegisterValues.js.map +1 -1
  65. package/dist/formats/webgpu/core/ir/inferValueTypes.js +10 -8
  66. package/dist/formats/webgpu/core/ir/inferValueTypes.js.map +1 -1
  67. package/dist/formats/webgpu/core/packageEffect.js +94 -9
  68. package/dist/formats/webgpu/core/packageEffect.js.map +1 -1
  69. package/dist/formats/webgpu/core/packageMetadata.js +10 -1
  70. package/dist/formats/webgpu/core/packageMetadata.js.map +1 -1
  71. package/dist/formats/webgpu/core/wgsl/buildResourceTransformPlan.js +17 -15
  72. package/dist/formats/webgpu/core/wgsl/buildResourceTransformPlan.js.map +1 -1
  73. package/dist/formats/webgpu/core/wgsl/buildWgslBindingPlan.js +13 -12
  74. package/dist/formats/webgpu/core/wgsl/buildWgslBindingPlan.js.map +1 -1
  75. package/dist/formats/webgpu/core/wgsl/buildWgslSet.js +9 -8
  76. package/dist/formats/webgpu/core/wgsl/buildWgslSet.js.map +1 -1
  77. package/dist/formats/webgpu/core/wgsl/lowerComputeProgram.js +23 -22
  78. package/dist/formats/webgpu/core/wgsl/lowerComputeProgram.js.map +1 -1
  79. package/dist/formats/webgpu/core/wgsl/selectionPlans.js +46 -44
  80. package/dist/formats/webgpu/core/wgsl/selectionPlans.js.map +1 -1
  81. package/dist/resource/CjsResource.js +700 -684
  82. package/dist/resource/CjsResource.js.map +1 -1
  83. package/dist/resource/Tr2LightProfileRes.js +18 -27
  84. package/dist/resource/Tr2LightProfileRes.js.map +1 -1
  85. package/dist/resource/audio/CjsAudioBufferRes.js +2 -2
  86. package/dist/resource/audio/CjsAudioBufferRes.js.map +1 -1
  87. package/dist/resource/audio/CjsAudioRes.js +2 -2
  88. package/dist/resource/audio/CjsAudioRes.js.map +1 -1
  89. package/dist/resource/geometry/TriGeometryRes.js +605 -586
  90. package/dist/resource/geometry/TriGeometryRes.js.map +1 -1
  91. package/dist/resource/geometry/granny/Tr2GrannyStateRes.js +21 -30
  92. package/dist/resource/geometry/granny/Tr2GrannyStateRes.js.map +1 -1
  93. package/dist/resource/geometry/granny/TriGrannyRes.js +21 -30
  94. package/dist/resource/geometry/granny/TriGrannyRes.js.map +1 -1
  95. package/dist/resource/shader/Tr2EffectRes.js +9 -7
  96. package/dist/resource/shader/Tr2EffectRes.js.map +1 -1
  97. package/dist/resource/shader/Tr2MaterialArea.js +5 -3
  98. package/dist/resource/shader/Tr2MaterialArea.js.map +1 -1
  99. package/dist/resource/shader/Tr2MaterialMesh.js +4 -2
  100. package/dist/resource/shader/Tr2MaterialMesh.js.map +1 -1
  101. package/dist/resource/shader/Tr2MaterialRes.js +5 -3
  102. package/dist/resource/shader/Tr2MaterialRes.js.map +1 -1
  103. package/dist/resource/shader/Tr2Shader.js +26 -21
  104. package/dist/resource/shader/Tr2Shader.js.map +1 -1
  105. package/dist/resource/shader/Tr2ShaderPermutation.js +8 -6
  106. package/dist/resource/shader/Tr2ShaderPermutation.js.map +1 -1
  107. package/dist/resource/shader/reflection/Tr2EffectDefine.js +5 -3
  108. package/dist/resource/shader/reflection/Tr2EffectDefine.js.map +1 -1
  109. package/dist/resource/shader/reflection/Tr2EffectDescription.js +5 -3
  110. package/dist/resource/shader/reflection/Tr2EffectDescription.js.map +1 -1
  111. package/dist/resource/shader/reflection/Tr2EffectLibrary.js +17 -15
  112. package/dist/resource/shader/reflection/Tr2EffectLibrary.js.map +1 -1
  113. package/dist/resource/shader/reflection/Tr2EffectParameterAnnotation.js +11 -9
  114. package/dist/resource/shader/reflection/Tr2EffectParameterAnnotation.js.map +1 -1
  115. package/dist/resource/shader/reflection/Tr2EffectResource.js +8 -6
  116. package/dist/resource/shader/reflection/Tr2EffectResource.js.map +1 -1
  117. package/dist/resource/shader/reflection/Tr2EffectStageInput.js +15 -13
  118. package/dist/resource/shader/reflection/Tr2EffectStageInput.js.map +1 -1
  119. package/dist/resource/shader/reflection/Tr2EffectTechnique.js +7 -5
  120. package/dist/resource/shader/reflection/Tr2EffectTechnique.js.map +1 -1
  121. package/dist/resource/shader/reflection/Tr2Pass.js +11 -9
  122. package/dist/resource/shader/reflection/Tr2Pass.js.map +1 -1
  123. package/dist/resource/shader/sampler/Tr2SamplerSetup.js +8 -6
  124. package/dist/resource/shader/sampler/Tr2SamplerSetup.js.map +1 -1
  125. package/dist/resource/texture/CjsTextureArrayRes.js +400 -411
  126. package/dist/resource/texture/CjsTextureArrayRes.js.map +1 -1
  127. package/dist/resource/texture/Tr2ImageRes.js +99 -95
  128. package/dist/resource/texture/Tr2ImageRes.js.map +1 -1
  129. package/dist/resource/texture/TriTextureRes.js +312 -273
  130. package/dist/resource/texture/TriTextureRes.js.map +1 -1
  131. package/docs/README.md +13 -1
  132. package/docs/architecture.md +3 -3
  133. package/docs/concepts/resource-lifecycle.md +9 -2
  134. package/docs/concepts/shader-resource-model.md +114 -0
  135. package/docs/concepts/writing-an-engine-adapter.md +115 -0
  136. package/docs/formats/README.md +24 -1
  137. package/docs/formats/carbon-effect-container.md +452 -0
  138. package/docs/formats/dxbc/reference/classes/README.md +5 -72
  139. package/docs/formats/hlsl/reference/api.md +5 -4
  140. package/docs/formats/hlsl/reference/classes/README.md +6 -11
  141. package/docs/formats/provenance.md +23 -13
  142. package/docs/formats/webgl/reference/classes/README.md +5 -92
  143. package/docs/formats/webgpu/README.md +19 -16
  144. package/docs/formats/webgpu/architecture.md +15 -12
  145. package/docs/formats/webgpu/formats/cewgpu.md +175 -438
  146. package/docs/formats/webgpu/guides/effect-packaging.md +132 -140
  147. package/docs/formats/webgpu/reference/api.md +131 -131
  148. package/docs/formats/webgpu/reference/classes/README.md +5 -62
  149. package/docs/formats/webgpu/reference/wgsl-compatibility.md +146 -45
  150. package/docs/formats/wwise.md +34 -8
  151. package/docs/reference/classes/core.md +80 -0
  152. package/docs/reference/classes/formats.md +18 -28
  153. package/docs/reference/events.md +25 -0
  154. package/docs/reference/motherlode-cache.md +22 -8
  155. package/docs/reference/workers.md +5 -5
  156. package/docs/roadmap.md +61 -41
  157. package/package.json +2 -1
  158. package/dist/formats/webgpu/core/cewgpu/CewgpuPackage.js +0 -415
  159. package/dist/formats/webgpu/core/cewgpu/CewgpuPackage.js.map +0 -1
  160. package/dist/formats/webgpu/core/cewgpu/CewgpuPackageBuilder.js +0 -100
  161. package/dist/formats/webgpu/core/cewgpu/CewgpuPackageBuilder.js.map +0 -1
  162. package/dist/formats/webgpu/core/cewgpu/binary.js +0 -93
  163. package/dist/formats/webgpu/core/cewgpu/binary.js.map +0 -1
  164. package/dist/formats/webgpu/core/cewgpu/tags.js +0 -17
  165. package/dist/formats/webgpu/core/cewgpu/tags.js.map +0 -1
  166. package/dist/formats/webgpu/core/effectPackageValidation.js +0 -1078
  167. package/dist/formats/webgpu/core/effectPackageValidation.js.map +0 -1
  168. package/docs/formats/hlsl/reference/classes/carbon-compatibility.md +0 -66
  169. package/docs/formats/hlsl/reference/classes/public-api.md +0 -26
  170. package/docs/formats/hlsl/reference/classes/tr2-effect-model.md +0 -150
@@ -1,91 +1,22 @@
1
- import { readWwiseVar } from './helpers.js';
1
+ import { WWISE_NODE_BASE_VERSION, parseNodeBaseRange, WwiseCursor, readNodeBase, boundedCount, finite, readInitialRtpcs, readCurvePoints, readInitialRtpc } from './nodeBase.js';
2
2
 
3
3
  // Wwise v150 authored-SFX node decoding. Like musicNodes.js, this interprets
4
4
  // inspect() payload views outside the BNK read path and accepts a suffix only
5
- // when one anchored candidate consumes the payload exactly.
5
+ // when one anchored candidate consumes the payload exactly. Common NodeBase
6
+ // facts and non-playable hierarchy/attenuation objects stay separate from the
7
+ // playback-node map.
6
8
 
7
- const SUPPORTED_VERSION = 150;
9
+ const SUPPORTED_VERSION = WWISE_NODE_BASE_VERSION;
8
10
  const SFX_CHILD_TYPES = new Set([2, 5, 6, 7, 9]);
9
11
  const TYPE_NAMES = Object.freeze({
10
12
  2: "sound",
11
13
  5: "random-sequence-container",
12
14
  6: "switch-container",
13
- 9: "blend-container"
15
+ 7: "actor-mixer",
16
+ 9: "blend-container",
17
+ 14: "attenuation"
14
18
  });
15
19
 
16
- /**
17
- * Bounds-aware little-endian cursor used for exact-end Wwise SFX-tail
18
- * validation.
19
- */
20
- class SfxCursor {
21
- /** Creates a cursor over one HIRC payload at a candidate tail offset. */
22
- constructor(bytes, offset = 0) {
23
- this.bytes = bytes;
24
- this.view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
25
- this.at = offset;
26
- }
27
-
28
- /** Returns the number of unread payload bytes. */
29
- get remaining() {
30
- return this.bytes.byteLength - this.at;
31
- }
32
-
33
- /** Verifies that a primitive read remains inside the HIRC payload. */
34
- ensure(size) {
35
- if (this.at + size > this.bytes.byteLength) {
36
- throw new RangeError("truncated SFX node");
37
- }
38
- }
39
-
40
- /** Reads an unsigned 8-bit integer. */
41
- u8() {
42
- this.ensure(1);
43
- return this.view.getUint8(this.at++);
44
- }
45
-
46
- /** Reads an unsigned little-endian 16-bit integer. */
47
- u16() {
48
- this.ensure(2);
49
- const value = this.view.getUint16(this.at, true);
50
- this.at += 2;
51
- return value;
52
- }
53
-
54
- /** Reads an unsigned little-endian 32-bit integer. */
55
- u32() {
56
- this.ensure(4);
57
- const value = this.view.getUint32(this.at, true);
58
- this.at += 4;
59
- return value;
60
- }
61
-
62
- /** Reads a signed little-endian 32-bit integer. */
63
- s32() {
64
- this.ensure(4);
65
- const value = this.view.getInt32(this.at, true);
66
- this.at += 4;
67
- return value;
68
- }
69
-
70
- /** Reads a little-endian 32-bit float. */
71
- f32() {
72
- this.ensure(4);
73
- const value = this.view.getFloat32(this.at, true);
74
- this.at += 4;
75
- return value;
76
- }
77
-
78
- /** Reads one MSB-first Wwise variable-length unsigned integer. */
79
- variable() {
80
- const result = readWwiseVar(this.bytes, this.at);
81
- if (!result) {
82
- throw new RangeError("invalid Wwise variable integer");
83
- }
84
- this.at = result.nextOffset;
85
- return result.value;
86
- }
87
- }
88
-
89
20
  /**
90
21
  * Decodes one v150 HIRC 5 Random/Sequence payload.
91
22
  *
@@ -122,6 +53,32 @@ function parseSfxLayer(payload, knownObjects, {
122
53
  return FindLayer(payload, NormalizeKnownObjects(knownObjects), bankVersion).node;
123
54
  }
124
55
 
56
+ /**
57
+ * Decodes one exact v150 HIRC 7 Actor-Mixer hierarchy object.
58
+ *
59
+ * Actor-Mixers carry inherited NodeBase facts and child identities, but are
60
+ * not themselves playable container behavior.
61
+ *
62
+ * @returns {object|null} Typed hierarchy object, or null when invalid.
63
+ */
64
+ function parseSfxActorMixer(payload, {
65
+ bankVersion = SUPPORTED_VERSION
66
+ } = {}) {
67
+ return FindActorMixer(payload, bankVersion).node;
68
+ }
69
+
70
+ /**
71
+ * Decodes one exact v150 HIRC 14 attenuation object without assigning curve
72
+ * slots semantic names.
73
+ *
74
+ * @returns {object|null} Typed attenuation object, or null when invalid.
75
+ */
76
+ function parseSfxAttenuation(payload, {
77
+ bankVersion = SUPPORTED_VERSION
78
+ } = {}) {
79
+ return FindAttenuation(payload, bankVersion).node;
80
+ }
81
+
125
82
  /**
126
83
  * Decodes typed SFX nodes, events, and actions across inspected v150 banks.
127
84
  *
@@ -148,9 +105,13 @@ function sfxNodesFromBanks(inspections) {
148
105
  }
149
106
  }
150
107
  const nodes = new Map();
108
+ const nodeBases = new Map();
109
+ const actorMixers = new Map();
110
+ const attenuations = new Map();
151
111
  const events = new Map();
152
112
  const actions = new Map();
153
113
  const failed = [];
114
+ const nodeBaseFailed = [];
154
115
  const ambiguous = [];
155
116
  const unsupportedVersions = [];
156
117
  const parsedByType = {};
@@ -182,6 +143,7 @@ function sfxNodesFromBanks(inspections) {
182
143
  bank,
183
144
  actionType: entry.actionType,
184
145
  targetId: entry.targetId,
146
+ action: entry.action ?? null,
185
147
  payload: entry.payload
186
148
  });
187
149
  continue;
@@ -210,8 +172,12 @@ function sfxNodesFromBanks(inspections) {
210
172
  result = FindRandomSequence(entry.payload, knownObjects, version);
211
173
  } else if (entry.type === 6) {
212
174
  result = FindSwitch(entry.payload, knownObjects, version);
175
+ } else if (entry.type === 7) {
176
+ result = FindActorMixer(entry.payload, version);
213
177
  } else if (entry.type === 9) {
214
178
  result = FindLayer(entry.payload, knownObjects, version);
179
+ } else if (entry.type === 14) {
180
+ result = FindAttenuation(entry.payload, version);
215
181
  } else {
216
182
  continue;
217
183
  }
@@ -231,35 +197,179 @@ function sfxNodesFromBanks(inspections) {
231
197
  }
232
198
  parsed++;
233
199
  parsedByType[result.node.type] = (parsedByType[result.node.type] ?? 0) + 1;
234
- nodes.set(entry.id, {
200
+ if (entry.type === 14) {
201
+ attenuations.set(entry.id, {
202
+ id: entry.id,
203
+ bank,
204
+ ...result.node
205
+ });
206
+ continue;
207
+ }
208
+ if (entry.type === 7) {
209
+ actorMixers.set(entry.id, {
210
+ id: entry.id,
211
+ bank,
212
+ ...result.node
213
+ });
214
+ nodeBases.set(entry.id, result.node.nodeBase);
215
+ continue;
216
+ }
217
+ const node = {
235
218
  id: entry.id,
236
219
  bank,
237
220
  ...result.node
238
- });
221
+ };
222
+ const nodeBaseResult = ReadEntryNodeBase(entry, result, version);
223
+ nodes.set(entry.id, node);
224
+ if (nodeBaseResult.nodeBase) {
225
+ nodeBases.set(entry.id, nodeBaseResult.nodeBase);
226
+ } else {
227
+ nodeBaseFailed.push({
228
+ bank,
229
+ version,
230
+ type: TYPE_NAMES[entry.type],
231
+ id: entry.id,
232
+ reason: nodeBaseResult.reason
233
+ });
234
+ }
239
235
  }
240
236
  }
241
237
  return {
242
238
  nodes,
239
+ nodeBases,
240
+ actorMixers,
241
+ attenuations,
243
242
  events,
244
243
  actions,
245
244
  diagnostics: {
246
245
  parsed,
247
246
  parsedByType,
248
247
  failed,
248
+ nodeBaseFailed,
249
249
  ambiguous,
250
250
  unsupportedVersions,
251
251
  duplicates
252
252
  }
253
253
  };
254
254
  }
255
+ function ReadEntryNodeBase(entry, result, bankVersion) {
256
+ if (entry.type === 2) {
257
+ const start = SoundNodeBaseOffset(entry);
258
+ if (start === null) {
259
+ return {
260
+ nodeBase: null,
261
+ reason: "sound NodeBase offset is unavailable"
262
+ };
263
+ }
264
+ const nodeBase = parseNodeBaseRange(entry.payload, start, entry.payload.byteLength, {
265
+ bankVersion
266
+ });
267
+ return {
268
+ nodeBase,
269
+ reason: nodeBase ? "" : "sound NodeBase did not consume its exact byte range"
270
+ };
271
+ }
272
+ if (result.anchor === undefined) {
273
+ return {
274
+ nodeBase: null,
275
+ reason: "container NodeBase anchor is unavailable"
276
+ };
277
+ }
278
+ const nodeBase = parseNodeBaseRange(entry.payload, 0, result.anchor, {
279
+ bankVersion
280
+ });
281
+ return {
282
+ nodeBase,
283
+ reason: nodeBase ? "" : "container NodeBase did not consume its exact byte range"
284
+ };
285
+ }
286
+ function SoundNodeBaseOffset(entry) {
287
+ const payload = entry?.payload;
288
+ if (!(payload instanceof Uint8Array) || payload.byteLength < 14) {
289
+ return null;
290
+ }
291
+ const pluginType = Number(entry.pluginType ?? entry.pluginId & 0x0f);
292
+ if (pluginType !== 2) {
293
+ return 14;
294
+ }
295
+ if (payload.byteLength < 18) {
296
+ return null;
297
+ }
298
+ const view = new DataView(payload.buffer, payload.byteOffset, payload.byteLength);
299
+ const parameterSize = view.getUint32(14, true);
300
+ const start = 18 + parameterSize;
301
+ return Number.isSafeInteger(start) && start <= payload.byteLength ? start : null;
302
+ }
303
+ function FindActorMixer(payload, bankVersion) {
304
+ return ParseExact(payload, bankVersion, cursor => {
305
+ const nodeBase = readNodeBase(cursor);
306
+ const count = boundedCount(cursor.u32(), cursor.remaining, 4, 8192);
307
+ const children = [];
308
+ for (let index = 0; index < count; index++) {
309
+ children.push(cursor.u32());
310
+ }
311
+ return {
312
+ type: "actor-mixer",
313
+ nodeBase,
314
+ children
315
+ };
316
+ });
317
+ }
318
+ function FindAttenuation(payload, bankVersion) {
319
+ return ParseExact(payload, bankVersion, cursor => {
320
+ const heightSpreadRaw = cursor.u8();
321
+ const coneFlags = cursor.u8();
322
+ let cone = null;
323
+ if (coneFlags & 0x01) {
324
+ cone = {
325
+ insideDegrees: finite(cursor.f32()),
326
+ outsideDegrees: finite(cursor.f32()),
327
+ outsideVolume: finite(cursor.f32()),
328
+ lowPass: finite(cursor.f32()),
329
+ highPass: finite(cursor.f32())
330
+ };
331
+ }
332
+ const curveToUse = [];
333
+ for (let index = 0; index < 19; index++) {
334
+ curveToUse.push(cursor.s8());
335
+ }
336
+ const curveCount = boundedCount(cursor.u8(), cursor.remaining, 3, 19);
337
+ const curves = [];
338
+ for (let index = 0; index < curveCount; index++) {
339
+ const scaling = cursor.u8();
340
+ const pointCount = boundedCount(cursor.u16(), cursor.remaining, 12, 65535);
341
+ const rtpc = ReadInitialRTPCFromCurve(cursor, scaling, pointCount);
342
+ curves.push(rtpc);
343
+ }
344
+ return {
345
+ type: "attenuation",
346
+ heightSpreadRaw,
347
+ heightSpread: Boolean(heightSpreadRaw),
348
+ coneFlags,
349
+ cone,
350
+ curveToUse,
351
+ curves,
352
+ rtpcs: readInitialRtpcs(cursor)
353
+ };
354
+ });
355
+ }
356
+ function ReadInitialRTPCFromCurve(cursor, scaling, pointCount) {
357
+ if (![0, 2, 3, 4].includes(scaling)) {
358
+ throw new RangeError("invalid attenuation curve scaling");
359
+ }
360
+ return {
361
+ scaling,
362
+ points: readCurvePoints(cursor, pointCount)
363
+ };
364
+ }
255
365
  function FindRandomSequence(payload, knownObjects, bankVersion) {
256
366
  return FindUnique(payload, bankVersion, cursor => {
257
367
  const loopCount = cursor.u16();
258
368
  const loopModMin = cursor.u16();
259
369
  const loopModMax = cursor.u16();
260
- const transitionTime = Finite(cursor.f32());
261
- const transitionTimeModMin = Finite(cursor.f32());
262
- const transitionTimeModMax = Finite(cursor.f32());
370
+ const transitionTime = finite(cursor.f32());
371
+ const transitionTimeModMin = finite(cursor.f32());
372
+ const transitionTimeModMax = finite(cursor.f32());
263
373
  const avoidRepeatCount = cursor.u16();
264
374
  const transitionMode = cursor.u8();
265
375
  const randomMode = cursor.u8();
@@ -269,7 +379,7 @@ function FindRandomSequence(payload, knownObjects, bankVersion) {
269
379
  throw new RangeError("invalid Random/Sequence enum");
270
380
  }
271
381
  const children = ReadChildren(cursor, knownObjects);
272
- const playlistCount = BoundedCount(cursor.u16(), cursor.remaining, 8, 8192);
382
+ const playlistCount = boundedCount(cursor.u16(), cursor.remaining, 8, 8192);
273
383
  const childSet = new Set(children);
274
384
  const playlist = [];
275
385
  for (let index = 0; index < playlistCount; index++) {
@@ -314,18 +424,14 @@ function FindSwitch(payload, knownObjects, bankVersion) {
314
424
  throw new RangeError("invalid Switch enum");
315
425
  }
316
426
  const children = ReadChildren(cursor, knownObjects);
317
- const childSet = new Set(children);
318
- const assignmentCount = BoundedCount(cursor.u32(), cursor.remaining, 8, 8192);
427
+ const assignmentCount = boundedCount(cursor.u32(), cursor.remaining, 8, 8192);
319
428
  const assignments = [];
320
429
  for (let index = 0; index < assignmentCount; index++) {
321
430
  const valueId = cursor.u32();
322
- const itemCount = BoundedCount(cursor.u32(), cursor.remaining, 4, 8192);
431
+ const itemCount = boundedCount(cursor.u32(), cursor.remaining, 4, 8192);
323
432
  const childIds = [];
324
433
  for (let item = 0; item < itemCount; item++) {
325
434
  const childId = cursor.u32();
326
- if (!childSet.has(childId)) {
327
- throw new RangeError("invalid Switch assignment child");
328
- }
329
435
  childIds.push(childId);
330
436
  }
331
437
  assignments.push({
@@ -333,14 +439,14 @@ function FindSwitch(payload, knownObjects, bankVersion) {
333
439
  childIds
334
440
  });
335
441
  }
336
- const parameterCount = BoundedCount(cursor.u32(), cursor.remaining, 14, 8192);
442
+ const parameterCount = boundedCount(cursor.u32(), cursor.remaining, 14, 8192);
337
443
  const parameters = [];
338
444
  for (let index = 0; index < parameterCount; index++) {
339
445
  const childId = cursor.u32();
340
446
  const flags1 = cursor.u8();
341
447
  const flags2 = cursor.u8();
342
448
  const onSwitchMode = flags2 & 0x07;
343
- if (!childSet.has(childId) || flags1 & ~0x03 || flags2 & ~0x07 || onSwitchMode > 1) {
449
+ if (flags1 & ~0x03 || flags2 & ~0x07 || onSwitchMode > 1) {
344
450
  throw new RangeError("invalid Switch parameter");
345
451
  }
346
452
  parameters.push({
@@ -352,6 +458,10 @@ function FindSwitch(payload, knownObjects, bankVersion) {
352
458
  fadeInMs: cursor.s32()
353
459
  });
354
460
  }
461
+
462
+ // Essential and partial banks may retain raw SwitchList and parameter
463
+ // references to nodes that are not serialized in direct Children.
464
+ // Consumers decide whether those referenced nodes are available.
355
465
  return {
356
466
  type: "switch",
357
467
  groupType,
@@ -368,31 +478,31 @@ function FindLayer(payload, knownObjects, bankVersion) {
368
478
  return FindUnique(payload, bankVersion, cursor => {
369
479
  const children = ReadChildren(cursor, knownObjects);
370
480
  const childSet = new Set(children);
371
- const layerCount = BoundedCount(cursor.u32(), cursor.remaining, 15, 1024);
481
+ const layerCount = boundedCount(cursor.u32(), cursor.remaining, 15, 1024);
372
482
  const layers = [];
373
483
  for (let index = 0; index < layerCount; index++) {
374
484
  const layerId = cursor.u32();
375
- const initialRtpcCount = BoundedCount(cursor.u16(), cursor.remaining, 14, 4096);
485
+ const initialRtpcCount = boundedCount(cursor.u16(), cursor.remaining, 14, 4096);
376
486
  const initialRtpcs = [];
377
487
  for (let rtpc = 0; rtpc < initialRtpcCount; rtpc++) {
378
- initialRtpcs.push(ReadInitialRTPC(cursor));
488
+ initialRtpcs.push(readInitialRtpc(cursor));
379
489
  }
380
490
  const controlId = cursor.u32();
381
491
  const controlType = cursor.u8();
382
- const associationCount = BoundedCount(cursor.u32(), cursor.remaining, 8, 8192);
492
+ const associationCount = boundedCount(cursor.u32(), cursor.remaining, 8, 8192);
383
493
  const associations = [];
384
494
  if (controlType > 4) {
385
495
  throw new RangeError("invalid Layer control type");
386
496
  }
387
497
  for (let association = 0; association < associationCount; association++) {
388
498
  const childId = cursor.u32();
389
- const pointCount = BoundedCount(cursor.u32(), cursor.remaining, 12, 65535);
499
+ const pointCount = boundedCount(cursor.u32(), cursor.remaining, 12, 65535);
390
500
  if (!childSet.has(childId)) {
391
501
  throw new RangeError("invalid Layer association child");
392
502
  }
393
503
  associations.push({
394
504
  childId,
395
- points: ReadPoints(cursor, pointCount)
505
+ points: readCurvePoints(cursor, pointCount)
396
506
  });
397
507
  }
398
508
  layers.push({
@@ -415,46 +525,8 @@ function FindLayer(payload, knownObjects, bankVersion) {
415
525
  };
416
526
  });
417
527
  }
418
- function ReadInitialRTPC(cursor) {
419
- const controlId = cursor.u32();
420
- const controlType = cursor.u8();
421
- const accumulation = cursor.u8();
422
- const parameterId = cursor.variable();
423
- const curveId = cursor.u32();
424
- const scaling = cursor.u8();
425
- const pointCount = BoundedCount(cursor.u16(), cursor.remaining, 12, 65535);
426
- if (controlType > 4 || accumulation > 6 || scaling > 5) {
427
- throw new RangeError("invalid initial RTPC enum");
428
- }
429
- return {
430
- controlId,
431
- controlType,
432
- accumulation,
433
- parameterId,
434
- curveId,
435
- scaling,
436
- points: ReadPoints(cursor, pointCount)
437
- };
438
- }
439
- function ReadPoints(cursor, count) {
440
- const points = [];
441
- for (let index = 0; index < count; index++) {
442
- const from = Finite(cursor.f32());
443
- const to = Finite(cursor.f32());
444
- const interpolation = cursor.u32();
445
- if (interpolation > 9) {
446
- throw new RangeError("invalid curve interpolation");
447
- }
448
- points.push({
449
- from,
450
- to,
451
- interpolation
452
- });
453
- }
454
- return points;
455
- }
456
528
  function ReadChildren(cursor, knownObjects) {
457
- const count = BoundedCount(cursor.u32(), cursor.remaining, 4, 8192);
529
+ const count = boundedCount(cursor.u32(), cursor.remaining, 4, 8192);
458
530
  const children = [];
459
531
  for (let index = 0; index < count; index++) {
460
532
  const id = cursor.u32();
@@ -476,10 +548,13 @@ function FindUnique(payload, bankVersion, parse) {
476
548
  const candidates = [];
477
549
  for (let anchor = 0; anchor < payload.byteLength; anchor++) {
478
550
  try {
479
- const cursor = new SfxCursor(payload, anchor);
551
+ const cursor = new WwiseCursor(payload, anchor);
480
552
  const node = parse(cursor);
481
553
  if (cursor.at === payload.byteLength) {
482
- candidates.push(node);
554
+ candidates.push({
555
+ node,
556
+ anchor
557
+ });
483
558
  }
484
559
  } catch {
485
560
  // This byte was not the exact type-tail anchor.
@@ -487,15 +562,52 @@ function FindUnique(payload, bankVersion, parse) {
487
562
  }
488
563
  if (candidates.length === 1) {
489
564
  return {
490
- node: candidates[0],
565
+ node: candidates[0].node,
566
+ anchor: candidates[0].anchor,
491
567
  reason: ""
492
568
  };
493
569
  }
570
+ if (candidates.length > 1) {
571
+ const exactNodeBase = candidates.filter(candidate => parseNodeBaseRange(payload, 0, candidate.anchor, {
572
+ bankVersion
573
+ }));
574
+ if (exactNodeBase.length === 1) {
575
+ return {
576
+ node: exactNodeBase[0].node,
577
+ anchor: exactNodeBase[0].anchor,
578
+ reason: ""
579
+ };
580
+ }
581
+ }
494
582
  return {
495
583
  node: null,
496
584
  reason: candidates.length ? "ambiguous anchors" : "no exact anchor"
497
585
  };
498
586
  }
587
+ function ParseExact(payload, bankVersion, parse) {
588
+ if (!(payload instanceof Uint8Array) || Number(bankVersion) !== SUPPORTED_VERSION) {
589
+ return {
590
+ node: null,
591
+ reason: "unsupported bank version"
592
+ };
593
+ }
594
+ try {
595
+ const cursor = new WwiseCursor(payload);
596
+ const node = parse(cursor);
597
+ return cursor.at === payload.byteLength ? {
598
+ node,
599
+ reason: ""
600
+ } : {
601
+ node: null,
602
+ reason: "trailing bytes"
603
+ };
604
+ } catch (cause) {
605
+ return {
606
+ node: null,
607
+ reason: cause instanceof Error ? cause.message : "invalid object"
608
+ };
609
+ }
610
+ }
499
611
  function NormalizeKnownObjects(value) {
500
612
  if (value instanceof Map) {
501
613
  return value;
@@ -515,18 +627,6 @@ function NormalizeKnownObjects(value) {
515
627
  function KnownType(value) {
516
628
  return typeof value === "number" ? value : value?.type;
517
629
  }
518
- function BoundedCount(value, remaining, stride, maximum) {
519
- if (!Number.isSafeInteger(value) || value < 0 || value > maximum || value * stride > remaining) {
520
- throw new RangeError("invalid SFX node count");
521
- }
522
- return value;
523
- }
524
- function Finite(value) {
525
- if (!Number.isFinite(value)) {
526
- throw new RangeError("non-finite SFX node value");
527
- }
528
- return value;
529
- }
530
630
 
531
- export { parseSfxLayer, parseSfxRandomSequence, parseSfxSwitch, sfxNodesFromBanks };
631
+ export { parseSfxActorMixer, parseSfxAttenuation, parseSfxLayer, parseSfxRandomSequence, parseSfxSwitch, sfxNodesFromBanks };
532
632
  //# sourceMappingURL=sfxNodes.js.map