UNPKG

@awayfl/avm1

Version:

Virtual machine for executing AS1 and AS2 code

225 lines (224 loc) 10.2 kB
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 };