@flighthq/swf 0.3.0-edge.1458.0c01b63
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/dist/contract.d.ts +2 -0
- package/dist/contract.d.ts.map +1 -0
- package/dist/contract.js +2 -0
- package/dist/contract.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/swfDocument.d.ts +4 -0
- package/dist/swfDocument.d.ts.map +1 -0
- package/dist/swfDocument.js +606 -0
- package/dist/swfDocument.js.map +1 -0
- package/package.json +49 -0
- package/src/swfDocument.test.ts +919 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC"}
|
package/dist/contract.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,kCAAkC,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,kCAAkC,EAAE,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { Scene2DDocument, Scene2DDocumentImporterRegistry } from '@flighthq/types/contract';
|
|
2
|
+
export declare function createScene2DFromSwf(source: Uint8Array): Scene2DDocument | null;
|
|
3
|
+
export declare function registerSwfScene2DDocumentImporter(registry: Scene2DDocumentImporterRegistry): void;
|
|
4
|
+
//# sourceMappingURL=swfDocument.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"swfDocument.d.ts","sourceRoot":"","sources":["../src/swfDocument.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAMV,eAAe,EAEf,+BAA+B,EAChC,MAAM,0BAA0B,CAAC;AAElC,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,UAAU,GAAG,eAAe,GAAG,IAAI,CAgC/E;AAED,wBAAgB,kCAAkC,CAAC,QAAQ,EAAE,+BAA+B,GAAG,IAAI,CAElG"}
|
|
@@ -0,0 +1,606 @@
|
|
|
1
|
+
import { addNodeChild, getNodeRuntime, setNodeLocalMatrix } from '@flighthq/node/contract';
|
|
2
|
+
import { createScene2DDocument, createScene2DSlotReference, registerScene2DDocumentImporter, } from '@flighthq/scene2d-resources/contract';
|
|
3
|
+
import { createDisplayObject } from '@flighthq/scene2d/contract';
|
|
4
|
+
export function createScene2DFromSwf(source) {
|
|
5
|
+
const header = new SwfReader(source, 0, source.length);
|
|
6
|
+
const signature = header.readUint8();
|
|
7
|
+
if (signature !== FWS_SIGNATURE || header.readUint8() !== W_SIGNATURE || header.readUint8() !== S_SIGNATURE) {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
const version = header.readUint8();
|
|
11
|
+
const fileLength = header.readUint32();
|
|
12
|
+
if (!header.valid || version === 0 || fileLength < MIN_SWF_LENGTH || fileLength > source.length)
|
|
13
|
+
return null;
|
|
14
|
+
const body = new SwfReader(source, SWF_PREFIX_LENGTH, fileLength);
|
|
15
|
+
const stageBounds = readSwfRectangle(body);
|
|
16
|
+
if (stageBounds === null)
|
|
17
|
+
return null;
|
|
18
|
+
body.readUint16();
|
|
19
|
+
body.readUint16();
|
|
20
|
+
if (!body.valid)
|
|
21
|
+
return null;
|
|
22
|
+
const parsed = readSwfTags(body);
|
|
23
|
+
if (parsed === null)
|
|
24
|
+
return null;
|
|
25
|
+
const root = createSwfDisplayObject(stageBounds);
|
|
26
|
+
const references = [];
|
|
27
|
+
const instantiation = {
|
|
28
|
+
activeSymbols: new Set(),
|
|
29
|
+
resolvingBounds: new Set(),
|
|
30
|
+
resolvedBounds: new Map(),
|
|
31
|
+
remainingNodes: MAX_INSTANTIATED_NODES,
|
|
32
|
+
};
|
|
33
|
+
if (!appendSwfPlacements(root, parsed.placements, parsed, references, instantiation, 0))
|
|
34
|
+
return null;
|
|
35
|
+
return createScene2DDocument(root, references, 'swf');
|
|
36
|
+
}
|
|
37
|
+
export function registerSwfScene2DDocumentImporter(registry) {
|
|
38
|
+
registerScene2DDocumentImporter(registry, 'swf', matchesSwfDocument, (source) => createScene2DFromSwf(source));
|
|
39
|
+
}
|
|
40
|
+
class SwfReader {
|
|
41
|
+
source;
|
|
42
|
+
end;
|
|
43
|
+
bitPosition = 0;
|
|
44
|
+
pos;
|
|
45
|
+
valid = true;
|
|
46
|
+
constructor(source, start, end) {
|
|
47
|
+
this.source = source;
|
|
48
|
+
this.end = end;
|
|
49
|
+
this.pos = start;
|
|
50
|
+
}
|
|
51
|
+
alignToByte() {
|
|
52
|
+
if (this.bitPosition === 0)
|
|
53
|
+
return;
|
|
54
|
+
this.bitPosition = 0;
|
|
55
|
+
this.pos++;
|
|
56
|
+
}
|
|
57
|
+
readSignedBits(count) {
|
|
58
|
+
const value = this.readUnsignedBits(count);
|
|
59
|
+
if (count === 0)
|
|
60
|
+
return 0;
|
|
61
|
+
const sign = 2 ** (count - 1);
|
|
62
|
+
return value >= sign ? value - 2 ** count : value;
|
|
63
|
+
}
|
|
64
|
+
readString() {
|
|
65
|
+
this.alignToByte();
|
|
66
|
+
const start = this.pos;
|
|
67
|
+
while (this.pos < this.end && this.source[this.pos] !== 0)
|
|
68
|
+
this.pos++;
|
|
69
|
+
if (this.pos >= this.end) {
|
|
70
|
+
this.valid = false;
|
|
71
|
+
return '';
|
|
72
|
+
}
|
|
73
|
+
const value = _decoder.decode(this.source.subarray(start, this.pos));
|
|
74
|
+
this.pos++;
|
|
75
|
+
return value;
|
|
76
|
+
}
|
|
77
|
+
readUint8() {
|
|
78
|
+
this.alignToByte();
|
|
79
|
+
if (this.pos >= this.end) {
|
|
80
|
+
this.valid = false;
|
|
81
|
+
return 0;
|
|
82
|
+
}
|
|
83
|
+
return this.source[this.pos++];
|
|
84
|
+
}
|
|
85
|
+
readUint16() {
|
|
86
|
+
const low = this.readUint8();
|
|
87
|
+
const high = this.readUint8();
|
|
88
|
+
return low + high * 0x100;
|
|
89
|
+
}
|
|
90
|
+
readUint32() {
|
|
91
|
+
const low = this.readUint16();
|
|
92
|
+
const high = this.readUint16();
|
|
93
|
+
return low + high * 0x10000;
|
|
94
|
+
}
|
|
95
|
+
readUnsignedBits(count) {
|
|
96
|
+
let value = 0;
|
|
97
|
+
for (let i = 0; i < count; i++) {
|
|
98
|
+
if (this.pos >= this.end) {
|
|
99
|
+
this.valid = false;
|
|
100
|
+
return 0;
|
|
101
|
+
}
|
|
102
|
+
value = value * 2 + ((this.source[this.pos] >> (7 - this.bitPosition)) & 1);
|
|
103
|
+
this.bitPosition++;
|
|
104
|
+
if (this.bitPosition === 8) {
|
|
105
|
+
this.bitPosition = 0;
|
|
106
|
+
this.pos++;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return value;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function matchesSwfDocument(source, context) {
|
|
113
|
+
if (context.mimeType === SWF_MIME_TYPE)
|
|
114
|
+
return true;
|
|
115
|
+
if (source.length < 3 || source[1] !== W_SIGNATURE || source[2] !== S_SIGNATURE)
|
|
116
|
+
return false;
|
|
117
|
+
return source[0] === FWS_SIGNATURE || source[0] === CWS_SIGNATURE || source[0] === ZWS_SIGNATURE;
|
|
118
|
+
}
|
|
119
|
+
function appendSwfPlacements(parent, placements, parsed, references, state, depth) {
|
|
120
|
+
if (depth > MAX_SPRITE_NESTING)
|
|
121
|
+
return false;
|
|
122
|
+
const ordered = [...placements.values()].sort((a, b) => a.depth - b.depth);
|
|
123
|
+
for (const placement of ordered) {
|
|
124
|
+
const sprite = parsed.sprites.get(placement.characterId);
|
|
125
|
+
if (!placement.name && sprite === undefined)
|
|
126
|
+
continue;
|
|
127
|
+
if (state.remainingNodes === 0)
|
|
128
|
+
return false;
|
|
129
|
+
state.remainingNodes--;
|
|
130
|
+
const target = createSwfDisplayObject(resolveSwfCharacterBounds(parsed, placement.characterId, state, 0));
|
|
131
|
+
setNodeLocalMatrix(target, placement.matrix);
|
|
132
|
+
addNodeChild(parent, target);
|
|
133
|
+
if (placement.name) {
|
|
134
|
+
references.push(createScene2DSlotReference(placement.name, target, placement.directLinkage ?? parsed.linkages.get(placement.characterId) ?? null));
|
|
135
|
+
}
|
|
136
|
+
if (sprite !== undefined) {
|
|
137
|
+
if (state.activeSymbols.has(placement.characterId))
|
|
138
|
+
return false;
|
|
139
|
+
state.activeSymbols.add(placement.characterId);
|
|
140
|
+
const appended = appendSwfPlacements(target, sprite.placements, parsed, references, state, depth + 1);
|
|
141
|
+
state.activeSymbols.delete(placement.characterId);
|
|
142
|
+
if (!appended)
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
function computeSwfLocalBoundsRectangle(out, source) {
|
|
149
|
+
const bounds = source.data.authoredBounds;
|
|
150
|
+
out.x = bounds.x;
|
|
151
|
+
out.y = bounds.y;
|
|
152
|
+
out.width = bounds.width;
|
|
153
|
+
out.height = bounds.height;
|
|
154
|
+
}
|
|
155
|
+
function createSwfDisplayObject(bounds) {
|
|
156
|
+
const target = createDisplayObject();
|
|
157
|
+
if (bounds !== null) {
|
|
158
|
+
target.data = { authoredBounds: { ...bounds } };
|
|
159
|
+
getNodeRuntime(target).computeLocalBoundsRectangle = computeSwfLocalBoundsRectangle;
|
|
160
|
+
}
|
|
161
|
+
return target;
|
|
162
|
+
}
|
|
163
|
+
function mergeSwfRectangles(a, b) {
|
|
164
|
+
const x = Math.min(a.x, b.x);
|
|
165
|
+
const y = Math.min(a.y, b.y);
|
|
166
|
+
const maxX = Math.max(a.x + a.width, b.x + b.width);
|
|
167
|
+
const maxY = Math.max(a.y + a.height, b.y + b.height);
|
|
168
|
+
return { height: maxY - y, width: maxX - x, x, y };
|
|
169
|
+
}
|
|
170
|
+
function resolveSwfCharacterBounds(parsed, characterId, state, depth) {
|
|
171
|
+
const direct = parsed.characterBounds.get(characterId);
|
|
172
|
+
if (direct !== undefined)
|
|
173
|
+
return direct;
|
|
174
|
+
if (state.resolvedBounds.has(characterId))
|
|
175
|
+
return state.resolvedBounds.get(characterId) ?? null;
|
|
176
|
+
const sprite = parsed.sprites.get(characterId);
|
|
177
|
+
if (sprite === undefined || depth > MAX_SPRITE_NESTING || state.resolvingBounds.has(characterId))
|
|
178
|
+
return null;
|
|
179
|
+
state.resolvingBounds.add(characterId);
|
|
180
|
+
let bounds = null;
|
|
181
|
+
for (const placement of sprite.placements.values()) {
|
|
182
|
+
const childBounds = resolveSwfCharacterBounds(parsed, placement.characterId, state, depth + 1);
|
|
183
|
+
if (childBounds === null)
|
|
184
|
+
continue;
|
|
185
|
+
const transformed = transformSwfRectangle(childBounds, placement.matrix);
|
|
186
|
+
bounds = bounds === null ? transformed : mergeSwfRectangles(bounds, transformed);
|
|
187
|
+
}
|
|
188
|
+
state.resolvingBounds.delete(characterId);
|
|
189
|
+
state.resolvedBounds.set(characterId, bounds);
|
|
190
|
+
return bounds;
|
|
191
|
+
}
|
|
192
|
+
function transformSwfRectangle(bounds, matrix) {
|
|
193
|
+
const x0 = matrix.a * bounds.x + matrix.c * bounds.y + matrix.tx;
|
|
194
|
+
const y0 = matrix.b * bounds.x + matrix.d * bounds.y + matrix.ty;
|
|
195
|
+
const x1 = matrix.a * (bounds.x + bounds.width) + matrix.c * bounds.y + matrix.tx;
|
|
196
|
+
const y1 = matrix.b * (bounds.x + bounds.width) + matrix.d * bounds.y + matrix.ty;
|
|
197
|
+
const x2 = matrix.a * bounds.x + matrix.c * (bounds.y + bounds.height) + matrix.tx;
|
|
198
|
+
const y2 = matrix.b * bounds.x + matrix.d * (bounds.y + bounds.height) + matrix.ty;
|
|
199
|
+
const x3 = matrix.a * (bounds.x + bounds.width) + matrix.c * (bounds.y + bounds.height) + matrix.tx;
|
|
200
|
+
const y3 = matrix.b * (bounds.x + bounds.width) + matrix.d * (bounds.y + bounds.height) + matrix.ty;
|
|
201
|
+
const x = Math.min(x0, x1, x2, x3);
|
|
202
|
+
const y = Math.min(y0, y1, y2, y3);
|
|
203
|
+
const maxX = Math.max(x0, x1, x2, x3);
|
|
204
|
+
const maxY = Math.max(y0, y1, y2, y3);
|
|
205
|
+
return { height: maxY - y, width: maxX - x, x, y };
|
|
206
|
+
}
|
|
207
|
+
function readPlaceObject(body, placements, hasExtendedFlags) {
|
|
208
|
+
const flags = body.readUint8();
|
|
209
|
+
const extendedFlags = hasExtendedFlags ? body.readUint8() : 0;
|
|
210
|
+
const depth = body.readUint16();
|
|
211
|
+
const existing = placements.get(depth);
|
|
212
|
+
const isMove = (flags & 0x01) !== 0;
|
|
213
|
+
const inherited = isMove ? existing : undefined;
|
|
214
|
+
const hasCharacter = (flags & 0x02) !== 0;
|
|
215
|
+
const hasClassName = (extendedFlags & 0x08) !== 0 || ((extendedFlags & 0x10) !== 0 && hasCharacter);
|
|
216
|
+
const directLinkage = hasClassName ? body.readString() : (inherited?.directLinkage ?? null);
|
|
217
|
+
const characterId = hasCharacter ? body.readUint16() : (inherited?.characterId ?? 0);
|
|
218
|
+
const matrix = (flags & 0x04) !== 0 ? readSwfMatrix(body) : (inherited?.matrix ?? IDENTITY_MATRIX);
|
|
219
|
+
if ((flags & 0x08) !== 0)
|
|
220
|
+
readSwfColorTransform(body);
|
|
221
|
+
if ((flags & 0x10) !== 0)
|
|
222
|
+
body.readUint16();
|
|
223
|
+
const name = (flags & 0x20) !== 0 ? body.readString() : (inherited?.name ?? null);
|
|
224
|
+
if (!body.valid || (isMove && existing === undefined) || (characterId === 0 && directLinkage === null))
|
|
225
|
+
return;
|
|
226
|
+
placements.set(depth, { characterId, depth, directLinkage, matrix, name });
|
|
227
|
+
}
|
|
228
|
+
function readLegacyPlaceObject(body, placements) {
|
|
229
|
+
const characterId = body.readUint16();
|
|
230
|
+
const depth = body.readUint16();
|
|
231
|
+
const matrix = readSwfMatrix(body);
|
|
232
|
+
if (body.pos < body.end)
|
|
233
|
+
readSwfColorTransform(body, 3);
|
|
234
|
+
if (!body.valid || characterId === 0)
|
|
235
|
+
return;
|
|
236
|
+
placements.set(depth, { characterId, depth, directLinkage: null, matrix, name: null });
|
|
237
|
+
}
|
|
238
|
+
function readLegacyRemoveObject(body, placements) {
|
|
239
|
+
const characterId = body.readUint16();
|
|
240
|
+
const depth = body.readUint16();
|
|
241
|
+
if (!body.valid)
|
|
242
|
+
return;
|
|
243
|
+
const existing = placements.get(depth);
|
|
244
|
+
if (existing?.characterId === characterId)
|
|
245
|
+
placements.delete(depth);
|
|
246
|
+
}
|
|
247
|
+
function readSwfColorTransform(reader, channelCount = 4) {
|
|
248
|
+
const hasAdd = reader.readUnsignedBits(1) !== 0;
|
|
249
|
+
const hasMultiply = reader.readUnsignedBits(1) !== 0;
|
|
250
|
+
const bits = reader.readUnsignedBits(4);
|
|
251
|
+
if (hasMultiply) {
|
|
252
|
+
for (let i = 0; i < channelCount; i++)
|
|
253
|
+
reader.readSignedBits(bits);
|
|
254
|
+
}
|
|
255
|
+
if (hasAdd) {
|
|
256
|
+
for (let i = 0; i < channelCount; i++)
|
|
257
|
+
reader.readSignedBits(bits);
|
|
258
|
+
}
|
|
259
|
+
reader.alignToByte();
|
|
260
|
+
}
|
|
261
|
+
function readSwfLinkages(body, linkages) {
|
|
262
|
+
const count = body.readUint16();
|
|
263
|
+
for (let i = 0; i < count && body.valid; i++) {
|
|
264
|
+
const characterId = body.readUint16();
|
|
265
|
+
const name = body.readString();
|
|
266
|
+
if (name)
|
|
267
|
+
linkages.set(characterId, name);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
function readSwfMatrix(reader) {
|
|
271
|
+
let a = 1;
|
|
272
|
+
let d = 1;
|
|
273
|
+
if (reader.readUnsignedBits(1) !== 0) {
|
|
274
|
+
const scaleBits = reader.readUnsignedBits(5);
|
|
275
|
+
a = reader.readSignedBits(scaleBits) / FIXED_16_ONE;
|
|
276
|
+
d = reader.readSignedBits(scaleBits) / FIXED_16_ONE;
|
|
277
|
+
}
|
|
278
|
+
let b = 0;
|
|
279
|
+
let c = 0;
|
|
280
|
+
if (reader.readUnsignedBits(1) !== 0) {
|
|
281
|
+
const rotateBits = reader.readUnsignedBits(5);
|
|
282
|
+
b = reader.readSignedBits(rotateBits) / FIXED_16_ONE;
|
|
283
|
+
c = reader.readSignedBits(rotateBits) / FIXED_16_ONE;
|
|
284
|
+
}
|
|
285
|
+
const translateBits = reader.readUnsignedBits(5);
|
|
286
|
+
const tx = reader.readSignedBits(translateBits) / TWIPS_PER_PIXEL;
|
|
287
|
+
const ty = reader.readSignedBits(translateBits) / TWIPS_PER_PIXEL;
|
|
288
|
+
reader.alignToByte();
|
|
289
|
+
return { a, b, c, d, tx, ty };
|
|
290
|
+
}
|
|
291
|
+
function readSwfRectangle(reader) {
|
|
292
|
+
const bits = reader.readUnsignedBits(5);
|
|
293
|
+
const xMin = reader.readSignedBits(bits);
|
|
294
|
+
const xMax = reader.readSignedBits(bits);
|
|
295
|
+
const yMin = reader.readSignedBits(bits);
|
|
296
|
+
const yMax = reader.readSignedBits(bits);
|
|
297
|
+
reader.alignToByte();
|
|
298
|
+
if (!reader.valid || xMax < xMin || yMax < yMin)
|
|
299
|
+
return null;
|
|
300
|
+
return {
|
|
301
|
+
height: (yMax - yMin) / TWIPS_PER_PIXEL,
|
|
302
|
+
width: (xMax - xMin) / TWIPS_PER_PIXEL,
|
|
303
|
+
x: xMin / TWIPS_PER_PIXEL,
|
|
304
|
+
y: yMin / TWIPS_PER_PIXEL,
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
function readSwfTags(reader) {
|
|
308
|
+
const state = {
|
|
309
|
+
characterBounds: new Map(),
|
|
310
|
+
definedCharacters: new Set(),
|
|
311
|
+
linkages: new Map(),
|
|
312
|
+
sprites: new Map(),
|
|
313
|
+
};
|
|
314
|
+
const placements = readSwfTimeline(reader, state);
|
|
315
|
+
if (placements === null)
|
|
316
|
+
return null;
|
|
317
|
+
return {
|
|
318
|
+
characterBounds: state.characterBounds,
|
|
319
|
+
linkages: state.linkages,
|
|
320
|
+
placements,
|
|
321
|
+
sprites: state.sprites,
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
function readSwfTimeline(reader, state) {
|
|
325
|
+
const placements = new Map();
|
|
326
|
+
let firstFrame = null;
|
|
327
|
+
let foundEnd = false;
|
|
328
|
+
while (reader.pos < reader.end && reader.valid) {
|
|
329
|
+
const tagHeader = reader.readUint16();
|
|
330
|
+
const code = tagHeader >> 6;
|
|
331
|
+
const shortLength = tagHeader & 0x3f;
|
|
332
|
+
const length = shortLength === 0x3f ? reader.readUint32() : shortLength;
|
|
333
|
+
const bodyEnd = reader.pos + length;
|
|
334
|
+
if (!reader.valid || bodyEnd > reader.end)
|
|
335
|
+
return null;
|
|
336
|
+
const body = new SwfReader(reader.source, reader.pos, bodyEnd);
|
|
337
|
+
reader.pos = bodyEnd;
|
|
338
|
+
if (code === TAG_END) {
|
|
339
|
+
foundEnd = true;
|
|
340
|
+
break;
|
|
341
|
+
}
|
|
342
|
+
if (code === TAG_SHOW_FRAME) {
|
|
343
|
+
firstFrame ??= new Map(placements);
|
|
344
|
+
}
|
|
345
|
+
else if (code === TAG_PLACE_OBJECT) {
|
|
346
|
+
readLegacyPlaceObject(body, placements);
|
|
347
|
+
}
|
|
348
|
+
else if (code === TAG_PLACE_OBJECT_2) {
|
|
349
|
+
readPlaceObject(body, placements, false);
|
|
350
|
+
}
|
|
351
|
+
else if (code === TAG_REMOVE_OBJECT) {
|
|
352
|
+
readLegacyRemoveObject(body, placements);
|
|
353
|
+
}
|
|
354
|
+
else if (code === TAG_REMOVE_OBJECT_2) {
|
|
355
|
+
placements.delete(body.readUint16());
|
|
356
|
+
}
|
|
357
|
+
else if (code === TAG_EXPORT_ASSETS || code === TAG_SYMBOL_CLASS) {
|
|
358
|
+
readSwfLinkages(body, state.linkages);
|
|
359
|
+
}
|
|
360
|
+
else if (code === TAG_DEFINE_BITS_JPEG_2 || code === TAG_DEFINE_BITS_JPEG_3 || code === TAG_DEFINE_BITS_JPEG_4) {
|
|
361
|
+
if (!readSwfEmbeddedImageDefinition(body, state, code))
|
|
362
|
+
return null;
|
|
363
|
+
}
|
|
364
|
+
else if (code === TAG_DEFINE_BITS_LOSSLESS || code === TAG_DEFINE_BITS_LOSSLESS_2) {
|
|
365
|
+
if (!readSwfLosslessBitmapDefinition(body, state, code === TAG_DEFINE_BITS_LOSSLESS_2))
|
|
366
|
+
return null;
|
|
367
|
+
}
|
|
368
|
+
else if (code === TAG_DEFINE_VIDEO_STREAM) {
|
|
369
|
+
if (!readSwfVideoDefinition(body, state))
|
|
370
|
+
return null;
|
|
371
|
+
}
|
|
372
|
+
else if (isSwfBoundedDefinitionTag(code)) {
|
|
373
|
+
if (!readSwfBoundedDefinition(body, state, code === TAG_DEFINE_MORPH_SHAPE || code === TAG_DEFINE_MORPH_SHAPE_2)) {
|
|
374
|
+
return null;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
else if (code === TAG_DEFINE_SPRITE) {
|
|
378
|
+
const spriteId = body.readUint16();
|
|
379
|
+
body.readUint16();
|
|
380
|
+
if (!body.valid || spriteId === 0 || state.definedCharacters.has(spriteId))
|
|
381
|
+
return null;
|
|
382
|
+
state.definedCharacters.add(spriteId);
|
|
383
|
+
const spriteReader = new SwfReader(body.source, body.pos, body.end);
|
|
384
|
+
const spritePlacements = readSwfTimeline(spriteReader, state);
|
|
385
|
+
if (spritePlacements === null || spriteReader.pos !== spriteReader.end) {
|
|
386
|
+
return null;
|
|
387
|
+
}
|
|
388
|
+
state.sprites.set(spriteId, { placements: spritePlacements });
|
|
389
|
+
}
|
|
390
|
+
else if (code === TAG_PLACE_OBJECT_3 || code === TAG_PLACE_OBJECT_4) {
|
|
391
|
+
readPlaceObject(body, placements, true);
|
|
392
|
+
}
|
|
393
|
+
if (!body.valid)
|
|
394
|
+
return null;
|
|
395
|
+
}
|
|
396
|
+
if (!reader.valid || !foundEnd)
|
|
397
|
+
return null;
|
|
398
|
+
return firstFrame ?? placements;
|
|
399
|
+
}
|
|
400
|
+
function isSwfBoundedDefinitionTag(code) {
|
|
401
|
+
return (code === TAG_DEFINE_SHAPE ||
|
|
402
|
+
code === TAG_DEFINE_SHAPE_2 ||
|
|
403
|
+
code === TAG_DEFINE_SHAPE_3 ||
|
|
404
|
+
code === TAG_DEFINE_SHAPE_4 ||
|
|
405
|
+
code === TAG_DEFINE_TEXT ||
|
|
406
|
+
code === TAG_DEFINE_TEXT_2 ||
|
|
407
|
+
code === TAG_DEFINE_EDIT_TEXT ||
|
|
408
|
+
code === TAG_DEFINE_MORPH_SHAPE ||
|
|
409
|
+
code === TAG_DEFINE_MORPH_SHAPE_2);
|
|
410
|
+
}
|
|
411
|
+
function readSwfBoundedDefinition(body, state, hasEndBounds) {
|
|
412
|
+
const characterId = body.readUint16();
|
|
413
|
+
const startBounds = readSwfRectangle(body);
|
|
414
|
+
const endBounds = hasEndBounds ? readSwfRectangle(body) : null;
|
|
415
|
+
if (!body.valid ||
|
|
416
|
+
characterId === 0 ||
|
|
417
|
+
startBounds === null ||
|
|
418
|
+
(hasEndBounds && endBounds === null) ||
|
|
419
|
+
state.definedCharacters.has(characterId)) {
|
|
420
|
+
return false;
|
|
421
|
+
}
|
|
422
|
+
state.definedCharacters.add(characterId);
|
|
423
|
+
state.characterBounds.set(characterId, endBounds === null ? startBounds : mergeSwfRectangles(startBounds, endBounds));
|
|
424
|
+
return true;
|
|
425
|
+
}
|
|
426
|
+
function readSwfEmbeddedImageDefinition(body, state, code) {
|
|
427
|
+
const characterId = body.readUint16();
|
|
428
|
+
let imageStart = body.pos;
|
|
429
|
+
let imageEnd = body.end;
|
|
430
|
+
if (code === TAG_DEFINE_BITS_JPEG_3 || code === TAG_DEFINE_BITS_JPEG_4) {
|
|
431
|
+
const alphaDataOffset = body.readUint32();
|
|
432
|
+
const alphaOffsetBase = body.pos;
|
|
433
|
+
if (code === TAG_DEFINE_BITS_JPEG_4)
|
|
434
|
+
body.readUint16();
|
|
435
|
+
imageStart = body.pos;
|
|
436
|
+
imageEnd = alphaOffsetBase + alphaDataOffset;
|
|
437
|
+
}
|
|
438
|
+
if (!body.valid ||
|
|
439
|
+
characterId === 0 ||
|
|
440
|
+
imageEnd < imageStart ||
|
|
441
|
+
imageEnd > body.end ||
|
|
442
|
+
state.definedCharacters.has(characterId)) {
|
|
443
|
+
return false;
|
|
444
|
+
}
|
|
445
|
+
const bounds = readSwfEmbeddedImageBounds(body.source, imageStart, imageEnd);
|
|
446
|
+
if (bounds === null)
|
|
447
|
+
return false;
|
|
448
|
+
state.definedCharacters.add(characterId);
|
|
449
|
+
state.characterBounds.set(characterId, bounds);
|
|
450
|
+
return true;
|
|
451
|
+
}
|
|
452
|
+
function readSwfEmbeddedImageBounds(source, start, end) {
|
|
453
|
+
if (end - start >= 24 &&
|
|
454
|
+
source[start] === 0x89 &&
|
|
455
|
+
source[start + 1] === 0x50 &&
|
|
456
|
+
source[start + 2] === 0x4e &&
|
|
457
|
+
source[start + 3] === 0x47 &&
|
|
458
|
+
source[start + 4] === 0x0d &&
|
|
459
|
+
source[start + 5] === 0x0a &&
|
|
460
|
+
source[start + 6] === 0x1a &&
|
|
461
|
+
source[start + 7] === 0x0a &&
|
|
462
|
+
readBigEndianUint32(source, start + 8) === 13 &&
|
|
463
|
+
source[start + 12] === 0x49 &&
|
|
464
|
+
source[start + 13] === 0x48 &&
|
|
465
|
+
source[start + 14] === 0x44 &&
|
|
466
|
+
source[start + 15] === 0x52) {
|
|
467
|
+
return createSwfDimensionBounds(readBigEndianUint32(source, start + 16), readBigEndianUint32(source, start + 20));
|
|
468
|
+
}
|
|
469
|
+
if (end - start >= 10 &&
|
|
470
|
+
source[start] === 0x47 &&
|
|
471
|
+
source[start + 1] === 0x49 &&
|
|
472
|
+
source[start + 2] === 0x46 &&
|
|
473
|
+
source[start + 3] === 0x38 &&
|
|
474
|
+
(source[start + 4] === 0x37 || source[start + 4] === 0x39) &&
|
|
475
|
+
source[start + 5] === 0x61) {
|
|
476
|
+
return createSwfDimensionBounds(source[start + 6] + source[start + 7] * 0x100, source[start + 8] + source[start + 9] * 0x100);
|
|
477
|
+
}
|
|
478
|
+
if (end - start < 4 || source[start] !== 0xff || source[start + 1] !== JPEG_START_OF_IMAGE)
|
|
479
|
+
return null;
|
|
480
|
+
let pos = start + 2;
|
|
481
|
+
while (pos < end) {
|
|
482
|
+
if (source[pos++] !== 0xff)
|
|
483
|
+
return null;
|
|
484
|
+
while (pos < end && source[pos] === 0xff)
|
|
485
|
+
pos++;
|
|
486
|
+
if (pos >= end)
|
|
487
|
+
return null;
|
|
488
|
+
const marker = source[pos++];
|
|
489
|
+
if (marker === JPEG_END_OF_IMAGE || marker === JPEG_START_OF_SCAN)
|
|
490
|
+
return null;
|
|
491
|
+
if (marker === JPEG_START_OF_IMAGE || marker === JPEG_TEMPORARY || (marker >= 0xd0 && marker <= 0xd7))
|
|
492
|
+
continue;
|
|
493
|
+
if (pos + 2 > end)
|
|
494
|
+
return null;
|
|
495
|
+
const length = readBigEndianUint16(source, pos);
|
|
496
|
+
if (length < 2 || pos + length > end)
|
|
497
|
+
return null;
|
|
498
|
+
if (marker >= 0xc0 &&
|
|
499
|
+
marker <= 0xcf &&
|
|
500
|
+
marker !== JPEG_DEFINE_HUFFMAN_TABLES &&
|
|
501
|
+
marker !== JPEG_EXTENSION &&
|
|
502
|
+
marker !== JPEG_DEFINE_ARITHMETIC_CODING) {
|
|
503
|
+
if (length < 7)
|
|
504
|
+
return null;
|
|
505
|
+
return createSwfDimensionBounds(readBigEndianUint16(source, pos + 5), readBigEndianUint16(source, pos + 3));
|
|
506
|
+
}
|
|
507
|
+
pos += length;
|
|
508
|
+
}
|
|
509
|
+
return null;
|
|
510
|
+
}
|
|
511
|
+
function createSwfDimensionBounds(width, height) {
|
|
512
|
+
return width === 0 || height === 0 ? null : { height, width, x: 0, y: 0 };
|
|
513
|
+
}
|
|
514
|
+
function readBigEndianUint16(source, offset) {
|
|
515
|
+
return source[offset] * 0x100 + source[offset + 1];
|
|
516
|
+
}
|
|
517
|
+
function readBigEndianUint32(source, offset) {
|
|
518
|
+
return source[offset] * 0x1000000 + source[offset + 1] * 0x10000 + source[offset + 2] * 0x100 + source[offset + 3];
|
|
519
|
+
}
|
|
520
|
+
function readSwfLosslessBitmapDefinition(body, state, hasAlpha) {
|
|
521
|
+
const characterId = body.readUint16();
|
|
522
|
+
const format = body.readUint8();
|
|
523
|
+
const width = body.readUint16();
|
|
524
|
+
const height = body.readUint16();
|
|
525
|
+
if (format === LOSSLESS_BITMAP_FORMAT_COLORMAPPED)
|
|
526
|
+
body.readUint8();
|
|
527
|
+
const validFormat = format === LOSSLESS_BITMAP_FORMAT_COLORMAPPED ||
|
|
528
|
+
format === LOSSLESS_BITMAP_FORMAT_32_BIT ||
|
|
529
|
+
(!hasAlpha && format === LOSSLESS_BITMAP_FORMAT_15_BIT);
|
|
530
|
+
if (!body.valid ||
|
|
531
|
+
characterId === 0 ||
|
|
532
|
+
width === 0 ||
|
|
533
|
+
height === 0 ||
|
|
534
|
+
state.definedCharacters.has(characterId) ||
|
|
535
|
+
!validFormat) {
|
|
536
|
+
return false;
|
|
537
|
+
}
|
|
538
|
+
state.definedCharacters.add(characterId);
|
|
539
|
+
state.characterBounds.set(characterId, { height, width, x: 0, y: 0 });
|
|
540
|
+
return true;
|
|
541
|
+
}
|
|
542
|
+
function readSwfVideoDefinition(body, state) {
|
|
543
|
+
const characterId = body.readUint16();
|
|
544
|
+
body.readUint16();
|
|
545
|
+
const width = body.readUint16();
|
|
546
|
+
const height = body.readUint16();
|
|
547
|
+
body.readUint8();
|
|
548
|
+
body.readUint8();
|
|
549
|
+
if (!body.valid || characterId === 0 || width === 0 || height === 0 || state.definedCharacters.has(characterId)) {
|
|
550
|
+
return false;
|
|
551
|
+
}
|
|
552
|
+
state.definedCharacters.add(characterId);
|
|
553
|
+
state.characterBounds.set(characterId, { height, width, x: 0, y: 0 });
|
|
554
|
+
return true;
|
|
555
|
+
}
|
|
556
|
+
const CWS_SIGNATURE = 0x43;
|
|
557
|
+
const FIXED_16_ONE = 0x10000;
|
|
558
|
+
const FWS_SIGNATURE = 0x46;
|
|
559
|
+
const IDENTITY_MATRIX = { a: 1, b: 0, c: 0, d: 1, tx: 0, ty: 0 };
|
|
560
|
+
const JPEG_DEFINE_ARITHMETIC_CODING = 0xcc;
|
|
561
|
+
const JPEG_DEFINE_HUFFMAN_TABLES = 0xc4;
|
|
562
|
+
const JPEG_END_OF_IMAGE = 0xd9;
|
|
563
|
+
const JPEG_EXTENSION = 0xc8;
|
|
564
|
+
const JPEG_START_OF_IMAGE = 0xd8;
|
|
565
|
+
const JPEG_START_OF_SCAN = 0xda;
|
|
566
|
+
const JPEG_TEMPORARY = 0x01;
|
|
567
|
+
const LOSSLESS_BITMAP_FORMAT_15_BIT = 4;
|
|
568
|
+
const LOSSLESS_BITMAP_FORMAT_32_BIT = 5;
|
|
569
|
+
const LOSSLESS_BITMAP_FORMAT_COLORMAPPED = 3;
|
|
570
|
+
const MAX_INSTANTIATED_NODES = 100_000;
|
|
571
|
+
const MAX_SPRITE_NESTING = 256;
|
|
572
|
+
const MIN_SWF_LENGTH = 12;
|
|
573
|
+
const S_SIGNATURE = 0x53;
|
|
574
|
+
const SWF_MIME_TYPE = 'application/x-shockwave-flash';
|
|
575
|
+
const SWF_PREFIX_LENGTH = 8;
|
|
576
|
+
const TAG_END = 0;
|
|
577
|
+
const TAG_DEFINE_BITS_JPEG_2 = 21;
|
|
578
|
+
const TAG_DEFINE_BITS_JPEG_3 = 35;
|
|
579
|
+
const TAG_DEFINE_BITS_JPEG_4 = 90;
|
|
580
|
+
const TAG_DEFINE_BITS_LOSSLESS = 20;
|
|
581
|
+
const TAG_DEFINE_BITS_LOSSLESS_2 = 36;
|
|
582
|
+
const TAG_DEFINE_EDIT_TEXT = 37;
|
|
583
|
+
const TAG_DEFINE_MORPH_SHAPE = 46;
|
|
584
|
+
const TAG_DEFINE_MORPH_SHAPE_2 = 84;
|
|
585
|
+
const TAG_DEFINE_SHAPE = 2;
|
|
586
|
+
const TAG_DEFINE_SHAPE_2 = 22;
|
|
587
|
+
const TAG_DEFINE_SHAPE_3 = 32;
|
|
588
|
+
const TAG_DEFINE_SHAPE_4 = 83;
|
|
589
|
+
const TAG_DEFINE_SPRITE = 39;
|
|
590
|
+
const TAG_DEFINE_TEXT = 11;
|
|
591
|
+
const TAG_DEFINE_TEXT_2 = 33;
|
|
592
|
+
const TAG_DEFINE_VIDEO_STREAM = 60;
|
|
593
|
+
const TAG_EXPORT_ASSETS = 56;
|
|
594
|
+
const TAG_PLACE_OBJECT = 4;
|
|
595
|
+
const TAG_PLACE_OBJECT_2 = 26;
|
|
596
|
+
const TAG_PLACE_OBJECT_3 = 70;
|
|
597
|
+
const TAG_PLACE_OBJECT_4 = 94;
|
|
598
|
+
const TAG_REMOVE_OBJECT = 5;
|
|
599
|
+
const TAG_REMOVE_OBJECT_2 = 28;
|
|
600
|
+
const TAG_SHOW_FRAME = 1;
|
|
601
|
+
const TAG_SYMBOL_CLASS = 76;
|
|
602
|
+
const TWIPS_PER_PIXEL = 20;
|
|
603
|
+
const W_SIGNATURE = 0x57;
|
|
604
|
+
const ZWS_SIGNATURE = 0x5a;
|
|
605
|
+
const _decoder = new TextDecoder();
|
|
606
|
+
//# sourceMappingURL=swfDocument.js.map
|