@swf2es/player-hosts 0.1.3 → 0.1.5
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/thread/format/bitmap.js +13 -14
- package/dist/thread/format/index.js +1 -1
- package/dist/thread/format/swf.js +11 -1
- package/dist/thread/player/avm1/globals/BitmapData.js +2 -1
- package/dist/thread/player/avm1/keyboard.js +13 -0
- package/dist/thread/player/avm1/loads.js +3 -0
- package/dist/thread/player/bitmap/composite.js +767 -0
- package/dist/thread/player/bitmap/draw.js +3 -3
- package/dist/thread/player/bitmap/filters.js +32 -32
- package/dist/thread/player/bitmap/ops.js +2 -766
- package/dist/thread/player/bitmap/pixel-bender.js +4 -1
- package/dist/thread/player/display/displayobject.js +1 -1
- package/dist/thread/player/display/instantiate.js +28 -29
- package/dist/thread/player/input/keyboard.js +20 -1
- package/dist/thread/player/input/text-input.js +99 -0
- package/dist/thread/player/media/audio.js +11 -0
- package/dist/thread/player/media/sounds.js +20 -0
- package/dist/thread/player/player.js +3 -1
- package/dist/thread/player/playerglobal/flash/desktop/NativeApplication.js +1 -1
- package/dist/thread/player/playerglobal/flash/display/BitmapData.js +3 -2
- package/dist/thread/player/playerglobal/flash/display/SimpleButton.js +12 -11
- package/dist/thread/player/playerglobal/flash/display/Stage.js +2 -8
- package/dist/thread/player/playerglobal/flash/display/shaders.js +1 -1
- package/dist/thread/player/playerglobal/flash/filesystem/paths.js +1 -1
- package/dist/thread/player/playerglobal/flash/filesystem/writes.js +1 -1
- package/dist/thread/player/playerglobal/flash/globalization/locale.js +1 -1
- package/dist/thread/player/playerglobal/flash/media/Sound.js +3 -0
- package/dist/thread/player/scripting/events.js +2 -2
- package/dist/thread/player/scripting/loads.js +4 -1
- package/dist/thread/player/scripting/symbols.js +24 -30
- package/dist/thread/player/scripting.js +1 -6
- package/dist/thread/runtime/avm1/errors.js +1 -1
- package/dist/thread/runtime/avm1/numbers.js +7 -8
- package/dist/thread/runtime/avm2/amf.js +2 -1
- package/dist/thread/runtime/avm2/names.js +0 -4
- package/dist/thread/runtime/avm2/natives/bytearray.js +6 -166
- package/dist/thread/runtime/avm2/natives/regexp.js +68 -64
- package/dist/thread/runtime/avm2/natives/shell-workers.js +1 -1
- package/dist/thread/runtime/avm2/natives/shell.js +2 -1
- package/dist/thread/runtime/avm2/natives/xml/parser.js +2 -2
- package/dist/thread/runtime/avm2/numbers.js +1 -1
- package/dist/thread/runtime/avm2/runtime.js +10 -8
- package/dist/thread/runtime/avm2/traits.js +1 -1
- package/dist/thread/runtime/avm2/utf8.js +167 -0
- package/package.json +2 -2
|
@@ -767,7 +767,10 @@ export class PixelBender {
|
|
|
767
767
|
}
|
|
768
768
|
}
|
|
769
769
|
}
|
|
770
|
-
/**
|
|
770
|
+
/**
|
|
771
|
+
* A float as a bitmap's byte: clamped, NaN as 0, then rounded to the nearest, a half to the even.
|
|
772
|
+
* @public tests/player/pixel-bender.ts imports it into Chrome by name.
|
|
773
|
+
*/
|
|
771
774
|
export function outputByte(v) {
|
|
772
775
|
const c = v > 0 ? (v < 1 ? v : 1) : 0;
|
|
773
776
|
return nearest(Math.fround(c * 255));
|
|
@@ -12,7 +12,7 @@ export const TRANSFORM = 1;
|
|
|
12
12
|
export const CHILDREN = 2;
|
|
13
13
|
export const CONTENT = 4;
|
|
14
14
|
/** A Bitmap's pixels changed in place: the texture uploads again, nothing is rebuilt. */
|
|
15
|
-
|
|
15
|
+
const PIXELS = 8;
|
|
16
16
|
/**
|
|
17
17
|
* A count that moves whenever something changes that a round of frame
|
|
18
18
|
* scripts (Scripting.runFrameScripts) reads to find a script to run: a
|
|
@@ -10,35 +10,34 @@ import { INVALID_PIXELS } from "./timeline.js";
|
|
|
10
10
|
/** A display object for a character, before its first frame: a shape, or a clip. */
|
|
11
11
|
/** `hasImage` is PlaceObject3's flag where a timeline places it, which a bitmap's construction reads. */
|
|
12
12
|
export function displayFor(character, library, hasImage = false) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
return button;
|
|
13
|
+
switch (character.type) {
|
|
14
|
+
case "shape":
|
|
15
|
+
return new ShapeObject(character);
|
|
16
|
+
case "morph":
|
|
17
|
+
return ShapeObject.ofMorph(character);
|
|
18
|
+
case "bitmap": {
|
|
19
|
+
// With scripts, its Bitmap's constructor gives it its data; without, a copy of the pixels.
|
|
20
|
+
const bitmap = new BitmapObject(library.construct ? null : BitmapStore.of(character.pixels ?? INVALID_PIXELS));
|
|
21
|
+
bitmap.character = character;
|
|
22
|
+
bitmap.hasImage = hasImage;
|
|
23
|
+
return bitmap;
|
|
24
|
+
}
|
|
25
|
+
case "static":
|
|
26
|
+
return new StaticTextObject(character, library.characters);
|
|
27
|
+
case "text": {
|
|
28
|
+
const text = new TextObject(character);
|
|
29
|
+
text.fonts = library.fonts;
|
|
30
|
+
return text;
|
|
31
|
+
}
|
|
32
|
+
// Its states are made where it is constructed (Scripting.construct).
|
|
33
|
+
case "button": {
|
|
34
|
+
const button = new ButtonObject();
|
|
35
|
+
button.character = character;
|
|
36
|
+
button.library = library;
|
|
37
|
+
button.trackAsMenu = character.trackAsMenu;
|
|
38
|
+
button.scale9Grid = character.grid;
|
|
39
|
+
return button;
|
|
40
|
+
}
|
|
42
41
|
}
|
|
43
42
|
const clip = new MovieClip(character.timeline, library);
|
|
44
43
|
clip.character = character;
|
|
@@ -11,6 +11,7 @@ import { apply } from "../display/geometry.js";
|
|
|
11
11
|
import { TEXT } from "../scripting/clipboard.js";
|
|
12
12
|
import { dispatchEvent } from "../scripting/events.js";
|
|
13
13
|
import { caretAt, pasteable, selectionAt, TextEditor } from "./text-editor.js";
|
|
14
|
+
import { isTextInput } from "./text-input.js";
|
|
14
15
|
const TAB = 9;
|
|
15
16
|
const fieldsOf = (d) => (d.object ?? {});
|
|
16
17
|
/**
|
|
@@ -129,6 +130,21 @@ export class KeyboardInput {
|
|
|
129
130
|
: false;
|
|
130
131
|
});
|
|
131
132
|
}
|
|
133
|
+
get editing() {
|
|
134
|
+
const focus = this.focused();
|
|
135
|
+
return focus instanceof TextObject && focus.type === "input";
|
|
136
|
+
}
|
|
137
|
+
insert(text) {
|
|
138
|
+
this.handled++;
|
|
139
|
+
return this.scripting.clipboard.gesture(() => {
|
|
140
|
+
const focus = this.focused();
|
|
141
|
+
if (!(focus instanceof TextObject) || focus.type !== "input") {
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
const typed = pasteable(focus.multiline, text);
|
|
145
|
+
return typed !== "" && this.editor.type(focus, typed);
|
|
146
|
+
});
|
|
147
|
+
}
|
|
132
148
|
key(type, k, edits) {
|
|
133
149
|
const s = this.scripting;
|
|
134
150
|
const focus = this.focused();
|
|
@@ -376,12 +392,15 @@ function charCodeOf(e) {
|
|
|
376
392
|
/** Whether a browser event is the page's own: typed into its input or button, or editable content. */
|
|
377
393
|
function pagesOwn(e) {
|
|
378
394
|
const into = e.target;
|
|
395
|
+
if (isTextInput(into)) {
|
|
396
|
+
return false;
|
|
397
|
+
}
|
|
379
398
|
return (!!into?.isContentEditable || /^(INPUT|TEXTAREA|SELECT|BUTTON|A)$/.test(into?.tagName ?? ""));
|
|
380
399
|
}
|
|
381
400
|
/**
|
|
382
401
|
* Send the browser's keys on `target` (the window, say) to the player. A
|
|
383
402
|
* key goes to the SWF unless it is typed into the page's own input or
|
|
384
|
-
* button, or
|
|
403
|
+
* button, or composes an IME's text, which comes by bindTextInput; the
|
|
385
404
|
* browser is kept from acting on a field's edit or caret, Tab moving the
|
|
386
405
|
* SWF's focus, or a scrolling key meant for the SWF. Its shortcuts stay.
|
|
387
406
|
* The copy, cut and paste events the browser sends for Ctrl+C, X and V,
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// Text that comes with no key of its own: an IME's composed text, a soft
|
|
2
|
+
// keyboard's, or any insertion a browser makes (DevTools' Input.insertText).
|
|
3
|
+
// A browser sends those only to an editable element, which the player's
|
|
4
|
+
// canvas is not, so the host keeps a hidden textarea of its own, focused
|
|
5
|
+
// while a SWF's input field has focus. Keys typed in it still bubble to
|
|
6
|
+
// bindKeyboard's listener, which types their characters; only text
|
|
7
|
+
// without such a key goes to the field from here.
|
|
8
|
+
/** The hidden textareas the player made: their keys are the SWF's, not the page's. */
|
|
9
|
+
const editors = new WeakSet();
|
|
10
|
+
/** Whether `target` is the player's own textarea, whose keys are the SWF's. */
|
|
11
|
+
export function isTextInput(target) {
|
|
12
|
+
return !!target && editors.has(target);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* A hidden textarea in `box`, inside `host`, the element the page focuses,
|
|
16
|
+
* through which committed text reaches the player: an insertion without a
|
|
17
|
+
* composition, or a composition's end. Text a key typed, which the player
|
|
18
|
+
* has from its keydown, is left out, as is text in mid-composition, which
|
|
19
|
+
* the SWF never sees, and what was inserted is cleared away at once.
|
|
20
|
+
*/
|
|
21
|
+
export function bindTextInput(player, host, box) {
|
|
22
|
+
const editor = box.ownerDocument.createElement("textarea");
|
|
23
|
+
editors.add(editor);
|
|
24
|
+
editor.tabIndex = -1;
|
|
25
|
+
editor.autocomplete = "off";
|
|
26
|
+
editor.spellcheck = false;
|
|
27
|
+
editor.setAttribute("autocapitalize", "off");
|
|
28
|
+
editor.setAttribute("autocorrect", "off");
|
|
29
|
+
editor.style.cssText =
|
|
30
|
+
"position:absolute;left:0;top:0;width:1px;height:1px;padding:0;border:0;margin:0;" +
|
|
31
|
+
"opacity:0;resize:none;overflow:hidden;pointer-events:none;caret-color:transparent";
|
|
32
|
+
box.append(editor);
|
|
33
|
+
// A key bindKeyboard sent the SWF, down until let go: what it inserts, its keydown typed.
|
|
34
|
+
let keyed = false;
|
|
35
|
+
const insert = (text) => {
|
|
36
|
+
if (text !== "") {
|
|
37
|
+
player.keyboard?.insert(text);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
const keydown = (event) => {
|
|
41
|
+
const e = event;
|
|
42
|
+
keyed = !e.isComposing && e.keyCode !== 229;
|
|
43
|
+
};
|
|
44
|
+
const keyup = () => {
|
|
45
|
+
keyed = false;
|
|
46
|
+
};
|
|
47
|
+
const compositionend = (event) => {
|
|
48
|
+
insert(event.data);
|
|
49
|
+
editor.value = "";
|
|
50
|
+
};
|
|
51
|
+
const input = (event) => {
|
|
52
|
+
const e = event;
|
|
53
|
+
if (e.isComposing) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
// A composition's own inputs, Safari's insertFromComposition after its end among them, are
|
|
57
|
+
// left to compositionend, and a paste to bindKeyboard's paste listener.
|
|
58
|
+
if (e.inputType === "insertText" && !keyed && e.data) {
|
|
59
|
+
insert(e.data);
|
|
60
|
+
}
|
|
61
|
+
editor.value = "";
|
|
62
|
+
};
|
|
63
|
+
const listeners = [
|
|
64
|
+
["keydown", keydown],
|
|
65
|
+
["keyup", keyup],
|
|
66
|
+
["compositionend", compositionend],
|
|
67
|
+
["input", input],
|
|
68
|
+
];
|
|
69
|
+
for (const [type, listener] of listeners) {
|
|
70
|
+
editor.addEventListener(type, listener);
|
|
71
|
+
}
|
|
72
|
+
// The host's own methods, not what is on it: a SWF's ExternalInterface callbacks may be.
|
|
73
|
+
const focusHost = () => HTMLElement.prototype.focus.call(host, { preventScroll: true });
|
|
74
|
+
const focusOf = (node) => Node.prototype.getRootNode.call(node).activeElement;
|
|
75
|
+
return {
|
|
76
|
+
sync() {
|
|
77
|
+
const outer = focusOf(host);
|
|
78
|
+
const inEditor = focusOf(editor) === editor;
|
|
79
|
+
const hostFocused = outer === host || (outer !== null && Node.prototype.contains.call(host, outer));
|
|
80
|
+
const editing = player.keyboard?.editing ?? false;
|
|
81
|
+
if (editing && hostFocused && !inEditor) {
|
|
82
|
+
editor.focus({ preventScroll: true });
|
|
83
|
+
}
|
|
84
|
+
else if (!editing && inEditor) {
|
|
85
|
+
focusHost();
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
destroy() {
|
|
89
|
+
for (const [type, listener] of listeners) {
|
|
90
|
+
editor.removeEventListener(type, listener);
|
|
91
|
+
}
|
|
92
|
+
// Removed while focused, the focus would leave the host for the page's body.
|
|
93
|
+
if (focusOf(editor) === editor) {
|
|
94
|
+
focusHost();
|
|
95
|
+
}
|
|
96
|
+
editor.remove();
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
}
|
|
@@ -26,6 +26,17 @@ export function browserAudioHost() {
|
|
|
26
26
|
streams.add(stream);
|
|
27
27
|
return stream;
|
|
28
28
|
},
|
|
29
|
+
warm() {
|
|
30
|
+
if (closed) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
context ??= new Context();
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
// A browser out of contexts refuses one: the first decode asks again, and fails there.
|
|
38
|
+
}
|
|
39
|
+
},
|
|
29
40
|
close() {
|
|
30
41
|
// A browser keeps a context, and its thread, until it is closed.
|
|
31
42
|
closed = true;
|
|
@@ -26,6 +26,26 @@ export function outputMix(local, global) {
|
|
|
26
26
|
};
|
|
27
27
|
}
|
|
28
28
|
export const mixerMix = (s) => mixerMixes.get(s) ?? mixOf(null);
|
|
29
|
+
/**
|
|
30
|
+
* Has `audio` open its device as `library` arrives, if the library has a
|
|
31
|
+
* sound or a stream to play: before its first frame, not at its first sound
|
|
32
|
+
* mid-play. A movie without either opens no device for it.
|
|
33
|
+
*/
|
|
34
|
+
export function warmAudioFor(audio, library) {
|
|
35
|
+
if (!audio?.warm) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (library.root.stream) {
|
|
39
|
+
audio.warm();
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
for (const character of library.characters.values()) {
|
|
43
|
+
if (character.type === "sound" || (character.type === "sprite" && character.timeline.stream)) {
|
|
44
|
+
audio.warm();
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
29
49
|
const hosts = new WeakMap();
|
|
30
50
|
/** An AS3 movie's SoundHost: its device, clock and symbols, its sprites' and SoundMixer's transforms. */
|
|
31
51
|
export function soundHostOf(s) {
|
|
@@ -15,7 +15,7 @@ import { customContextMenu } from "./input/context-menu.js";
|
|
|
15
15
|
import { KeyboardInput } from "./input/keyboard.js";
|
|
16
16
|
import { PointerInput } from "./input/pointer.js";
|
|
17
17
|
import { TouchInput } from "./input/touch.js";
|
|
18
|
-
import { soundHostOf, streamFrames } from "./media/sounds.js";
|
|
18
|
+
import { soundHostOf, streamFrames, warmAudioFor } from "./media/sounds.js";
|
|
19
19
|
/** Ordinary frames one advance() may run at most, however fast the SWF and slow the display. */
|
|
20
20
|
const MAX_FRAMES_PER_CALL = 5;
|
|
21
21
|
/** A stream may catch up further when its scripts are cheap; input still gets a bounded turn. */
|
|
@@ -108,6 +108,8 @@ export class Player {
|
|
|
108
108
|
this.avm1 = avm1;
|
|
109
109
|
this.swf = readSwf(bytes);
|
|
110
110
|
this.library = readLibrary(this.swf);
|
|
111
|
+
// The audio device opens while the page waits for the movie, not at its first sound.
|
|
112
|
+
warmAudioFor(scripting?.audio ?? avm1?.audio ?? null, this.library);
|
|
111
113
|
this.width = Math.round((this.swf.frameSize.xMax - this.swf.frameSize.xMin) / 20);
|
|
112
114
|
this.height = Math.round((this.swf.frameSize.yMax - this.swf.frameSize.yMin) / 20);
|
|
113
115
|
setStageSize(this.stage, this.width, this.height);
|
|
@@ -13,7 +13,7 @@ import { nativeWindowOf } from "../display/NativeWindow.js";
|
|
|
13
13
|
import { addListener, removeListener } from "../events/EventDispatcher.js";
|
|
14
14
|
import { fileAt } from "../filesystem/File.js";
|
|
15
15
|
/** NativeApplication.runtimeVersion, and its patch level: the AIR these natives follow. */
|
|
16
|
-
|
|
16
|
+
const AIR_RUNTIME_VERSION = "51.3.1.2";
|
|
17
17
|
const AIR_PATCH_LEVEL = 2;
|
|
18
18
|
const INVOKE = "invoke";
|
|
19
19
|
export function nativeApplicationNatives(s) {
|
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
// ArgumentError 2015 for a size it refuses and for a store disposed.
|
|
4
4
|
import { avm2 } from "../../../../runtime/index.js";
|
|
5
5
|
import { BitmapStore, pixelRect, spanRect } from "../../../bitmap/bitmap.js";
|
|
6
|
+
import { drawBitmap } from "../../../bitmap/composite.js";
|
|
6
7
|
import { drawDisplayObject } from "../../../bitmap/draw.js";
|
|
7
8
|
import { applyFilter, filterRefusal, filtersDrawn, GLOWS, generatedRect, } from "../../../bitmap/filters.js";
|
|
8
|
-
import { alphaAt, colorBounds, colorTransform, comparePixels, copyChannel,
|
|
9
|
+
import { alphaAt, colorBounds, colorTransform, comparePixels, copyChannel, floodFill, histogram, isThresholdOperation, merge, noise, paletteMap, perlinNoise, pixelDissolve, scroll, threshold, } from "../../../bitmap/ops.js";
|
|
9
10
|
import { shaderFilter } from "../../../bitmap/pixel-bender.js";
|
|
10
11
|
import { encodePng } from "../../../bitmap/png.js";
|
|
11
12
|
import { INVALID_PIXELS } from "../../../display/timeline.js";
|
|
@@ -74,7 +75,7 @@ function sourceOf(s, v) {
|
|
|
74
75
|
}
|
|
75
76
|
/**
|
|
76
77
|
* draw: a BitmapData, or a Bitmap as its data, its own transform ignored,
|
|
77
|
-
* composited on the CPU (bitmap/
|
|
78
|
+
* composited on the CPU (bitmap/composite.ts); any other display
|
|
78
79
|
* object rendered by the Scripting's drawer at `samples` a side, over its
|
|
79
80
|
* own bounds alone, then composited the same way. A null source is
|
|
80
81
|
* ArgumentError 2005, as Flash words it.
|
|
@@ -24,17 +24,18 @@ export function simpleButtonNatives(s) {
|
|
|
24
24
|
if (d?.parent && d.parent !== b && !d.encloses(b)) {
|
|
25
25
|
d.parent.removeChild(d);
|
|
26
26
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
27
|
+
switch (state) {
|
|
28
|
+
case "up":
|
|
29
|
+
b.upState = d;
|
|
30
|
+
break;
|
|
31
|
+
case "over":
|
|
32
|
+
b.overState = d;
|
|
33
|
+
break;
|
|
34
|
+
case "down":
|
|
35
|
+
b.downState = d;
|
|
36
|
+
break;
|
|
37
|
+
default:
|
|
38
|
+
b.hitTestState = d;
|
|
38
39
|
}
|
|
39
40
|
b.show();
|
|
40
41
|
// adl reports #2150 as an error nothing caught, from the setter of the state the button
|
|
@@ -41,12 +41,9 @@ export function stageNatives(s) {
|
|
|
41
41
|
s.invalidated = true;
|
|
42
42
|
}
|
|
43
43
|
get scaleMode() {
|
|
44
|
-
return s.
|
|
44
|
+
return s.stageScaleMode;
|
|
45
45
|
}
|
|
46
46
|
set scaleMode(v) {
|
|
47
|
-
if (!s.liveStageLayout) {
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
47
|
if (v == null) {
|
|
51
48
|
throw s.rt.error("TypeError", 2007, "scaleMode");
|
|
52
49
|
}
|
|
@@ -67,12 +64,9 @@ export function stageNatives(s) {
|
|
|
67
64
|
}
|
|
68
65
|
}
|
|
69
66
|
get align() {
|
|
70
|
-
return s.
|
|
67
|
+
return s.stageAlign;
|
|
71
68
|
}
|
|
72
69
|
set align(v) {
|
|
73
|
-
if (!s.liveStageLayout) {
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
70
|
if (v == null) {
|
|
77
71
|
throw s.rt.error("TypeError", 2007, "align");
|
|
78
72
|
}
|
|
@@ -29,7 +29,7 @@ export function kernelOfShader(shader) {
|
|
|
29
29
|
* number, or ArgumentError 2004, as many as its type holds, 0 past the
|
|
30
30
|
* Array's end.
|
|
31
31
|
*/
|
|
32
|
-
|
|
32
|
+
function parameterValues(s, data, p) {
|
|
33
33
|
const classes = shaderClasses(s);
|
|
34
34
|
const size = PBJ_TYPE_SIZES[p.type] ?? 0;
|
|
35
35
|
const float = pbjFloatType(p.type);
|
|
@@ -151,6 +151,6 @@ export class NativePaths {
|
|
|
151
151
|
}
|
|
152
152
|
}
|
|
153
153
|
/** A path segment in a URL: UTF-8, percent-encoded as adl writes them ("#?%" and spaces among them). */
|
|
154
|
-
|
|
154
|
+
function encodeSegment(segment) {
|
|
155
155
|
return encodeURIComponent(segment);
|
|
156
156
|
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import { avm2 } from "../../../../runtime/index.js";
|
|
8
8
|
import { NativePaths } from "./paths.js";
|
|
9
9
|
/** "Error #id: message", as an IOErrorEvent's text begins. */
|
|
10
|
-
|
|
10
|
+
function errorText(id) {
|
|
11
11
|
return `Error #${id}: ${avm2.errorMessages[id]}`;
|
|
12
12
|
}
|
|
13
13
|
/** Throws adl's SecurityError where `at` is in the application's directory by its app: URL. */
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// LastOperationStatus's values.
|
|
2
2
|
export const NO_ERROR = "noError";
|
|
3
3
|
export const USING_FALLBACK_WARNING = "usingFallbackWarning";
|
|
4
|
-
|
|
4
|
+
const USING_DEFAULT_WARNING = "usingDefaultWarning";
|
|
5
5
|
export const PARSE_ERROR = "parseError";
|
|
6
6
|
export const UNSUPPORTED_ERROR = "unsupportedError";
|
|
7
7
|
export const PATTERN_SYNTAX_ERROR = "patternSyntaxError";
|
|
@@ -327,6 +327,9 @@ export function soundNatives(s) {
|
|
|
327
327
|
throw s.rt.error("Error", 2037);
|
|
328
328
|
}
|
|
329
329
|
sound.used = true;
|
|
330
|
+
// What it loads is decoded as it arrives, which may be mid-play: the
|
|
331
|
+
// device opens now, as it is asked for, rather than then.
|
|
332
|
+
s.audio?.warm?.();
|
|
330
333
|
const generation = ++sound.generation;
|
|
331
334
|
const abort = new AbortController();
|
|
332
335
|
sound.abort = abort;
|
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
// player's input and frame events and playerglobal's dispatchEvent all
|
|
5
5
|
// go through it.
|
|
6
6
|
import { avm2 } from "../../runtime/index.js";
|
|
7
|
-
|
|
7
|
+
const CAPTURING_PHASE = 1;
|
|
8
8
|
export const AT_TARGET = 2;
|
|
9
|
-
|
|
9
|
+
const BUBBLING_PHASE = 3;
|
|
10
10
|
/** The fields of `s`'s events, found once playerglobal is loaded. */
|
|
11
11
|
export function eventFields(s) {
|
|
12
12
|
if (s.eventFields) {
|
|
@@ -12,7 +12,7 @@ import { decodeImageFile, decodeImages, hasUndecoded, imageFormat } from "../bit
|
|
|
12
12
|
import { MovieClip, rootOf, unloads } from "../display/display.js";
|
|
13
13
|
import { readLibrary } from "../display/timeline.js";
|
|
14
14
|
import { LoadedAvm1Input } from "../input/avm1.js";
|
|
15
|
-
import { streamFrames } from "../media/sounds.js";
|
|
15
|
+
import { streamFrames, warmAudioFor } from "../media/sounds.js";
|
|
16
16
|
import { dispatchEvent } from "./events.js";
|
|
17
17
|
export class Loads {
|
|
18
18
|
s;
|
|
@@ -240,6 +240,7 @@ export class Loads {
|
|
|
240
240
|
avm1Library(swf, domain) {
|
|
241
241
|
const library = readLibrary(swf);
|
|
242
242
|
library.domain = domain;
|
|
243
|
+
warmAudioFor(this.s.audio, library);
|
|
243
244
|
return library;
|
|
244
245
|
}
|
|
245
246
|
/**
|
|
@@ -522,6 +523,8 @@ export class Loads {
|
|
|
522
523
|
}
|
|
523
524
|
const library = readLibrary(swf);
|
|
524
525
|
library.domain = load.domain;
|
|
526
|
+
// Its sounds' device opens as it arrives, before the frame that shows it.
|
|
527
|
+
warmAudioFor(this.s.audio, library);
|
|
525
528
|
// Its images first: once its code is linked, a script in the domain can
|
|
526
529
|
// make its symbols, whose bitmaps take the pixels there are then.
|
|
527
530
|
await decodeImages(library, this.s.decodeImage);
|
|
@@ -269,36 +269,30 @@ export class Symbols {
|
|
|
269
269
|
}
|
|
270
270
|
return display;
|
|
271
271
|
}
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
return new VideoObject();
|
|
297
|
-
}
|
|
298
|
-
if (t.name === "flash.text::TextField") {
|
|
299
|
-
const text = new TextObject(null);
|
|
300
|
-
text.fonts = (this.s.code.codeLibrary() ?? library).fonts;
|
|
301
|
-
return text;
|
|
272
|
+
switch (t.name) {
|
|
273
|
+
case "flash.display::SimpleButton":
|
|
274
|
+
return new ButtonObject();
|
|
275
|
+
case "flash.display::MovieClip":
|
|
276
|
+
return new MovieClip(EMPTY_TIMELINE, library);
|
|
277
|
+
case "flash.display::Shape":
|
|
278
|
+
return new ShapeObject(null);
|
|
279
|
+
// Only a timeline makes a MorphShape or a StaticText.
|
|
280
|
+
case "flash.display::MorphShape":
|
|
281
|
+
throw this.s.rt.error("ArgumentError", 2012, "MorphShape$");
|
|
282
|
+
case "flash.text::StaticText":
|
|
283
|
+
throw this.s.rt.error("ArgumentError", 2012, "StaticText$");
|
|
284
|
+
// Only a load of an AVM1 SWF makes one (requestLoad).
|
|
285
|
+
case "flash.display::AVM1Movie":
|
|
286
|
+
throw this.s.rt.error("ArgumentError", 2012, "AVM1Movie$");
|
|
287
|
+
case "flash.display::Bitmap":
|
|
288
|
+
return new BitmapObject(null);
|
|
289
|
+
case "flash.media::Video":
|
|
290
|
+
return new VideoObject();
|
|
291
|
+
case "flash.text::TextField": {
|
|
292
|
+
const text = new TextObject(null);
|
|
293
|
+
text.fonts = (this.s.code.codeLibrary() ?? library).fonts;
|
|
294
|
+
return text;
|
|
295
|
+
}
|
|
302
296
|
}
|
|
303
297
|
}
|
|
304
298
|
return new Container();
|
|
@@ -208,8 +208,6 @@ export class Scripting {
|
|
|
208
208
|
/** What flash.display.Stage reports and sets; the player copies the frame rate back each frame. */
|
|
209
209
|
stageWidth = 0;
|
|
210
210
|
stageHeight = 0;
|
|
211
|
-
/** Live Stage layout is part of the opt-in Context3D installation. */
|
|
212
|
-
liveStageLayout = false;
|
|
213
211
|
stageScaleMode = "showAll";
|
|
214
212
|
stageAlign = "";
|
|
215
213
|
/** A script's setting takes precedence over the embedding's defaults. */
|
|
@@ -586,9 +584,6 @@ export class Scripting {
|
|
|
586
584
|
}
|
|
587
585
|
/** Embedding defaults apply until ActionScript sets the corresponding property. */
|
|
588
586
|
setStageDefaults(scaleMode, align) {
|
|
589
|
-
if (!this.liveStageLayout) {
|
|
590
|
-
return;
|
|
591
|
-
}
|
|
592
587
|
if (!this.stageScaleModeSet) {
|
|
593
588
|
this.stageScaleMode = scaleMode;
|
|
594
589
|
}
|
|
@@ -598,7 +593,7 @@ export class Scripting {
|
|
|
598
593
|
}
|
|
599
594
|
/** The host's visible size in noScale, the authored size in other modes. */
|
|
600
595
|
resizeStage(width, height) {
|
|
601
|
-
if (
|
|
596
|
+
if (width === this.stageWidth && height === this.stageHeight) {
|
|
602
597
|
return;
|
|
603
598
|
}
|
|
604
599
|
this.stageWidth = width;
|
|
@@ -16,7 +16,7 @@ export class Halt extends Error {
|
|
|
16
16
|
* own check (adl; Ruffle's continue_if_base_clip_exists). One instance,
|
|
17
17
|
* thrown as a signal and never shown.
|
|
18
18
|
*/
|
|
19
|
-
|
|
19
|
+
class BaseRemoved extends Error {
|
|
20
20
|
name = "Avm1BaseRemoved";
|
|
21
21
|
}
|
|
22
22
|
export const BASE_REMOVED = new BaseRemoved("the clip whose code runs was removed");
|
|
@@ -43,14 +43,13 @@ export function numberToString(n) {
|
|
|
43
43
|
if (Number.isNaN(n)) {
|
|
44
44
|
return "NaN";
|
|
45
45
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
return "0";
|
|
46
|
+
switch (n) {
|
|
47
|
+
case Number.POSITIVE_INFINITY:
|
|
48
|
+
return "Infinity";
|
|
49
|
+
case Number.NEGATIVE_INFINITY:
|
|
50
|
+
return "-Infinity";
|
|
51
|
+
case 0:
|
|
52
|
+
return "0";
|
|
54
53
|
}
|
|
55
54
|
if (n >= -2147483648 && n <= 2147483647 && Number.isInteger(n)) {
|
|
56
55
|
return String(n);
|
|
@@ -13,8 +13,9 @@
|
|
|
13
13
|
// the Mozilla Public License, v. 2.0: http://mozilla.org/MPL/2.0/.
|
|
14
14
|
import { FloatValue } from "./float.js";
|
|
15
15
|
import { NS_PackageInternal, NS_Public, namespace, publicQname, qname } from "./names.js";
|
|
16
|
-
import { bytesOf
|
|
16
|
+
import { bytesOf } from "./natives/bytearray.js";
|
|
17
17
|
import { xmlToXMLString } from "./natives/xml/strings.js";
|
|
18
|
+
import { fromUtf8, utf8 } from "./utf8.js";
|
|
18
19
|
const kUndefined = 0;
|
|
19
20
|
const kNull = 1;
|
|
20
21
|
const kFalse = 2;
|
|
@@ -7,9 +7,6 @@ import { NO_CACHE } from "./property-cache.js";
|
|
|
7
7
|
/** Namespace kinds, numbered as the compiler's NS_* constants. */
|
|
8
8
|
export const NS_Public = 0;
|
|
9
9
|
export const NS_PackageInternal = 1;
|
|
10
|
-
export const NS_Protected = 2;
|
|
11
|
-
export const NS_Explicit = 3;
|
|
12
|
-
export const NS_StaticProtected = 4;
|
|
13
10
|
export const NS_Private = 5;
|
|
14
11
|
export class Namespace {
|
|
15
12
|
kind;
|
|
@@ -77,7 +74,6 @@ export const CONSTANT_Multiname = 0x09;
|
|
|
77
74
|
export const CONSTANT_MultinameA = 0x0e;
|
|
78
75
|
export const CONSTANT_MultinameL = 0x1b;
|
|
79
76
|
export const CONSTANT_MultinameLA = 0x1c;
|
|
80
|
-
export const CONSTANT_TypeName = 0x1d;
|
|
81
77
|
/**
|
|
82
78
|
* A name as a property lookup uses it: its namespaces, each with the API
|
|
83
79
|
* version it sees bindings at, and its local name, null for any name.
|