@awayfl/avm1
Version:
Virtual machine for executing AS1 and AS2 code
225 lines (224 loc) • 10.2 kB
JavaScript
import { SWFParser, StageAlign, StageScaleMode } from '@awayfl/swf-loader';
import { AVM1SceneGraphFactory } from './AVM1SceneGraphFactory';
import { MouseEvent, KeyboardEvent, MovieClip, FrameScriptManager } from '@awayjs/scene';
import { AssetLibrary, EventBase } from '@awayjs/core';
import { AVMVERSION } from '@awayfl/swf-loader';
import { AVM1ContextImpl } from './interpreter';
import { SecurityDomain } from './SecurityDomain';
import { AVM1Globals, TraceLevel } from './lib/AVM1Globals';
import { getAVM1Object } from './lib/AVM1Utils';
import { AVM1SymbolBase } from './lib/AVM1SymbolBase';
var AVM1Handler = /** @class */ (function () {
function AVM1Handler() {
this.avmVersion = AVMVERSION.AVM1;
this.enterEvent = new EventBase('enterFrame');
this.exitEvent = new EventBase('exitFrame');
this.avm1Listener = {};
this._collectedDispatcher = [];
}
Object.defineProperty(AVM1Handler.prototype, "factory", {
get: function () {
if (!this._factory)
throw ('AVM1Handler - no factory set');
return this._factory;
},
enumerable: false,
configurable: true
});
AVM1Handler.prototype.init = function (avmStage, swfFile, callback) {
var _this = this;
FrameScriptManager.useAVM1 = true;
if (this._avmStage) {
callback(false);
return;
}
this._avmStage = avmStage;
// we only do this in AVM1:
// stage is registered on MouseManager so we can dispatch events on it without event-bubbling
// AVM1 doesnt use veent bubbling, but has those onMouseDown / onMouseUp events that listen on stage,
// no matter what object they are assigned to.
// todo verify this is AVM1 only
this._avmStage.mouseManager.eventBubbling = false;
this._factory = new AVM1SceneGraphFactory(new AVM1ContextImpl(swfFile.swfVersion));
this._factory.avm1Context.sec = new SecurityDomain();
this._factory.avm1Context.setStage(this._avmStage, this, document);
this._factory.avm1Context.swfVersion = swfFile.swfVersion;
this._avmStage.scaleMode = StageScaleMode.SHOW_ALL;
this._avmStage.align = StageAlign.TOP;
AVM1Globals.tracelevel = TraceLevel.ALL;
AVM1Globals._scenegraphFactory = this._factory;
AssetLibrary.enableParser(SWFParser);
// field is readonly, and assigned only in this place
this._factory.avm1Context.globals.SWF_BASE_URL =
swfFile.url.substring(0, swfFile.url.lastIndexOf('/') + 1);
this.clearAllAVM1Listener();
var stage = this._avmStage.view.stage;
stage.addEventListener(MouseEvent.MOUSE_DOWN, function (evt) { return _this.onMouseEvent(evt); });
stage.addEventListener(MouseEvent.MOUSE_UP, function (evt) { return _this.onMouseEvent(evt); });
stage.addEventListener(MouseEvent.MOUSE_MOVE, function (evt) { return _this.onMouseEvent(evt); });
stage.addEventListener(KeyboardEvent.KEYDOWN, function (evt) { return _this.onKeyEvent(evt); });
stage.addEventListener(KeyboardEvent.KEYUP, function (evt) { return _this.onKeyEvent(evt); });
if (this._avmStage.avmTestHandler) {
this._factory.avm1Context.actions.originalTrace = this._factory.avm1Context.actions.trace;
this._factory.avm1Context.actions.trace = function (expression) {
_this._factory.avm1Context.actions.originalTrace(expression);
_this._avmStage.avmTestHandler.addMessage(expression);
};
}
callback(true);
};
AVM1Handler.prototype.collectMCs = function (mc, ouput, event) {
var child;
var c = mc.numChildren;
while (c > 0) {
c--;
child = mc.getChildAt(c);
if (child.isAsset(MovieClip))
this.collectMCs(child, ouput, event);
if (child.hasEventListener(event.type))
ouput.push(child);
}
};
AVM1Handler.prototype.enterFrame = function (dt) {
// todo: do we need this ?
// this._avmStage.view.stage.clear();
FrameScriptManager.execute_queue();
this._avmStage.root.advanceFrame();
//FrameScriptManager.execute_queue();
// collect all enterEvent movieclips
var enterMCs = [];
this.collectMCs(this._avmStage.root, enterMCs, this.enterEvent);
// now dispatch the onEnterFrame
for (var i = 0; i < enterMCs.length; i++)
(enterMCs[i]).dispatchEvent(this.enterEvent);
//we should register clip events after dispatching
AVM1SymbolBase.CompleteEventRegistering();
FrameScriptManager.execute_queue();
FrameScriptManager.execute_intervals(dt);
FrameScriptManager.execute_queue();
// collect all enterEvent movieclips
var exitMCs = [];
this.collectMCs(this._avmStage.root, exitMCs, this.exitEvent);
// now dispatch the onExitFrame
for (var i = 0; i < exitMCs.length; i++)
(exitMCs[i]).dispatchEvent(this.exitEvent);
FrameScriptManager.execute_queue();
};
AVM1Handler.prototype.onKeyEvent = function (event) {
if (!this.avm1Listener[event.type])
return;
// the correct order for stage-event on childs is children first, highest depth first
this._collectedDispatcher.length = 0;
var i = 0;
var child;
for (i = 0; i < this._avmStage.root.numChildren; i++) {
child = this._avmStage.root.getChildAt(i);
if (child.isAsset(MovieClip)) {
this.collectMousEvents(child);
}
}
for (i = 0; i < this._collectedDispatcher.length; i++) {
if (this.avm1Listener[event.type] && this.avm1Listener[event.type][this._collectedDispatcher[i].id]) {
var listeners = this.avm1Listener[event.type][this._collectedDispatcher[i].id];
for (var e = 0; e < listeners.length; e++) {
if (typeof listeners[e].keyCode !== 'number' || listeners[e].keyCode == event.keyCode)
listeners[e].callback();
}
}
}
FrameScriptManager.execute_queue();
};
AVM1Handler.prototype.addAVM1EventListener = function (asset, type, callback, eventProps) {
if (!this.avm1Listener[type])
this.avm1Listener[type] = {};
if (!this.avm1Listener[type][asset.id])
this.avm1Listener[type][asset.id] = [];
var listeners = this.avm1Listener[type][asset.id];
listeners.push({ type: type, callback: callback });
if (eventProps && typeof eventProps.keyCode === 'number') {
listeners[listeners.length - 1].keyCode = eventProps.keyCode;
}
};
AVM1Handler.prototype.removeAVM1EventListener = function (asset, type, callback) {
if (!this.avm1Listener[type])
return;
delete this.avm1Listener[type][asset.id];
};
AVM1Handler.prototype.clearAllAVM1Listener = function () {
this.avm1Listener = {};
};
AVM1Handler.prototype.collectMousEvents = function (child) {
var child2;
var c = child.numChildren;
while (c > 0) {
c--;
child2 = child.getChildAt(c);
this.collectMousEvents(child2);
}
if (child.isAsset(MovieClip)) {
this._collectedDispatcher[this._collectedDispatcher.length] = child;
}
};
AVM1Handler.prototype.onMouseEvent = function (mouseEvent) {
if (!this.avm1Listener[mouseEvent.type])
return;
// the correct order for stage-event on childs is children first, highest depth first
this._collectedDispatcher.length = 0;
var i = 0;
var child;
var len = this._avmStage.root.numChildren;
for (i = 0; i < len; i++) {
child = this._avmStage.root.getChildAt(i);
if (child.isAsset(MovieClip)) {
this.collectMousEvents(child);
}
}
len = this._collectedDispatcher.length;
var dispatcherLen;
for (i = 0; i < len; i++) {
var listenersMouseType = this.avm1Listener[mouseEvent.type];
if (listenersMouseType && listenersMouseType[this._collectedDispatcher[i].id]) {
dispatcherLen = listenersMouseType[this._collectedDispatcher[i].id].length;
for (var e = 0; e < dispatcherLen; e++) {
var listeners = listenersMouseType[this._collectedDispatcher[i].id];
if (listeners && listeners[e])
listeners[e].callback();
}
}
}
FrameScriptManager.execute_queue();
};
AVM1Handler.prototype.dispose = function () {
// @todo
};
AVM1Handler.prototype.resizeStage = function () {
// @todo: is this available for AVM1 code
// if so we must dispatch/broadcast a event here
};
AVM1Handler.prototype.addAsset = function (asset, addScene) {
if (asset.isAsset(MovieClip)) {
if (addScene && asset.isAVMScene) {
var scene = getAVM1Object(asset.clone(), this._factory.avm1Context);
//scene.adaptee.reset();
this._factory.avm1Context.globals._addRoot(0, scene.adaptee);
/*if(this._skipFrames>0){
FrameScriptManager.execute_queue();
if(this._skipFramesCallback){
AudioManager.setVolume(0);
this._skipFramesCallback(()=>{
AudioManager.setVolume(1);
(<MovieClip>asset).currentFrameIndex=this._skipFrames;
(<MovieClip>asset).play();
})
}
else{
(<MovieClip>asset).currentFrameIndex=this._skipFrames;
(<MovieClip>asset).play();
}
}*/
}
}
};
return AVM1Handler;
}());
export { AVM1Handler };