@swf2es/format 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/LICENSE +202 -0
  2. package/LICENSE.third-party +35 -0
  3. package/README.md +22 -2
  4. package/dist/abc-names.d.ts +3 -0
  5. package/dist/abc-names.d.ts.map +1 -0
  6. package/dist/abc-names.js +127 -0
  7. package/dist/abc-names.js.map +1 -0
  8. package/dist/actions.d.ts +106 -0
  9. package/dist/actions.d.ts.map +1 -0
  10. package/dist/actions.js +223 -0
  11. package/dist/actions.js.map +1 -0
  12. package/dist/air-descriptor.d.ts +11 -0
  13. package/dist/air-descriptor.d.ts.map +1 -0
  14. package/dist/air-descriptor.js +64 -0
  15. package/dist/air-descriptor.js.map +1 -0
  16. package/dist/api-versions.d.ts +12 -0
  17. package/dist/api-versions.d.ts.map +1 -0
  18. package/dist/api-versions.js +23 -0
  19. package/dist/api-versions.js.map +1 -0
  20. package/dist/bitmap.d.ts +34 -0
  21. package/dist/bitmap.d.ts.map +1 -0
  22. package/dist/bitmap.js +200 -0
  23. package/dist/bitmap.js.map +1 -0
  24. package/dist/button.d.ts +39 -0
  25. package/dist/button.d.ts.map +1 -0
  26. package/dist/button.js +61 -0
  27. package/dist/button.js.map +1 -0
  28. package/dist/compression.d.ts +47 -0
  29. package/dist/compression.d.ts.map +1 -0
  30. package/dist/compression.js +201 -0
  31. package/dist/compression.js.map +1 -0
  32. package/dist/display.d.ts +106 -0
  33. package/dist/display.d.ts.map +1 -0
  34. package/dist/display.js +306 -0
  35. package/dist/display.js.map +1 -0
  36. package/dist/filters.d.ts +66 -0
  37. package/dist/filters.d.ts.map +1 -0
  38. package/dist/filters.js +137 -0
  39. package/dist/filters.js.map +1 -0
  40. package/dist/font.d.ts +60 -0
  41. package/dist/font.d.ts.map +1 -0
  42. package/dist/font.js +169 -0
  43. package/dist/font.js.map +1 -0
  44. package/dist/index.d.ts +27 -0
  45. package/dist/index.d.ts.map +1 -0
  46. package/dist/index.js +27 -0
  47. package/dist/index.js.map +1 -0
  48. package/dist/lzma.d.ts +33 -0
  49. package/dist/lzma.d.ts.map +1 -0
  50. package/dist/lzma.js +279 -0
  51. package/dist/lzma.js.map +1 -0
  52. package/dist/pbj.d.ts +178 -0
  53. package/dist/pbj.d.ts.map +1 -0
  54. package/dist/pbj.js +485 -0
  55. package/dist/pbj.js.map +1 -0
  56. package/dist/shape.d.ts +152 -0
  57. package/dist/shape.d.ts.map +1 -0
  58. package/dist/shape.js +412 -0
  59. package/dist/shape.js.map +1 -0
  60. package/dist/sound.d.ts +71 -0
  61. package/dist/sound.d.ts.map +1 -0
  62. package/dist/sound.js +92 -0
  63. package/dist/sound.js.map +1 -0
  64. package/dist/swf.d.ts +77 -0
  65. package/dist/swf.d.ts.map +1 -0
  66. package/dist/swf.js +164 -0
  67. package/dist/swf.js.map +1 -0
  68. package/dist/tags.d.ts +57 -0
  69. package/dist/tags.d.ts.map +1 -0
  70. package/dist/tags.js +59 -0
  71. package/dist/tags.js.map +1 -0
  72. package/dist/text.d.ts +75 -0
  73. package/dist/text.d.ts.map +1 -0
  74. package/dist/text.js +136 -0
  75. package/dist/text.js.map +1 -0
  76. package/dist/zip.d.ts +17 -0
  77. package/dist/zip.d.ts.map +1 -0
  78. package/dist/zip.js +92 -0
  79. package/dist/zip.js.map +1 -0
  80. package/package.json +33 -3
  81. package/src/abc-names.ts +138 -0
  82. package/src/actions.ts +319 -0
  83. package/src/air-descriptor.ts +84 -0
  84. package/src/api-versions.ts +27 -0
  85. package/src/bitmap.ts +259 -0
  86. package/src/button.ts +99 -0
  87. package/src/compression.ts +243 -0
  88. package/src/display.ts +419 -0
  89. package/src/filters.ts +206 -0
  90. package/src/font.ts +238 -0
  91. package/src/index.ts +151 -0
  92. package/src/pbj.ts +651 -0
  93. package/src/shape.ts +618 -0
  94. package/src/sound.ts +173 -0
  95. package/src/swf.ts +234 -0
  96. package/src/tags.ts +58 -0
  97. package/src/text.ts +213 -0
  98. package/src/zip.ts +135 -0
package/src/pbj.ts ADDED
@@ -0,0 +1,651 @@
1
+ // Pixel Bender bytecode (PBJ), as flash.display.Shader takes it: the
2
+ // kernel's metadata, parameters and image inputs, then its operations. The
3
+ // layout is Ruffle's (https://github.com/ruffle-rs/ruffle)
4
+ // render/pixel_bender/src/parser.rs, itself after pbjas and Haxe's
5
+ // format.pbj: MIT OR Apache-2.0, Copyright (c) 2018-2022 Ruffle LLC and
6
+ // Ruffle contributors. What Flash refuses, and with which error, is
7
+ // adl's: bytes that end inside an opcode are EOFError 2030, and anything
8
+ // else it will not run, ArgumentError 2004, kernels Ruffle accepts among
9
+ // them (each operation's register kinds and sizes, PBJ_RULES).
10
+ import { utf8 } from "./display.js";
11
+
12
+ /** The parameter and metadata types, by their PBJ code, as ShaderParameterType names them. */
13
+ export const PBJ_TYPES = [
14
+ null,
15
+ "float",
16
+ "float2",
17
+ "float3",
18
+ "float4",
19
+ "matrix2x2",
20
+ "matrix3x3",
21
+ "matrix4x4",
22
+ "int",
23
+ "int2",
24
+ "int3",
25
+ "int4",
26
+ "string",
27
+ "bool",
28
+ "bool2",
29
+ "bool3",
30
+ "bool4",
31
+ ] as const;
32
+
33
+ export const PBJ_STRING = 0xc;
34
+
35
+ /** How many numbers a value of each type holds, by code; 0 for a string. */
36
+ export const PBJ_TYPE_SIZES = [0, 1, 2, 3, 4, 4, 9, 16, 1, 2, 3, 4, 0, 1, 2, 3, 4];
37
+
38
+ /** Whether a type's numbers are floats, by code. */
39
+ export function pbjFloatType(type: number): boolean {
40
+ return type >= 0x1 && type <= 0x7;
41
+ }
42
+
43
+ export const PbjOp = {
44
+ nop: 0x00,
45
+ add: 0x01,
46
+ sub: 0x02,
47
+ mul: 0x03,
48
+ rcp: 0x04,
49
+ div: 0x05,
50
+ atan2: 0x06,
51
+ pow: 0x07,
52
+ mod: 0x08,
53
+ min: 0x09,
54
+ max: 0x0a,
55
+ step: 0x0b,
56
+ sin: 0x0c,
57
+ cos: 0x0d,
58
+ tan: 0x0e,
59
+ asin: 0x0f,
60
+ acos: 0x10,
61
+ atan: 0x11,
62
+ exp: 0x12,
63
+ exp2: 0x13,
64
+ log: 0x14,
65
+ log2: 0x15,
66
+ sqrt: 0x16,
67
+ rsqrt: 0x17,
68
+ abs: 0x18,
69
+ sign: 0x19,
70
+ floor: 0x1a,
71
+ ceil: 0x1b,
72
+ fract: 0x1c,
73
+ mov: 0x1d,
74
+ floatToInt: 0x1e,
75
+ intToFloat: 0x1f,
76
+ matMatMul: 0x20,
77
+ vecMatMul: 0x21,
78
+ matVecMul: 0x22,
79
+ normalize: 0x23,
80
+ length: 0x24,
81
+ distance: 0x25,
82
+ dot: 0x26,
83
+ cross: 0x27,
84
+ equal: 0x28,
85
+ notEqual: 0x29,
86
+ lessThan: 0x2a,
87
+ lessThanEqual: 0x2b,
88
+ logicalNot: 0x2c,
89
+ logicalAnd: 0x2d,
90
+ logicalOr: 0x2e,
91
+ logicalXor: 0x2f,
92
+ sampleNearest: 0x30,
93
+ sampleLinear: 0x31,
94
+ load: 0x32,
95
+ select: 0x33,
96
+ if: 0x34,
97
+ else: 0x35,
98
+ endIf: 0x36,
99
+ floatToBool: 0x37,
100
+ boolToFloat: 0x38,
101
+ intToBool: 0x39,
102
+ boolToInt: 0x3a,
103
+ vectorEqual: 0x3b,
104
+ vectorNotEqual: 0x3c,
105
+ any: 0x3d,
106
+ all: 0x3e,
107
+ kernelMetadata: 0xa0,
108
+ parameter: 0xa1,
109
+ parameterMetadata: 0xa2,
110
+ texture: 0xa3,
111
+ name: 0xa4,
112
+ version: 0xa5,
113
+ } as const;
114
+
115
+ /** A register an operation names. */
116
+ export interface PbjRegister {
117
+ index: number;
118
+ int: boolean;
119
+ /**
120
+ * A destination's channels, 0 to 3 for r to a, those its mask writes in
121
+ * order; a source's swizzle, four channels, of which `size` are its own:
122
+ * an operation reads as many as it writes, the rest by the swizzle's
123
+ * remaining bits, as Flash does.
124
+ */
125
+ channels: number[];
126
+ /** A source's own channels; a destination's channel count. */
127
+ size: number;
128
+ /** 2, 3 or 4 for a matrix of that size, its columns from `index` on; 0 otherwise. */
129
+ matrix: number;
130
+ }
131
+
132
+ /** An operation of a destination and a source, or of matrices. */
133
+ export interface PbjCompute {
134
+ op: number;
135
+ dst: PbjRegister;
136
+ src: PbjRegister;
137
+ }
138
+
139
+ export interface PbjLoad {
140
+ op: typeof PbjOp.load;
141
+ dst: PbjRegister;
142
+ /** A float, or an int as the 16 bits Flash keeps. */
143
+ value: number;
144
+ }
145
+
146
+ export interface PbjSample {
147
+ op: typeof PbjOp.sampleNearest | typeof PbjOp.sampleLinear;
148
+ dst: PbjRegister;
149
+ src: PbjRegister;
150
+ texture: number;
151
+ }
152
+
153
+ export interface PbjIf {
154
+ op: typeof PbjOp.if;
155
+ src: PbjRegister;
156
+ }
157
+
158
+ export interface PbjSelect {
159
+ op: typeof PbjOp.select;
160
+ dst: PbjRegister;
161
+ condition: PbjRegister;
162
+ a: PbjRegister;
163
+ b: PbjRegister;
164
+ }
165
+
166
+ export type PbjOperation =
167
+ | PbjCompute
168
+ | PbjLoad
169
+ | PbjSample
170
+ | PbjIf
171
+ | { op: typeof PbjOp.else | typeof PbjOp.endIf }
172
+ | PbjSelect;
173
+
174
+ /** A metadata value: a string, or its numbers, ints as the unsigned 16 bits PBJ stores. */
175
+ export interface PbjMetadata {
176
+ key: string;
177
+ type: number;
178
+ value: string | number[];
179
+ }
180
+
181
+ export interface PbjParameter {
182
+ name: string;
183
+ /** The type's code, PBJ_TYPES; one Flash does not know is kept, as it keeps it. */
184
+ type: number;
185
+ output: boolean;
186
+ register: PbjRegister;
187
+ /** Its metadata, from the second metadata opcode alone: Flash drops the first's here. */
188
+ metadata: PbjMetadata[];
189
+ }
190
+
191
+ export interface PbjInput {
192
+ name: string;
193
+ /** The texture's number, which sampling names and ShaderInput.index gives. */
194
+ index: number;
195
+ channels: number;
196
+ }
197
+
198
+ export interface PbjKernel {
199
+ /** The kernel's name, or null where it has none: ShaderData then has no `name`. */
200
+ name: string | null;
201
+ /** Every first-opcode metadata, an int or a string, which Flash gives the kernel wherever it stands. */
202
+ metadata: PbjMetadata[];
203
+ parameters: PbjParameter[];
204
+ inputs: PbjInput[];
205
+ operations: PbjOperation[];
206
+ }
207
+
208
+ /** Why Flash refuses a kernel: EOFError 2030 for bytes that end too soon, ArgumentError 2004 for anything else. */
209
+ export class PbjError extends Error {
210
+ constructor(readonly errorID: 2004 | 2030) {
211
+ super(errorID === 2030 ? "End of file was encountered." : "One of the parameters is invalid.");
212
+ }
213
+ }
214
+
215
+ const F = 1;
216
+ const I = 2;
217
+
218
+ /**
219
+ * Which register kinds and sizes adl takes for each operation of one
220
+ * destination and one source, by opcode: `kinds` lists the destination's
221
+ * and the source's kinds as pairs, and `sizes` how the destination's
222
+ * channel count and the source's size may go together.
223
+ */
224
+ export interface PbjRule {
225
+ kinds: number[];
226
+ sizes: "same" | "any" | "scalar" | "vector" | "length" | "cross";
227
+ }
228
+
229
+ const FF = [F, F];
230
+ const FF_II = [F, F, I, I];
231
+ const SAME_FLOAT: PbjRule = { kinds: FF, sizes: "same" };
232
+ const SAME_EITHER: PbjRule = { kinds: FF_II, sizes: "same" };
233
+
234
+ export const PBJ_RULES = new Map<number, PbjRule>([
235
+ [PbjOp.add, SAME_EITHER],
236
+ [PbjOp.sub, SAME_EITHER],
237
+ [PbjOp.mul, SAME_EITHER],
238
+ [PbjOp.div, SAME_EITHER],
239
+ [PbjOp.mov, SAME_EITHER],
240
+ [PbjOp.equal, SAME_EITHER],
241
+ [PbjOp.notEqual, SAME_EITHER],
242
+ [PbjOp.lessThan, SAME_EITHER],
243
+ [PbjOp.lessThanEqual, SAME_EITHER],
244
+ [PbjOp.matMatMul, SAME_FLOAT],
245
+ [PbjOp.vecMatMul, { kinds: [F, F, F, I, I, F, I, I], sizes: "scalar" }],
246
+ [PbjOp.matVecMul, { kinds: [F, F, F, I, I, F, I, I], sizes: "scalar" }],
247
+ [PbjOp.normalize, { kinds: FF, sizes: "vector" }],
248
+ [PbjOp.length, { kinds: FF, sizes: "length" }],
249
+ [PbjOp.distance, { kinds: FF, sizes: "vector" }],
250
+ [PbjOp.cross, { kinds: FF, sizes: "cross" }],
251
+ [PbjOp.floatToInt, { kinds: [I, F], sizes: "same" }],
252
+ [PbjOp.intToFloat, { kinds: [F, I], sizes: "same" }],
253
+ [PbjOp.logicalNot, { kinds: [I, I], sizes: "scalar" }],
254
+ [PbjOp.logicalAnd, { kinds: [I, I], sizes: "scalar" }],
255
+ [PbjOp.logicalOr, { kinds: [I, I], sizes: "scalar" }],
256
+ [PbjOp.logicalXor, { kinds: [I, I], sizes: "scalar" }],
257
+ [PbjOp.floatToBool, { kinds: [I, F], sizes: "any" }],
258
+ [PbjOp.boolToFloat, { kinds: [F, I], sizes: "any" }],
259
+ [PbjOp.intToBool, { kinds: [I, I], sizes: "any" }],
260
+ [PbjOp.boolToInt, { kinds: [F, I], sizes: "any" }],
261
+ [PbjOp.vectorEqual, { kinds: FF_II, sizes: "any" }],
262
+ [PbjOp.vectorNotEqual, { kinds: FF_II, sizes: "any" }],
263
+ [PbjOp.any, { kinds: [I, I], sizes: "any" }],
264
+ [PbjOp.all, { kinds: [I, I], sizes: "any" }],
265
+ ]);
266
+ for (let op: number = PbjOp.rcp; op <= PbjOp.fract; op++) {
267
+ if (op !== PbjOp.div) {
268
+ PBJ_RULES.set(op, SAME_FLOAT);
269
+ }
270
+ }
271
+
272
+ PBJ_RULES.set(PbjOp.dot, SAME_FLOAT);
273
+
274
+ /** The operations adl takes on matrices, each column as its registers hold it. */
275
+ const MATRIX_OPS: readonly number[] = [
276
+ PbjOp.add,
277
+ PbjOp.sub,
278
+ PbjOp.mul,
279
+ PbjOp.mov,
280
+ PbjOp.matMatMul,
281
+ PbjOp.matVecMul,
282
+ PbjOp.vecMatMul,
283
+ ];
284
+
285
+ function sizesFit(rule: PbjRule, dst: number, src: number): boolean {
286
+ switch (rule.sizes) {
287
+ case "same":
288
+ return dst === src;
289
+ case "any":
290
+ return true;
291
+ case "scalar":
292
+ return dst === 1 && src === 1;
293
+ case "vector":
294
+ return dst === src && src >= 2;
295
+ case "length":
296
+ return dst === 1 && src >= 2;
297
+ case "cross":
298
+ return dst === 3 && src === 3;
299
+ }
300
+ }
301
+
302
+ function kindsFit(rule: PbjRule, dst: PbjRegister, src: PbjRegister): boolean {
303
+ const d = dst.int ? I : F;
304
+ const s = src.int ? I : F;
305
+ for (let i = 0; i < rule.kinds.length; i += 2) {
306
+ if (rule.kinds[i] === d && rule.kinds[i + 1] === s) {
307
+ return true;
308
+ }
309
+ }
310
+
311
+ return false;
312
+ }
313
+
314
+ class Reader {
315
+ private at = 0;
316
+ private readonly view: DataView;
317
+
318
+ constructor(private readonly bytes: Uint8Array) {
319
+ this.view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
320
+ }
321
+
322
+ get done(): boolean {
323
+ return this.at >= this.bytes.length;
324
+ }
325
+
326
+ private need(n: number): number {
327
+ const at = this.at;
328
+ if (at + n > this.bytes.length) {
329
+ throw new PbjError(2030);
330
+ }
331
+
332
+ this.at += n;
333
+ return at;
334
+ }
335
+
336
+ u8(): number {
337
+ return this.bytes[this.need(1)];
338
+ }
339
+
340
+ u16(): number {
341
+ return this.view.getUint16(this.need(2), true);
342
+ }
343
+
344
+ u24(): number {
345
+ const at = this.need(3);
346
+ return this.bytes[at] | (this.bytes[at + 1] << 8) | (this.bytes[at + 2] << 16);
347
+ }
348
+
349
+ i32(): number {
350
+ return this.view.getInt32(this.need(4), true);
351
+ }
352
+
353
+ /** A float, big-endian, unlike every other number in PBJ. */
354
+ f32(): number {
355
+ return this.view.getFloat32(this.need(4), false);
356
+ }
357
+
358
+ skip(n: number): void {
359
+ this.need(n);
360
+ }
361
+
362
+ /** A string to its terminating zero, in UTF-8. */
363
+ cstring(): string {
364
+ const start = this.at;
365
+ while (this.u8() !== 0) {
366
+ // Up to the zero.
367
+ }
368
+
369
+ return utf8(this.bytes.subarray(start, this.at - 1));
370
+ }
371
+
372
+ string(length: number): string {
373
+ return utf8(this.bytes.subarray(this.need(length), this.at));
374
+ }
375
+ }
376
+
377
+ /** A destination: a register of the channels its 4-bit mask writes, r first. */
378
+ function destination(value: number, mask: number): PbjRegister {
379
+ const channels: number[] = [];
380
+ for (let c = 0; c < 4; c++) {
381
+ if (mask & (8 >> c)) {
382
+ channels.push(c);
383
+ }
384
+ }
385
+
386
+ return {
387
+ index: value & 0x7fff,
388
+ int: (value & 0x8000) !== 0,
389
+ channels,
390
+ size: channels.length,
391
+ matrix: 0,
392
+ };
393
+ }
394
+
395
+ /** A source: its register in the low 16 bits, the swizzle's four channels in the high 8. */
396
+ function source(value: number, size: number): PbjRegister {
397
+ const swizzle = value >>> 16;
398
+ const channels: number[] = [];
399
+ for (let i = 0; i < 4; i++) {
400
+ channels.push((swizzle >> (6 - i * 2)) & 3);
401
+ }
402
+
403
+ return { index: value & 0x7fff, int: (value & 0x8000) !== 0, channels, size, matrix: 0 };
404
+ }
405
+
406
+ function matrix(value: number, size: number): PbjRegister {
407
+ return {
408
+ index: value & 0x7fff,
409
+ int: (value & 0x8000) !== 0,
410
+ channels: [],
411
+ size: 1,
412
+ matrix: size,
413
+ };
414
+ }
415
+
416
+ function metadataValue(r: Reader, type: number): string | number[] {
417
+ if (type === PBJ_STRING) {
418
+ return r.cstring();
419
+ }
420
+
421
+ const values: number[] = [];
422
+ for (let i = 0; i < PBJ_TYPE_SIZES[type]; i++) {
423
+ values.push(pbjFloatType(type) ? r.f32() : r.u16());
424
+ }
425
+
426
+ return values;
427
+ }
428
+
429
+ /**
430
+ * The kernel `bytes` hold, or PbjError with the error Flash refuses it
431
+ * with. Every byte is read: PBJ has no length, so it ends with the bytes.
432
+ */
433
+ export function readPbj(bytes: Uint8Array): PbjKernel {
434
+ const r = new Reader(bytes);
435
+ const kernel: PbjKernel = {
436
+ name: null,
437
+ metadata: [],
438
+ parameters: [],
439
+ inputs: [],
440
+ operations: [],
441
+ };
442
+ /** The parameter the second metadata opcode adds to: the last one, but for an input after it. */
443
+ let owner: PbjParameter | null = null;
444
+ let depth = 0;
445
+ const elses: boolean[] = [];
446
+ const refuse = (): never => {
447
+ throw new PbjError(2004);
448
+ };
449
+
450
+ while (!r.done) {
451
+ const op = r.u8();
452
+ switch (op) {
453
+ case PbjOp.version: {
454
+ if (r.i32() !== 1) {
455
+ refuse();
456
+ }
457
+
458
+ break;
459
+ }
460
+
461
+ case PbjOp.name: {
462
+ kernel.name = r.string(r.u16());
463
+ break;
464
+ }
465
+
466
+ case PbjOp.kernelMetadata:
467
+ case PbjOp.parameterMetadata: {
468
+ const type = r.u8();
469
+ const key = r.cstring();
470
+ if (type === 0 || type >= PBJ_TYPES.length) {
471
+ // adl reads on past what it cannot size, to the end.
472
+ throw new PbjError(2030);
473
+ }
474
+
475
+ const value = metadataValue(r, type);
476
+ if (op === PbjOp.kernelMetadata) {
477
+ if (type !== PBJ_STRING && type !== 0x8) {
478
+ refuse();
479
+ }
480
+
481
+ kernel.metadata.push({ key, type, value });
482
+ } else {
483
+ owner?.metadata.push({ key, type, value });
484
+ }
485
+
486
+ break;
487
+ }
488
+
489
+ case PbjOp.parameter: {
490
+ const qualifier = r.u8();
491
+ const type = r.u8();
492
+ const reg = r.u16();
493
+ const mask = r.u8();
494
+ const name = r.cstring();
495
+ const output = qualifier === 2;
496
+ if (type === PBJ_STRING || name === "name" || (output && !pbjFloatType(type))) {
497
+ refuse();
498
+ }
499
+
500
+ const size = type - 0x5 + 2;
501
+ const register = size >= 2 && size <= 4 ? matrix(reg, size) : destination(reg, mask & 0xf);
502
+ const parameter: PbjParameter = { name, type, output, register, metadata: [] };
503
+ if (output && register.matrix) {
504
+ refuse();
505
+ }
506
+
507
+ kernel.parameters.push(parameter);
508
+ owner = parameter;
509
+ break;
510
+ }
511
+
512
+ case PbjOp.texture: {
513
+ const index = r.u8();
514
+ const channels = r.u8();
515
+ kernel.inputs.push({ name: r.cstring(), index, channels });
516
+ owner = null;
517
+ break;
518
+ }
519
+
520
+ case PbjOp.nop: {
521
+ // Both words read first: a nop cut short is EOFError 2030, whatever its bytes.
522
+ const first = r.u24();
523
+ const second = r.u24();
524
+ if (first !== 0 || second !== 0) {
525
+ refuse();
526
+ }
527
+
528
+ break;
529
+ }
530
+
531
+ case PbjOp.load: {
532
+ const dst = destination(r.u16(), r.u8() >> 4);
533
+ const value = dst.int ? r.i32() & 0xffff : r.f32();
534
+ if (dst.size === 0) {
535
+ refuse();
536
+ }
537
+
538
+ kernel.operations.push({ op, dst, value });
539
+ break;
540
+ }
541
+
542
+ case PbjOp.sampleNearest:
543
+ case PbjOp.sampleLinear: {
544
+ const reg = r.u16();
545
+ const mask = r.u8();
546
+ const src = source(r.u24(), (mask & 3) + 1);
547
+ const texture = r.u8();
548
+ const dst = destination(reg, mask >> 4);
549
+ // adl takes an int coordinate, and a texture never declared, which then has no input.
550
+ if (dst.int || dst.size === 0) {
551
+ refuse();
552
+ }
553
+
554
+ kernel.operations.push({ op, dst, src, texture });
555
+ break;
556
+ }
557
+
558
+ case PbjOp.if: {
559
+ r.skip(3);
560
+ const src = source(r.u24(), 1);
561
+ r.skip(1);
562
+ if (!src.int) {
563
+ refuse();
564
+ }
565
+
566
+ depth++;
567
+ elses.push(false);
568
+ kernel.operations.push({ op, src });
569
+ break;
570
+ }
571
+
572
+ case PbjOp.else:
573
+ case PbjOp.endIf: {
574
+ r.skip(7);
575
+ if (depth === 0 || (op === PbjOp.else && elses[elses.length - 1])) {
576
+ refuse();
577
+ }
578
+
579
+ if (op === PbjOp.else) {
580
+ elses[elses.length - 1] = true;
581
+ } else {
582
+ depth--;
583
+ elses.pop();
584
+ }
585
+
586
+ kernel.operations.push({ op });
587
+ break;
588
+ }
589
+
590
+ case PbjOp.select: {
591
+ const dst = destination(r.u16(), r.u8() >> 4);
592
+ const condition = source(r.u24(), 1);
593
+ r.skip(1);
594
+ const a = source(r.u24(), dst.size);
595
+ r.skip(1);
596
+ const b = source(r.u24(), dst.size);
597
+ r.skip(1);
598
+ if (!condition.int || dst.int !== a.int || a.int !== b.int || dst.size === 0) {
599
+ refuse();
600
+ }
601
+
602
+ kernel.operations.push({ op, dst, condition, a, b });
603
+ break;
604
+ }
605
+
606
+ default: {
607
+ const reg = r.u16();
608
+ const mask = r.u8();
609
+ const value = r.u24();
610
+ r.skip(1);
611
+ const rule = PBJ_RULES.get(op) ?? refuse();
612
+ const size = (mask & 3) + 1;
613
+ const bits = (mask >> 2) & 3;
614
+ if (bits === 0) {
615
+ const dst = destination(reg, mask >> 4);
616
+ const src = source(value, size);
617
+ if (dst.size === 0 || !kindsFit(rule, dst, src) || !sizesFit(rule, dst.size, src.size)) {
618
+ refuse();
619
+ }
620
+
621
+ kernel.operations.push({ op, dst, src });
622
+ break;
623
+ }
624
+
625
+ // A matrix: its size in the mask, and no swizzle.
626
+ const n = bits + 1;
627
+ const src = matrix(value, n);
628
+ const vector = op === PbjOp.matVecMul || op === PbjOp.vecMatMul;
629
+ const dst = vector ? destination(reg, mask >> 4) : matrix(reg, n);
630
+ if (
631
+ !MATRIX_OPS.includes(op) ||
632
+ value >>> 16 !== 0 ||
633
+ size !== 1 ||
634
+ src.int ||
635
+ dst.int ||
636
+ (vector ? dst.size !== n : mask >> 4 !== 0)
637
+ ) {
638
+ refuse();
639
+ }
640
+
641
+ kernel.operations.push({ op, dst, src });
642
+ }
643
+ }
644
+ }
645
+
646
+ if (depth !== 0 || !kernel.parameters.some((p) => p.output)) {
647
+ refuse();
648
+ }
649
+
650
+ return kernel;
651
+ }