import { MakiTree, ModernAction, ModernStore, XmlTree, XmlNode, Thunk, } from "./types"; import JSZip from "jszip"; import * as Utils from "./utils"; import initialize from "./initialize"; import initializeStateTree from "./initializeStateTree"; import { run } from "./maki-interpreter/virtualMachine"; import System from "./runtime/System"; import runtime from "./runtime"; import MakiObject from "./runtime/MakiObject"; import JsScript from "./runtime/JsScript"; export function setMakiTree(makiTree: MakiTree): ModernAction { return { type: "SET_MAKI_TREE", makiTree }; } export function setXmlTree(xmlTree: XmlTree): Thunk { return async dispatch => { dispatch({ type: "SET_XML_TREE", xmlTree: await initializeStateTree(xmlTree), }); }; } export function gotSkinUrl(skinUrl: string, store: ModernStore): Thunk { return async dispatch => { const resp = await fetch(skinUrl); dispatch(gotSkinBlob(await resp.blob(), store)); }; } export function gotSkinBlob(blob: Blob, store: ModernStore): Thunk { return async dispatch => { dispatch(gotSkinZip(await JSZip.loadAsync(blob), store)); }; } async function unloadSkin(makiTree: MakiTree): Promise { await Utils.asyncTreeFlatMap(makiTree, async (node: MakiObject) => { if (node instanceof JsScript && node.system) { node.system.onscriptunloading(); } return node; }); } export function gotSkinZip(zip: JSZip, store: ModernStore): Thunk { return async dispatch => { // unload current skin if one has been loaded if (store.getState().modernSkin.skinLoaded) { await unloadSkin(store.getState().modernSkin.makiTree); dispatch({ type: "SKIN_UNLOADED" }); } const skinXml = await Utils.readXml(zip, "skin.xml"); if (skinXml == null) { throw new Error("Could not find skin.xml in skin"); } const rawXmlTree = await Utils.inlineIncludes(skinXml, zip); const xmlTree = Utils.mapTreeBreadth( rawXmlTree, (node: XmlNode, parent: XmlNode) => { return { ...node, uid: Utils.getId(), parent }; } ); dispatch(setXmlTree(xmlTree)); const makiTree: MakiObject = await initialize(zip, xmlTree); // Execute scripts await Utils.asyncTreeFlatMap(makiTree, async (node: MakiObject) => { if (!(node instanceof JsScript)) { return node; } const scriptPath = node.getScriptPath(); if (scriptPath == null) { return node; } if (scriptPath.endsWith("standardframe.maki")) { // TODO: stop ignoring standardframe return node; } // removes groupdefs from consideration (only run scripts when actually referenced by group) if (Utils.findParentNodeOfType(node, new Set(["groupdef"]))) { return node; } const scriptGroup = Utils.findParentNodeOfType( node, new Set(["group", "winampabstractionlayer", "wasabixml"]) ); node.system = new System(scriptGroup, store); const script = await Utils.readUint8array(zip, scriptPath); if (script == null) { console.warn(`Unable to find script at ${scriptPath}`); return node; } run({ runtime, data: script, system: node.system, log: false, }); return node; }); dispatch(setMakiTree(makiTree)); }; } export function setVolume(volume: number): ModernAction { return { type: "SET_VOLUME", volume }; }