UNPKG

@ralphwetzel/node-red-mcu-plugin

Version:

Plugin to integrate Node-RED MCU Edition into the Node-RED Editor

682 lines (641 loc) 40.3 kB
/* part of the Moddable SDK https://github.com/phoddie/runmod/tree/master/html */ class XsbugConnection { constructor(uri) { this.requestID = 1; this.pending = []; } // transmit xsbug message doClearBreakpoint(path, line) { this.sendCommand(`<clear-breakpoint path="${path}" line="${line}"/>`); } doGo() { this.sendCommand("<go/>"); } doScript(msg) { this.sendCommand(`<script><![CDATA[${msg}]]></script>`); } doSetBreakpoint(path, line) { this.sendCommand(`<set-breakpoint path="${path}" line="${line}"/>`); } doSelect(value) { this.sendCommand(`<select id="${value}"/>`); } doSetAllBreakpoints(breakpoints = [], exceptions = true, start = false) { breakpoints = breakpoints.map(b => `<breakpoint path="${b.path}" line="${b.line}"/>`); if (exceptions) breakpoints.unshift('<breakpoint path="exceptions" line="0"/>') if (start) breakpoints.unshift('<breakpoint path="start" line="0"/>') this.sendCommand(`<set-all-breakpoints>${breakpoints.join("")}</set-all-breakpoints>`); } doStep() { this.sendCommand("<step/>"); } doStepInside() { this.sendCommand("<step-inside/>"); } doStepOutside() { this.sendCommand("<step-outside/>"); } doToggle(value) { this.sendCommand(`<toggle id="${value}"/>`); } // transmit host messages doGetPreference(domain, key, callback) { const byteLength = domain.length + 1 + key.length + 1; const payload = new Uint8Array(byteLength); let j = 0; for (let i = 0; i < domain.length; i++) payload[j++] = domain.charCodeAt(i); j++; for (let i = 0; i < key.length; i++) payload[j++] = key.charCodeAt(i); this.sendBinaryCommand(6, payload, callback); } doInstall(data, callback) { let offset = 0; let sendOne = (max) => { const use = Math.min(max, data.byteLength - offset); const payload = new Uint8Array(4 + use); payload[0] = (offset >> 24) & 0xff; payload[1] = (offset >> 16) & 0xff; payload[2] = (offset >> 8) & 0xff; payload[3] = offset & 0xff; payload.set(new Uint8Array(data, offset, use), 4); this.sendBinaryCommand(3, payload, function(code) { offset += max; if (offset >= data.byteLength) { if (callback) callback(code); return; } sendOne(1024); }); } sendOne(16); } doLoadModule(name, callback) { const payload = new Uint8Array(name.length + 1); for (let i = 0; i < name.length; i++) payload[i] = name.charCodeAt(i); this.sendBinaryCommand(10, payload, callback); } doRestart() { this.sendBinaryCommand(1); this.reset(); } doSetBaud(baud, callback) { const payload = new DataView(new ArrayBuffer(4)); payload.setUint32(0, baud, false); // big endian this.sendBinaryCommand(8, payload, msg => { this.usb.controlTransferOut({ requestType: 'vendor', recipient: 'device', request: 0x01, // SET_BAUDRATE index: 0x00, value: 3686400 / baud, }) .then(() => { if (callback) callback.call(this, msg); }, () => {debugger;}); }); } doSetPreference(domain, key, value) { // assumes 7 bit ASCII values const byteLength = domain.length + 1 + key.length + 1 + value.length + 1; const payload = new Uint8Array(byteLength); let j = 0; for (let i = 0; i < domain.length; i++) payload[j++] = domain.charCodeAt(i); j++; for (let i = 0; i < key.length; i++) payload[j++] = key.charCodeAt(i); j++; for (let i = 0; i < value.length; i++) payload[j++] = value.charCodeAt(i); this.sendBinaryCommand(4, payload); } doUninstall(callback) { this.sendBinaryCommand(2, undefined, callback); } // receive messages onBreak(msg) {} onLogin(msg) {} onInstrumentationConfigure(msg) {} onInstrumentationSamples(msg) {} onLocal(msg) {} onLog(msg) {} // helpers sendCommand(msg) { this.send(XsbugConnection.crlf + msg + XsbugConnection.crlf); } sendBinaryCommand(command, payload, callback) { // https://github.com/phoddie/runmod#commands if (payload) { if (!(payload instanceof ArrayBuffer)) payload = payload.buffer; } let needed = 1; if (payload) needed += 2 + payload.byteLength; else if (callback) needed += 2; const msg = new Uint8Array(needed); msg[0] = command; if (callback) { msg[1] = this.requestID >> 8; msg[2] = this.requestID & 0xff; this.pending.push({callback, id: this.requestID++}); } if (payload) msg.set(new Uint8Array(payload), 3); this.send(msg.buffer); } onReceive(data) { if ("string" === typeof data) { const msg = new XsbugMessage((new DOMParser).parseFromString(data, "application/xml")); console.log(msg); if (msg.break) this.onBreak(msg); else if (msg.login) this.onLogin(msg); else if (msg.instruments) this.onInstrumentationConfigure(msg); else if (msg.local) this.onLocal(msg); else if (msg.log) this.onLog(msg); else if (msg.samples) this.onInstrumentationSamples(msg); else debugger; // unhandled } else { const view = new DataView(data); switch (view.getUint8(0)) { case 5: const id = view.getUint16(1), code = view.getInt16(3); const index = this.pending.findIndex(pending => id === pending.id) if (index >= 0) { const pending = this.pending[index]; this.pending.splice(index, 1); (pending.callback)(code, data.slice(5)); } break; default: debugger; break; } } } } XsbugConnection.crlf = String.fromCharCode(13) + String.fromCharCode(10); class XsbugWebSocket extends XsbugConnection { constructor(uri) { super(); this.ws = new WebSocket(uri, "x-xsbug"); this.ws.onopen = this.onopen.bind(this); this.ws.onclose = this.onclose.bind(this); this.ws.onerror = this.onerror.bind(this); this.ws.onmessage = this.onmessage.bind(this); this.ws.binaryType = "arraybuffer"; } onopen() { console.log("WS OPEN"); } onclose() { if (this.ws) console.log("WS CLOSE"); } onerror() { if (this.ws) console.log("WS ERROR"); } onmessage(event) { console.log("WS RECEIVE " + event.data); this.onReceive(event.data); } send(data) { console.log("WS SEND " + data); return this.ws.send(data); } disconnect() { if (this.ws) this.ws.close(); delete this.ws; } } // SiLabs controlTransfer documentation: https://www.silabs.com/documents/public/application-notes/AN571.pdf const filters = [{ 'vendorId': 0x10c4, 'productId': 0xea60 }]; const DTR = Object.freeze({ CLEAR: 0, SET: 1, MASK: 1 << 8 }); const RTS = Object.freeze({ CLEAR: 0, SET: 2, MASK: 2 << 8 }); /* xsbug can send a lot of data. WebUSB doesn't seem to buffer much. The implementation keeps multiple reads pendinng to try to avoid dropped data. */ class XsbugUSB extends XsbugConnection { constructor(options = {}) { super(); this.baud = options.baud || 921600; this.dst = new Uint8Array(32768); this.connect(); } reset() { this.binary = false; this.dstIndex = 0; this.currentMachine = undefined; } async connect() { try { this.reset(); await this.getDevice(); await this.openDevice(); await this.readLoop(); } catch (e) { console.log("Connect error: ", e.message); if (e.NETWORK_ERR === e.code) console.log(" ** Looks like you need to uninstall the driver **"); } } async getDevice() { // let devices = await navigator.usb.getDevices(); // if (devices.length > 0) { // const usb = devices[0]; // this.usb = usb; // const endpoints = usb.configurations[0].interfaces[0].alternates[0].endpoints; // let inEndpoint, outEndpoint; // for (let i = 0; i < endpoints.length; i++) { // if ("out" === endpoints[i].direction) // outEndpoint = endpoints[i].endpointNumber; // if ("in" === endpoints[i].direction) // inEndpoint = endpoints[i].endpointNumber; // } // if ((undefined === inEndpoint) || (undefined === outEndpoint)) // throw new Error("can't find endpoints"); // this.inEndpoint = inEndpoint; // this.outEndpoint = outEndpoint; // return usb; // } // this.usb = await navigator.usb.requestDevice({ filters }); this.usb = await navigator.serial.requestPort({}); } async openDevice() { await this.usb.open(); await this.usb.selectConfiguration(1); console.log(this.usb); await this.usb.claimInterface(0); await this.usb.controlTransferOut({ requestType: 'vendor', recipient: 'device', request: 0x00, // IFC_ENABLE index: 0x00, value: 0x01 }); await this.usb.controlTransferOut({ requestType: 'vendor', recipient: 'device', request: 0x07, // SET_MHS index: 0x00, value: DTR.MASK | RTS.MASK | RTS.SET | DTR.CLEAR, }); await this.usb.controlTransferOut({ requestType: 'vendor', recipient: 'device', request: 0x01, // SET_BAUDRATE index: 0x00, value: 3686400 / this.baud, }); await this.usb.controlTransferOut({ requestType: 'vendor', recipient: 'device', request: 0x12, // PURGE index: 0x00, value: 0x0f, // transmit & receive }); await new Promise(resolve => setTimeout(resolve, 100)); await this.usb.controlTransferOut({ requestType: 'vendor', recipient: 'device', request: 0x07, // SET_MHS index: 0x00, value: RTS.MASK | RTS.CLEAR, }); } async readLoop() { try { const byteLength = 8192; const results = [ this.usb.transferIn(this.inEndpoint, byteLength), this.usb.transferIn(this.inEndpoint, byteLength), this.usb.transferIn(this.inEndpoint, byteLength), ]; let phase = 0; while (true) { const result = await results[phase]; results[phase] = this.usb.transferIn(this.inEndpoint, byteLength); phase = (phase + 1) % results.length; tracePacket("> ", result.data.buffer); this.usbReceive(new Uint8Array(result.data.buffer)); } } catch (e) { console.log("readLoop exception: " + e); } } async send(data) { if ("string" == typeof data) { const preamble = XsbugConnection.crlf + `<?xs.${this.currentMachine}?>` + XsbugConnection.crlf; data = new TextEncoder().encode(preamble + data); tracePacket("<", data); await this.usb.transferOut(this.outEndpoint, data); } else { let preamble = XsbugConnection.crlf + `<?xs#${this.currentMachine}?>`; preamble = new TextEncoder().encode(preamble); let payload = new Uint8Array(data); let buffer = new Uint8Array(preamble.length + 2 + payload.length); buffer.set(preamble, 0); buffer[preamble.length] = (payload.length >> 8) & 0xff; buffer[preamble.length + 1] = payload.length & 0xff; buffer.set(payload, preamble.length + 2); tracePacket("< ", buffer); await this.usb.transferOut(this.outEndpoint, buffer.buffer); } } async disconnect() { if (this.usb) { await this.usb.close(); delete this.usb; } } usbReceive(src) { const mxTagSize = 17; let dst = this.dst; let dstIndex = this.dstIndex; let srcIndex = 0, machine; while (srcIndex < src.length) { if (dstIndex === dst.length) { // grow buffer dst = new Uint8Array(dst.length + 32768); dst.set(this.dst); this.dst = dst; } dst[dstIndex++] = src[srcIndex++]; if (this.binary) { if (dstIndex < 2) this.binaryLength = dst[0] << 8; else if (2 === dstIndex) this.binaryLength |= dst[1]; if ((2 + this.binaryLength) === dstIndex) { this.onReceive(dst.slice(2, 2 + this.binaryLength).buffer); dstIndex = 0; this.binary = false; delete this.binaryLength; } } else if ((dstIndex >= 2) && (dst[dstIndex - 2] == 13) && (dst[dstIndex - 1] == 10)) { if ((dstIndex >= mxTagSize) && (machine = XsbugUSB.matchProcessingInstruction(dst.subarray(dstIndex - mxTagSize, dstIndex)))) { if (machine.flag) this.currentMachine = machine.value; else this.currentMachine = undefined; this.binary = machine.binary; } else if ((dstIndex >= 10) && (dst[dstIndex - 10] == '<'.charCodeAt()) && (dst[dstIndex - 9] == '/'.charCodeAt()) && (dst[dstIndex - 8] == 'x'.charCodeAt()) && (dst[dstIndex - 7] == 's'.charCodeAt()) && (dst[dstIndex - 6] == 'b'.charCodeAt()) && (dst[dstIndex - 5] == 'u'.charCodeAt()) && (dst[dstIndex - 4] == 'g'.charCodeAt()) && (dst[dstIndex - 3] == '>'.charCodeAt())) { const message = new TextDecoder().decode(dst.subarray(0, dstIndex)); console.log(message); this.onReceive(message); } else { dst[dstIndex - 2] = 0; //@@ if (offset > 2) fprintf(stderr, "%s\n", self->buffer); } dstIndex = 0; } } this.dstIndex = dstIndex; } static matchProcessingInstruction(dst) { let flag, binary = false, value = 0; if (dst[0] != '<'.charCodeAt()) return; if (dst[1] != '?'.charCodeAt()) return; if (dst[2] != 'x'.charCodeAt()) return; if (dst[3] != 's'.charCodeAt()) return; let c = dst[4]; if (c == '.'.charCodeAt()) flag = true; else if (c == '-'.charCodeAt()) flag = false; else if (c == '#'.charCodeAt()) { flag = true; binary = true; } else return; for (let i = 0; i < 8; i++) { c = dst[5 + i] if (('0'.charCodeAt() <= c) && (c <= '9'.charCodeAt())) value = (value * 16) + (c - '0'.charCodeAt()); else if (('a'.charCodeAt() <= c) && (c <= 'f'.charCodeAt())) value = (value * 16) + (10 + c - 'a'.charCodeAt()); else if (('A'.charCodeAt() <= c) && (c <= 'F'.charCodeAt())) value = (value * 16) + (10 + c - 'A'.charCodeAt()); else return; } if (dst[13] != '?'.charCodeAt()) return; if (dst[14] != '>'.charCodeAt()) return; return {value: value.toString(16).padStart(8, "0"), flag, binary}; } } class XsbugMessage { constructor(xml) { xml = xml.documentElement; if ("xsbug" !== xml.nodeName) throw new Error("not xsbug xml"); for (let node = xml.firstChild; node; node = node.nextSibling) { XsbugMessage[node.nodeName](this, node); } return; } // node parsers static login(message, node) { message.login = { name: node.attributes.name.value, value: node.attributes.value.value, }; } static samples(message, node) { message.samples = node.textContent.split(",").map(value => parseInt(value)); } static frames(message, node) { message.frames = []; for (node = node.firstChild; node; node = node.nextSibling) message.frames.push(XsbugMessage.oneFrame(node)); } static local(message, node) { const local = XsbugMessage.oneFrame(node); local.properties = []; for (node = node.firstChild; node; node = node.nextSibling) local.properties.push(XsbugMessage.oneProperty(node)); message.local = local; } static global(message, node) { message.global = []; for (node = node.firstChild; node; node = node.nextSibling) message.global.push(XsbugMessage.oneProperty(node)); message.global.sort((a, b) => a.name.localeCompare(b.name)); } static grammar(message, node) { message.module = []; for (node = node.firstChild; node; node = node.nextSibling) message.module.push(XsbugMessage.oneProperty(node)); message.module.sort((a, b) => a.name.localeCompare(b.name)); } static break(message, node) { message.break = { message: node.textContent, }; if (node.attributes.path) message.path = node.attributes.path.value; if (node.attributes.line) message.path = node.attributes.line.value; } static log(message, node) { message.log = node.textContent; } static instruments(message, node) { message.instruments = []; for (node = node.firstChild; node; node = node.nextSibling) { message.instruments.push({ name: node.attributes.name.value, value: node.attributes.value.value, }); } } // helpers static oneFrame(node) { const frame = { name: node.attributes.name.value, value: node.attributes.value.value, }; if (node.attributes.path) { frame.path = node.attributes.path.value; frame.line = parseInt(node.attributes.line.value); } return frame; } static oneProperty(node) { const flags = node.attributes.flags.value; const property = { name: node.attributes.name.value, flags: { value: flags, delete: flags.indexOf("C") < 0, enum: flags.indexOf("E") < 0, set: flags.indexOf("W") < 0, }, }; if (node.attributes.value) property.value = node.attributes.value.value; if (node.firstChild) { property.property = []; for (let p = node.firstChild; p; p = p.nextSibling) property.property.push(XsbugMessage.oneProperty(p)) property.property.sort((a, b) => a.name.localeCompare(b.name)); } return property; } } const httpGetXSA = Uint8Array.of(0x00, 0x00, 0x01, 0xF2, 0x58, 0x53, 0x5F, 0x41, 0x00, 0x00, 0x00, 0x0C, 0x56, 0x45, 0x52, 0x53, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x53, 0x49, 0x47, 0x4E, 0x26, 0x3B, 0x7F, 0x80, 0x47, 0x45, 0x8C, 0x6A, 0x09, 0x84, 0x18, 0x27, 0x43, 0x30, 0x81, 0x75, 0x00, 0x00, 0x00, 0x18, 0x43, 0x48, 0x4B, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87, 0x53, 0x59, 0x4D, 0x42, 0x0C, 0x00, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x00, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x00, 0x53, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x00, 0x72, 0x65, 0x73, 0x70, 0x6F, 0x6E, 0x73, 0x65, 0x00, 0x65, 0x74, 0x63, 0x00, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x00, 0x74, 0x72, 0x61, 0x63, 0x65, 0x00, 0x70, 0x61, 0x74, 0x68, 0x00, 0x68, 0x6F, 0x73, 0x74, 0x00, 0x63, 0x61, 0x6C, 0x6C, 0x62, 0x61, 0x63, 0x6B, 0x00, 0x6D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x00, 0x2F, 0x55, 0x73, 0x65, 0x72, 0x73, 0x2F, 0x68, 0x6F, 0x64, 0x64, 0x69, 0x65, 0x2F, 0x50, 0x72, 0x6F, 0x6A, 0x65, 0x63, 0x74, 0x73, 0x2F, 0x72, 0x75, 0x6E, 0x6D, 0x6F, 0x64, 0x2F, 0x6D, 0x6F, 0x64, 0x73, 0x2F, 0x68, 0x74, 0x74, 0x70, 0x67, 0x65, 0x74, 0x2F, 0x6D, 0x6F, 0x64, 0x2E, 0x6A, 0x73, 0x00, 0x00, 0x00, 0x01, 0x1F, 0x4D, 0x4F, 0x44, 0x53, 0x00, 0x00, 0x00, 0x10, 0x50, 0x41, 0x54, 0x48, 0x6D, 0x6F, 0x64, 0x2E, 0x78, 0x73, 0x62, 0x00, 0x00, 0x00, 0x01, 0x07, 0x43, 0x4F, 0x44, 0x45, 0x4A, 0x0B, 0x80, 0x6B, 0x0F, 0x00, 0x4E, 0x0B, 0x80, 0x2C, 0xD1, 0x00, 0x0C, 0x00, 0x89, 0x03, 0x91, 0x02, 0x4A, 0x0B, 0x80, 0x6B, 0x11, 0x00, 0xAB, 0x15, 0x6D, 0x61, 0x6B, 0x69, 0x6E, 0x67, 0x20, 0x68, 0x74, 0x74, 0x70, 0x20, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x0A, 0x00, 0x60, 0x01, 0xBC, 0x80, 0x06, 0x80, 0x59, 0x06, 0x80, 0x21, 0x7E, 0x6B, 0x13, 0x00, 0x77, 0x7C, 0x98, 0x04, 0x52, 0x04, 0xB0, 0x08, 0x80, 0x09, 0xAB, 0x10, 0x77, 0x77, 0x77, 0x2E, 0x65, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x2E, 0x63, 0x6F, 0x6D, 0x00, 0x76, 0x00, 0x52, 0x04, 0xB0, 0x07, 0x80, 0x09, 0xAB, 0x02, 0x2F, 0x00, 0x76, 0x00, 0x52, 0x04, 0xB0, 0x03, 0x80, 0x09, 0x80, 0x02, 0x80, 0x59, 0x02, 0x80, 0x76, 0x00, 0xBE, 0x01, 0x60, 0x01, 0x50, 0x02, 0x73, 0x67, 0x03, 0x7E, 0x6B, 0x14, 0x00, 0x50, 0x03, 0x32, 0xFF, 0xFF, 0x2B, 0x4F, 0x0C, 0x03, 0x89, 0x03, 0x75, 0x0A, 0x80, 0x75, 0x05, 0x80, 0x75, 0x04, 0x80, 0x4A, 0x0B, 0x80, 0x6B, 0x14, 0x00, 0x02, 0x00, 0xC2, 0x02, 0x7E, 0x02, 0x01, 0xC2, 0x03, 0x7E, 0x02, 0x02, 0xC2, 0x04, 0x7E, 0x6B, 0x16, 0x00, 0x60, 0x05, 0x52, 0x02, 0x42, 0x18, 0x22, 0x6B, 0x17, 0x00, 0x52, 0x03, 0x60, 0x01, 0xBC, 0x80, 0x06, 0x80, 0x59, 0x06, 0x80, 0x21, 0x7E, 0x6B, 0x18, 0x00, 0xAB, 0x02, 0x0A, 0x00, 0x60, 0x01, 0xBC, 0x80, 0x06, 0x80, 0x59, 0x06, 0x80, 0x21, 0x7E, 0x3D, 0x9A, 0x09, 0x80, 0x7E, 0x3D, 0x41, 0x7E, 0xB0, 0x00, 0x80, 0x6B, 0x0F, 0x00, 0xAB, 0x05, 0x68, 0x74, 0x74, 0x70, 0x00, 0xB0, 0x00, 0x80, 0x60, 0x03, 0xB8, 0xB0, 0x01, 0x80, 0x7A, 0x7A, 0x60, 0x03, 0xB8, 0x60, 0x03, 0x6D, 0x8F, 0x3D, 0x00, 0x00, 0x00, 0x08, 0x52, 0x53, 0x52, 0x43).buffer; const helloWorldXSA = Uint8Array.of(0x00, 0x00, 0x01, 0xA3, 0x58, 0x53, 0x5F, 0x41, 0x00, 0x00, 0x00, 0x0C, 0x56, 0x45, 0x52, 0x53, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x53, 0x49, 0x47, 0x4E, 0xFC, 0x65, 0x34, 0x94, 0x71, 0x9B, 0xE2, 0x03, 0x00, 0xC7, 0x02, 0x39, 0x05, 0xC6, 0xC1, 0x9B, 0x00, 0x00, 0x00, 0x18, 0x43, 0x48, 0x4B, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0x53, 0x59, 0x4D, 0x42, 0x06, 0x00, 0x78, 0x00, 0x2F, 0x6D, 0x63, 0x2F, 0x6D, 0x6F, 0x64, 0x2E, 0x6A, 0x73, 0x00, 0x74, 0x72, 0x61, 0x63, 0x65, 0x00, 0x63, 0x6F, 0x6E, 0x66, 0x69, 0x67, 0x00, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74, 0x00, 0x2F, 0x6D, 0x6F, 0x64, 0x64, 0x61, 0x62, 0x6C, 0x65, 0x2F, 0x62, 0x75, 0x69, 0x6C, 0x64, 0x2F, 0x74, 0x6D, 0x70, 0x2F, 0x77, 0x61, 0x73, 0x6D, 0x2F, 0x64, 0x65, 0x62, 0x75, 0x67, 0x2F, 0x6D, 0x63, 0x2F, 0x63, 0x68, 0x65, 0x63, 0x6B, 0x2E, 0x6A, 0x73, 0x00, 0x00, 0x00, 0x01, 0x00, 0x4D, 0x4F, 0x44, 0x53, 0x00, 0x00, 0x00, 0x10, 0x50, 0x41, 0x54, 0x48, 0x6D, 0x6F, 0x64, 0x2E, 0x78, 0x73, 0x62, 0x00, 0x00, 0x00, 0x00, 0x76, 0x43, 0x4F, 0x44, 0x45, 0x4A, 0x01, 0x80, 0x6B, 0x0F, 0x00, 0x4E, 0x01, 0x80, 0x2B, 0x5C, 0x0C, 0x00, 0x89, 0x02, 0x4A, 0x01, 0x80, 0x6B, 0x0F, 0x00, 0xAB, 0x0D, 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64, 0x0A, 0x00, 0x60, 0x01, 0xBC, 0x80, 0x02, 0x80, 0x59, 0x02, 0x80, 0x21, 0x7E, 0x6B, 0x11, 0x00, 0x75, 0x00, 0x80, 0x60, 0x00, 0x69, 0x02, 0x7E, 0x87, 0x02, 0x52, 0x02, 0x60, 0x0A, 0x65, 0x18, 0x21, 0x6B, 0x12, 0x00, 0x52, 0x02, 0xAB, 0x02, 0x0A, 0x00, 0x60, 0x02, 0xBC, 0x80, 0x02, 0x80, 0x59, 0x02, 0x80, 0x21, 0x7E, 0x87, 0x02, 0x6B, 0x11, 0x00, 0x52, 0x02, 0x5D, 0x98, 0x02, 0x7E, 0x15, 0xD8, 0xBE, 0x01, 0x3D, 0x41, 0x7E, 0x60, 0x01, 0x6D, 0x8F, 0x3D, 0x00, 0x00, 0x00, 0x12, 0x50, 0x41, 0x54, 0x48, 0x63, 0x68, 0x65, 0x63, 0x6B, 0x2E, 0x78, 0x73, 0x62, 0x00, 0x00, 0x00, 0x00, 0x60, 0x43, 0x4F, 0x44, 0x45, 0x4A, 0x05, 0x80, 0x6B, 0x03, 0x00, 0x4E, 0x05, 0x80, 0x2B, 0x23, 0x0C, 0x00, 0x89, 0x02, 0x91, 0x02, 0x4A, 0x05, 0x80, 0x6B, 0x05, 0x00, 0x32, 0x04, 0x80, 0x2B, 0x0B, 0x0C, 0x00, 0x4A, 0x05, 0x80, 0x6B, 0x06, 0x00, 0xBC, 0x7E, 0x3D, 0xC0, 0x03, 0x7E, 0x6B, 0x05, 0x00, 0x3D, 0x41, 0x7E, 0xB0, 0x03, 0x80, 0x6B, 0x03, 0x00, 0xAB, 0x0A, 0x6D, 0x63, 0x2F, 0x63, 0x6F, 0x6E, 0x66, 0x69, 0x67, 0x00, 0xB0, 0x04, 0x80, 0x60, 0x03, 0xB8, 0xB0, 0x04, 0x80, 0x7A, 0x7A, 0xB0, 0x04, 0x80, 0x60, 0x04, 0xB8, 0x60, 0x03, 0x6D, 0x8F, 0x3D, 0x00, 0x00, 0x00, 0x08, 0x52, 0x53, 0x52, 0x43).buffer; const ballsXSA = Uint8Array.of(0x00, 0x00, 0x0A, 0xAA, 0x58, 0x53, 0x5F, 0x41, 0x00, 0x00, 0x00, 0x0C, 0x56, 0x45, 0x52, 0x53, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x53, 0x49, 0x47, 0x4E, 0xDA, 0xC0, 0xB4, 0xFE, 0x46, 0xF4, 0x1E, 0x52, 0x40, 0xBA, 0x31, 0x96, 0x4F, 0x15, 0xB5, 0x8B, 0x00, 0x00, 0x00, 0x18, 0x43, 0x48, 0x4B, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x99, 0x53, 0x59, 0x4D, 0x42, 0x2E, 0x00, 0x24, 0x00, 0x78, 0x00, 0x79, 0x00, 0x42, 0x61, 0x6C, 0x6C, 0x42, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6F, 0x72, 0x00, 0x41, 0x70, 0x70, 0x6C, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x00, 0x74, 0x65, 0x6D, 0x70, 0x6C, 0x61, 0x74, 0x65, 0x00, 0x62, 0x61, 0x63, 0x6B, 0x67, 0x72, 0x6F, 0x75, 0x6E, 0x64, 0x53, 0x6B, 0x69, 0x6E, 0x00, 0x54, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x00, 0x64, 0x78, 0x00, 0x64, 0x79, 0x00, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x00, 0x6F, 0x6E, 0x54, 0x69, 0x6D, 0x65, 0x43, 0x68, 0x61, 0x6E, 0x67, 0x65, 0x64, 0x00, 0x64, 0x69, 0x73, 0x70, 0x6C, 0x61, 0x79, 0x4C, 0x69, 0x73, 0x74, 0x4C, 0x65, 0x6E, 0x67, 0x74, 0x68, 0x00, 0x62, 0x61, 0x6C, 0x6C, 0x54, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x00, 0x2F, 0x6D, 0x63, 0x2F, 0x6D, 0x6F, 0x64, 0x2E, 0x6A, 0x73, 0x00, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x00, 0x42, 0x61, 0x6C, 0x6C, 0x41, 0x70, 0x70, 0x6C, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x00, 0x62, 0x6F, 0x74, 0x74, 0x6F, 0x6D, 0x00, 0x6C, 0x65, 0x6E, 0x67, 0x74, 0x68, 0x00, 0x74, 0x6F, 0x70, 0x00, 0x6F, 0x6E, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x00, 0x63, 0x6F, 0x6E, 0x74, 0x65, 0x6E, 0x74, 0x73, 0x00, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74, 0x00, 0x6D, 0x6F, 0x76, 0x65, 0x42, 0x79, 0x00, 0x64, 0x65, 0x6C, 0x74, 0x61, 0x00, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x00, 0x43, 0x6F, 0x6E, 0x74, 0x65, 0x6E, 0x74, 0x00, 0x72, 0x69, 0x67, 0x68, 0x74, 0x00, 0x53, 0x6B, 0x69, 0x6E, 0x00, 0x62, 0x61, 0x6C, 0x6C, 0x53, 0x6B, 0x69, 0x6E, 0x00, 0x77, 0x69, 0x64, 0x74, 0x68, 0x00, 0x73, 0x74, 0x61, 0x74, 0x65, 0x00, 0x62, 0x61, 0x6C, 0x6C, 0x00, 0x73, 0x74, 0x61, 0x72, 0x74, 0x00, 0x66, 0x69, 0x6C, 0x6C, 0x00, 0x42, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6F, 0x72, 0x00, 0x6C, 0x65, 0x66, 0x74, 0x00, 0x74, 0x6F, 0x75, 0x63, 0x68, 0x43, 0x6F, 0x75, 0x6E, 0x74, 0x00, 0x63, 0x6F, 0x6E, 0x74, 0x61, 0x69, 0x6E, 0x65, 0x72, 0x00, 0x73, 0x6B, 0x69, 0x6E, 0x00, 0x6F, 0x6E, 0x44, 0x69, 0x73, 0x70, 0x6C, 0x61, 0x79, 0x69, 0x6E, 0x67, 0x00, 0x63, 0x6F, 0x6E, 0x66, 0x69, 0x67, 0x00, 0x66, 0x6F, 0x72, 0x6D, 0x61, 0x74, 0x00, 0x72, 0x6F, 0x74, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x00, 0x45, 0x72, 0x72, 0x6F, 0x72, 0x00, 0x2F, 0x6D, 0x6F, 0x64, 0x64, 0x61, 0x62, 0x6C, 0x65, 0x2F, 0x62, 0x75, 0x69, 0x6C, 0x64, 0x2F, 0x74, 0x6D, 0x70, 0x2F, 0x77, 0x61, 0x73, 0x6D, 0x2F, 0x64, 0x65, 0x62, 0x75, 0x67, 0x2F, 0x6D, 0x63, 0x2F, 0x63, 0x68, 0x65, 0x63, 0x6B, 0x2E, 0x6A, 0x73, 0x00, 0x00, 0x00, 0x06, 0x2E, 0x4D, 0x4F, 0x44, 0x53, 0x00, 0x00, 0x00, 0x10, 0x50, 0x41, 0x54, 0x48, 0x6D, 0x6F, 0x64, 0x2E, 0x78, 0x73, 0x62, 0x00, 0x00, 0x00, 0x04, 0xE6, 0x43, 0x4F, 0x44, 0x45, 0x4A, 0x0E, 0x80, 0x6B, 0x0F, 0x00, 0x4E, 0x0E, 0x80, 0x2C, 0x8A, 0x04, 0x0C, 0x00, 0x89, 0x0A, 0x91, 0x06, 0x4A, 0x0E, 0x80, 0x6B, 0x11, 0x00, 0x77, 0x7C, 0x98, 0x08, 0x52, 0x08, 0xB0, 0x22, 0x80, 0x09, 0xAB, 0x07, 0x73, 0x69, 0x6C, 0x76, 0x65, 0x72, 0x00, 0x76, 0x00, 0xBE, 0x01, 0x60, 0x01, 0x80, 0x1C, 0x80, 0x59, 0x1C, 0x80, 0x73, 0x2E, 0x02, 0x7E, 0x6B, 0x12, 0x00, 0xAB, 0x08, 0x6D, 0x6F, 0x64, 0x2E, 0x70, 0x6E, 0x67, 0x00, 0x60, 0x01, 0x80, 0x07, 0x80, 0x59, 0x07, 0x80, 0x73, 0x2E, 0x03, 0x7E, 0x6B, 0x13, 0x00, 0x77, 0x7C, 0x98, 0x08, 0x52, 0x08, 0xB0, 0x0A, 0x80, 0x09, 0x50, 0x03, 0x76, 0x00, 0x52, 0x08, 0xB0, 0x19, 0x80, 0x09, 0x77, 0x06, 0x98, 0x09, 0x52, 0x09, 0x60, 0x04, 0x9A, 0x12, 0x80, 0x7E, 0x60, 0x00, 0x52, 0x09, 0x3B, 0x54, 0x22, 0x80, 0x21, 0x7E, 0x52, 0x09, 0x60, 0x00, 0x09, 0xAB, 0x04, 0x72, 0x65, 0x64, 0x00, 0x9B, 0x7E, 0x52, 0x09, 0x60, 0x01, 0x09, 0xAB, 0x06, 0x67, 0x72, 0x65, 0x65, 0x6E, 0x00, 0x9B, 0x7E, 0x52, 0x09, 0x60, 0x02, 0x09, 0xAB, 0x05, 0x62, 0x6C, 0x75, 0x65, 0x00, 0x9B, 0x7E, 0x52, 0x09, 0x60, 0x03, 0x09, 0xAB, 0x05, 0x67, 0x72, 0x61, 0x79, 0x00, 0x9B, 0x7E, 0xBE, 0x01, 0x76, 0x00, 0x52, 0x08, 0xB0, 0x01, 0x80, 0x09, 0x60, 0x00, 0x76, 0x00, 0x52, 0x08, 0xB0, 0x02, 0x80, 0x09, 0x60, 0x00, 0x76, 0x00, 0x52, 0x08, 0xB0, 0x1E, 0x80, 0x09, 0x60, 0x20, 0x76, 0x00, 0x52, 0x08, 0xB0, 0x0F, 0x80, 0x09, 0x60, 0x20, 0x76, 0x00, 0xBE, 0x01, 0x60, 0x01, 0x80, 0x1C, 0x80, 0x59, 0x1C, 0x80, 0x73, 0x2E, 0x04, 0x7E, 0x6B, 0x15, 0x00, 0x77, 0x77, 0x74, 0x03, 0x80, 0x80, 0x23, 0x80, 0x59, 0x23, 0x80, 0x48, 0x98, 0x08, 0x6B, 0x16, 0x00, 0x32, 0xFF, 0xFF, 0x2B, 0x0E, 0x0E, 0x00, 0x4A, 0x0E, 0x80, 0x6B, 0x17, 0x00, 0x60, 0x00, 0xAE, 0x9E, 0x7E, 0x40, 0xB6, 0x98, 0x09, 0x27, 0x52, 0x09, 0x72, 0x03, 0x80, 0x52, 0x08, 0xB0, 0x14, 0x80, 0x09, 0x6B, 0x19, 0x00, 0x4E, 0xFF, 0xFF, 0x2B, 0x2F, 0x0C, 0x02, 0x89, 0x02, 0x75, 0x20, 0x80, 0x75, 0x18, 0x80, 0x4A, 0x0E, 0x80, 0x6B, 0x19, 0x00, 0x02, 0x00, 0xC2, 0x02, 0x7E, 0x02, 0x01, 0xC2, 0x03, 0x7E, 0x6B, 0x1A, 0x00, 0xB3, 0x52, 0x03, 0x9A, 0x08, 0x80, 0x7E, 0x6B, 0x1B, 0x00, 0xB3, 0x52, 0x03, 0x9A, 0x09, 0x80, 0x7E, 0x3D, 0x76, 0x14, 0x52, 0x08, 0xB0, 0x28, 0x80, 0x09, 0x6B, 0x1D, 0x00, 0x4E, 0xFF, 0xFF, 0x2B, 0x66, 0x0C, 0x01, 0x89, 0x01, 0x75, 0x20, 0x80, 0x4A, 0x0E, 0x80, 0x6B, 0x1D, 0x00, 0x02, 0x00, 0xC2, 0x02, 0x7E, 0x6B, 0x1E, 0x00, 0xB3, 0x52, 0x02, 0x54, 0x01, 0x80, 0x9A, 0x01, 0x80, 0x7E, 0x6B, 0x1F, 0x00, 0xB3, 0x52, 0x02, 0x54, 0x02, 0x80, 0x9A, 0x02, 0x80, 0x7E, 0x6B, 0x20, 0x00, 0xB3, 0x52, 0x02, 0x54, 0x26, 0x80, 0x54, 0x1E, 0x80, 0x52, 0x02, 0x54, 0x1E, 0x80, 0xAD, 0x9A, 0x1E, 0x80, 0x7E, 0x6B, 0x21, 0x00, 0xB3, 0x52, 0x02, 0x54, 0x26, 0x80, 0x54, 0x0F, 0x80, 0x52, 0x02, 0x54, 0x0F, 0x80, 0xAD, 0x9A, 0x0F, 0x80, 0x7E, 0x6B, 0x22, 0x00, 0x60, 0x00, 0x52, 0x02, 0x3B, 0x54, 0x21, 0x80, 0x21, 0x7E, 0x3D, 0x76, 0x14, 0x52, 0x08, 0xB0, 0x0B, 0x80, 0x09, 0x6B, 0x24, 0x00, 0x4E, 0xFF, 0xFF, 0x2C, 0xCC, 0x00, 0x0C, 0x01, 0x89, 0x05, 0x75, 0x20, 0x80, 0x4A, 0x0E, 0x80, 0x6B, 0x24, 0x00, 0x02, 0x00, 0xC2, 0x02, 0x7E, 0x75, 0x08, 0x80, 0xBC, 0xC2, 0x03, 0x7E, 0x75, 0x09, 0x80, 0xBC, 0xC2, 0x04, 0x7E, 0x75, 0x01, 0x80, 0xBC, 0xC2, 0x05, 0x7E, 0x75, 0x02, 0x80, 0xBC, 0xC2, 0x06, 0x7E, 0x6B, 0x25, 0x00, 0xB3, 0x54, 0x08, 0x80, 0xC2, 0x03, 0x7E, 0x6B, 0x26, 0x00, 0xB3, 0x54, 0x09, 0x80, 0xC2, 0x04, 0x7E, 0x6B, 0x27, 0x00, 0x52, 0x03, 0x52, 0x04, 0x60, 0x02, 0x52, 0x02, 0x3B, 0x54, 0x17, 0x80, 0x21, 0x7E, 0x6B, 0x28, 0x00, 0xB3, 0x54, 0x01, 0x80, 0x52, 0x03, 0x01, 0xC2, 0x05, 0x7E, 0x6B, 0x29, 0x00, 0xB3, 0x54, 0x02, 0x80, 0x52, 0x04, 0x01, 0xC2, 0x06, 0x7E, 0x6B, 0x2A, 0x00, 0x52, 0x05, 0x60, 0x00, 0x65, 0x3B, 0x1B, 0x08, 0x7E, 0x52, 0x05, 0xB3, 0x54, 0x1E, 0x80, 0x6F, 0x18, 0x05, 0x52, 0x03, 0x6C, 0x83, 0x03, 0x6B, 0x2B, 0x00, 0x52, 0x06, 0x60, 0x00, 0x65, 0x3B, 0x1B, 0x08, 0x7E, 0x52, 0x06, 0xB3, 0x54, 0x0F, 0x80, 0x6F, 0x18, 0x05, 0x52, 0x04, 0x6C, 0x83, 0x04, 0x6B, 0x2C, 0x00, 0xB3, 0x52, 0x03, 0x9A, 0x08, 0x80, 0x7E, 0x6B, 0x2D, 0x00, 0xB3, 0x52, 0x04, 0x9A, 0x09, 0x80, 0x7E, 0x6B, 0x2E, 0x00, 0xB3, 0x52, 0x05, 0x9A, 0x01, 0x80, 0x7E, 0x6B, 0x2F, 0x00, 0xB3, 0x52, 0x06, 0x9A, 0x02, 0x80, 0x7E, 0xBE, 0x04, 0x3D, 0x76, 0x14, 0x2E, 0x0A, 0xBE, 0x01, 0xBE, 0x02, 0x6B, 0x15, 0x00, 0x67, 0x05, 0x7E, 0x6B, 0x33, 0x00, 0x4E, 0xFF, 0xFF, 0x2C, 0x8B, 0x01, 0x0C, 0x01, 0x89, 0x07, 0x91, 0x03, 0x93, 0x94, 0x75, 0x00, 0x80, 0x4A, 0x0E, 0x80, 0x6B, 0x33, 0x00, 0x02, 0x00, 0xC2, 0x05, 0x7E, 0x6B, 0x33, 0x00, 0x77, 0x7C, 0x98, 0x06, 0x52, 0x06, 0xB0, 0x27, 0x80, 0x09, 0x6B, 0x34, 0x00, 0x50, 0x02, 0x76, 0x00, 0x52, 0x06, 0xB0, 0x15, 0x80, 0x09, 0x6B, 0x35, 0x00, 0x77, 0x06, 0x98, 0x07, 0x52, 0x07, 0x60, 0x04, 0x9A, 0x12, 0x80, 0x7E, 0x60, 0x00, 0x52, 0x07, 0x3B, 0x54, 0x22, 0x80, 0x21, 0x7E, 0x52, 0x07, 0x60, 0x00, 0x09, 0x6B, 0x36, 0x00, 0x60, 0x06, 0x77, 0x7C, 0x98, 0x08, 0x52, 0x08, 0xB0, 0x24, 0x80, 0x09, 0x60, 0x00, 0x76, 0x00, 0x52, 0x08, 0xB0, 0x13, 0x80, 0x09, 0x60, 0x00, 0x76, 0x00, 0x52, 0x08, 0xB0, 0x27, 0x80, 0x09, 0x50, 0x03, 0x76, 0x00, 0x52, 0x08, 0xB0, 0x1F, 0x80, 0x09, 0x60, 0x00, 0x76, 0x00, 0x52, 0x08, 0xB0, 0x23, 0x80, 0x09, 0x50, 0x04, 0x76, 0x00, 0xBE, 0x01, 0x60, 0x02, 0xBC, 0x80, 0x1A, 0x80, 0x59, 0x1A, 0x80, 0x21, 0x9B, 0x7E, 0x52, 0x07, 0x60, 0x01, 0x09, 0x6B, 0x37, 0x00, 0x60, 0x05, 0x77, 0x7C, 0x98, 0x08, 0x52, 0x08, 0xB0, 0x1B, 0x80, 0x09, 0x60, 0x00, 0x76, 0x00, 0x52, 0x08, 0xB0, 0x13, 0x80, 0x09, 0x60, 0x00, 0x76, 0x00, 0x52, 0x08, 0xB0, 0x27, 0x80, 0x09, 0x50, 0x03, 0x76, 0x00, 0x52, 0x08, 0xB0, 0x1F, 0x80, 0x09, 0x60, 0x01, 0x76, 0x00, 0x52, 0x08, 0xB0, 0x23, 0x80, 0x09, 0x50, 0x04, 0x76, 0x00, 0xBE, 0x01, 0x60, 0x02, 0xBC, 0x80, 0x1A, 0x80, 0x59, 0x1A, 0x80, 0x21, 0x9B, 0x7E, 0x52, 0x07, 0x60, 0x02, 0x09, 0x6B, 0x38, 0x00, 0x60, 0x04, 0x77, 0x7C, 0x98, 0x08, 0x52, 0x08, 0xB0, 0x1B, 0x80, 0x09, 0x60, 0x00, 0x76, 0x00, 0x52, 0x08, 0xB0, 0x11, 0x80, 0x09, 0x60, 0x00, 0x76, 0x00, 0x52, 0x08, 0xB0, 0x27, 0x80, 0x09, 0x50, 0x03, 0x76, 0x00, 0x52, 0x08, 0xB0, 0x1F, 0x80, 0x09, 0x60, 0x02, 0x76, 0x00, 0x52, 0x08, 0xB0, 0x23, 0x80, 0x09, 0x50, 0x04, 0x76, 0x00, 0xBE, 0x01, 0x60, 0x02, 0xBC, 0x80, 0x1A, 0x80, 0x59, 0x1A, 0x80, 0x21, 0x9B, 0x7E, 0x52, 0x07, 0x60, 0x03, 0x09, 0x6B, 0x39, 0x00, 0x60, 0x03, 0x77, 0x7C, 0x98, 0x08, 0x52, 0x08, 0xB0, 0x24, 0x80, 0x09, 0x60, 0x00, 0x76, 0x00, 0x52, 0x08, 0xB0, 0x11, 0x80, 0x09, 0x60, 0x00, 0x76, 0x00, 0x52, 0x08, 0xB0, 0x27, 0x80, 0x09, 0x50, 0x03, 0x76, 0x00, 0x52, 0x08, 0xB0, 0x1F, 0x80, 0x09, 0x60, 0x03, 0x76, 0x00, 0x52, 0x08, 0xB0, 0x23, 0x80, 0x09, 0x50, 0x04, 0x76, 0x00, 0xBE, 0x01, 0x60, 0x02, 0xBC, 0x80, 0x1A, 0x80, 0x59, 0x1A, 0x80, 0x21, 0x9B, 0x7E, 0xBE, 0x01, 0x76, 0x00, 0xBE, 0x01, 0x8F, 0x15, 0x00, 0x3E, 0x41, 0xA4, 0x02, 0xA4, 0x04, 0xA4, 0x05, 0xA6, 0x7E, 0x60, 0x01, 0x80, 0x04, 0x80, 0x59, 0x04, 0x80, 0x3B, 0x54, 0x05, 0x80, 0x21, 0x67, 0x06, 0x7E, 0x6B, 0x3D, 0x00, 0x7A, 0x77, 0x7C, 0x98, 0x08, 0x52, 0x08, 0xB0, 0x0C, 0x80, 0x09, 0x61, 0x00, 0x10, 0x76, 0x00, 0x52, 0x08, 0xB0, 0x25, 0x80, 0x09, 0x60, 0x00, 0x76, 0x00, 0xBE, 0x01, 0x60, 0x02, 0x50, 0x06, 0x73, 0x67, 0x07, 0x7E, 0x3D, 0x41, 0x7E, 0x7A, 0xAB, 0x07, 0x70, 0x69, 0x75, 0x2F, 0x4D, 0x43, 0x00, 0x7A, 0x60, 0x03, 0xB8, 0xB0, 0x06, 0x80, 0x7A, 0x7A, 0x60, 0x03, 0xB8, 0xB0, 0x0D, 0x80, 0x7A, 0x7A, 0x60, 0x03, 0xB8, 0xB0, 0x1D, 0x80, 0x7A, 0x7A, 0x60, 0x03, 0xB8, 0xB0, 0x03, 0x80, 0x7A, 0x7A, 0x60, 0x03, 0xB8, 0xB0, 0x10, 0x80, 0x7A, 0x7A, 0x60, 0x03, 0xB8, 0xB0, 0x16, 0x80, 0x7A, 0x7A, 0xB0, 0x16, 0x80, 0x60, 0x04, 0xB8, 0x60, 0x08, 0x6D, 0x8F, 0x3D, 0x00, 0x00, 0x00, 0x12, 0x50, 0x41, 0x54, 0x48, 0x63, 0x68, 0x65, 0x63, 0x6B, 0x2E, 0x78, 0x73, 0x62, 0x00, 0x00, 0x00, 0x01, 0x1E, 0x43, 0x4F, 0x44, 0x45, 0x4A, 0x2D, 0x80, 0x6B, 0x03, 0x00, 0x4E, 0x2D, 0x80, 0x2C, 0xE0, 0x00, 0x0C, 0x00, 0x89, 0x02, 0x91, 0x02, 0x4A, 0x2D, 0x80, 0x6B, 0x05, 0x00, 0x32, 0x16, 0x80, 0x2C, 0xC3, 0x00, 0x0C, 0x00, 0x89, 0x01, 0x91, 0x01, 0x4A, 0x2D, 0x80, 0x6B, 0x06, 0x00, 0x50, 0x02, 0x54, 0x2A, 0x80, 0xAB, 0x09, 0x52, 0x47, 0x42, 0x35, 0x36, 0x35, 0x4C, 0x45, 0x00, 0x79, 0x18, 0x50, 0x6B, 0x07, 0x00, 0xAB, 0x23, 0x69, 0x6E, 0x63, 0x6F, 0x6D, 0x70, 0x61, 0x74, 0x69, 0x62, 0x6C, 0x65, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x3A, 0x20, 0x70, 0x69, 0x78, 0x65, 0x6C, 0x66, 0x6F, 0x72, 0x6D, 0x61, 0x74, 0x3A, 0x20, 0x00, 0x50, 0x02, 0x54, 0x2A, 0x80, 0x01, 0xAB, 0x15, 0x20, 0x69, 0x6E, 0x73, 0x74, 0x65, 0x61, 0x64, 0x20, 0x6F, 0x66, 0x20, 0x52, 0x47, 0x42, 0x35, 0x36, 0x35, 0x4C, 0x45, 0x00, 0x01, 0x60, 0x01, 0x80, 0x2C, 0x80, 0x59, 0x2C, 0x80, 0x73, 0xB4, 0x6B, 0x08, 0x00, 0x50, 0x02, 0x54, 0x2B, 0x80, 0x60, 0x00, 0x79, 0x18, 0x46, 0x6B, 0x09, 0x00, 0xAB, 0x20, 0x69, 0x6E, 0x63, 0x6F, 0x6D, 0x70, 0x61, 0x74, 0x69, 0x62, 0x6C, 0x65, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x3A, 0x20, 0x72, 0x6F, 0x74, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x3A, 0x20, 0x00, 0x50, 0x02, 0x54, 0x2B, 0x80, 0x01, 0xAB, 0x0E, 0x20, 0x69, 0x6E, 0x73, 0x74, 0x65, 0x61, 0x64, 0x20, 0x6F, 0x66, 0x20, 0x30, 0x00, 0x01, 0x60, 0x01, 0x80, 0x2C, 0x80, 0x59, 0x2C, 0x80, 0x73, 0xB4, 0x3D, 0x41, 0xA4, 0x02, 0x7E, 0xC0, 0x03, 0x7E, 0x6B, 0x05, 0x00, 0x3D, 0x41, 0x7E, 0xB0, 0x29, 0x80, 0x6B, 0x03, 0x00, 0xAB, 0x0A, 0x6D, 0x63, 0x2F, 0x63, 0x6F, 0x6E, 0x66, 0x69, 0x67, 0x00, 0xB0, 0x16, 0x80, 0x60, 0x03, 0xB8, 0xB0, 0x16, 0x80, 0x7A, 0x7A, 0xB0, 0x16, 0x80, 0x60, 0x04, 0xB8, 0x60, 0x03, 0x6D, 0x8F, 0x3D, 0x00, 0x00, 0x02, 0x9F, 0x52, 0x53, 0x52, 0x43, 0x00, 0x00, 0x00, 0x19, 0x50, 0x41, 0x54, 0x48, 0x6D, 0x6F, 0x64, 0x2D, 0x61, 0x6C, 0x70, 0x68, 0x61, 0x2E, 0x62, 0x6D, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x7E, 0x44, 0x41, 0x54, 0x41, 0x42, 0x4D, 0x76, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x12, 0x0B, 0x00, 0x00, 0x12, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x11, 0x11, 0x00, 0x22, 0x22, 0x22, 0x00, 0x33, 0x33, 0x33, 0x00, 0x44, 0x44, 0x44, 0x00, 0x55, 0x55, 0x55, 0x00, 0x66, 0x66, 0x66, 0x00, 0x77, 0x77, 0x77, 0x00, 0x88, 0x88, 0x88, 0x00, 0x99, 0x99, 0x99, 0x00, 0xAA, 0xAA, 0xAA, 0x00, 0xBB, 0xBB, 0xBB, 0x00, 0xCC, 0xCC, 0xCC, 0x00, 0xDD, 0xDD, 0xDD, 0x00, 0xEE, 0xEE, 0xEE, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xD9, 0x63, 0x10, 0x01, 0x36, 0x9D, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xB6, 0x10, 0x00, 0x00, 0x00, 0x00, 0x01, 0x6B, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xD7, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x7D, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFB, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xB3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3B, 0xFF, 0xFF, 0xFF, 0xFB, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xBF, 0xFF, 0xFF, 0xD4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4D, 0xFF, 0xFF, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFB, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xBF, 0xF6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6F, 0xD1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1D, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0xD1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1D, 0xF6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6F, 0xFB, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xBF, 0xFF, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0xD4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4D, 0xFF, 0xFF, 0xFB, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xBF, 0xFF, 0xFF, 0xFF, 0xB3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3B, 0xFF, 0xFF, 0xFF, 0xFF, 0xFB, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xD7, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x7D, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xB6, 0x10, 0x00, 0x00, 0x00, 0x00, 0x01, 0x6B, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xD9, 0x63, 0x10, 0x01, 0x36, 0x9D, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF).buffer; function onLogin(msg) { this.onBreak = onBreak; this.doSetAllBreakpoints([{path: "/Users/hoddie/Projects/moddable/examples/network/websocket/websocketserver/main.js", line: 51}]); this.doStep(); this.doSetPreference("config", "when", "boot"); this.doGetPreference("config", "when", function(code, data) { if (code) { console.log("can't get config/when pref"); return; } let result = ""; data = new Uint8Array(data); for (let i = 0; i < data.byteLength - 1; i++) result += String.fromCharCode(data[i]); console.log(`config/when pref is "${result}"`); }); setTimeout(function() { console.log("START INSTALL."); xsb.onBreak = function() {} xsb.doStep(); xsb.doInstall(helloWorldXSA, function() { console.log("INSTALL COMPLETE. RESTART."); xsb.doRestart(); }); }, 10 * 1000); } function onBreak(msg) { this.doGo(); } let xsb; // document.querySelector("#usb-connect-button").addEventListener("click", () => { // if (xsb) { // xsb.disconnect(); // xsb = undefined; // } // xsb = new XsbugUSB({baud: 921600 / 2}); // xsb.onLogin = onLogin; // }); // document.querySelector("#wifi-connect-button").addEventListener("click", () => { // if (xsb) { // xsb.disconnect(); // xsb = undefined; // } // xsb = new XsbugWebSocket("ws://runmod.local:8080"); // xsb.onLogin = onLogin; // }); function tracePacket(prefix, bytes) { return; for (let i = 0; i < bytes.length; i += 16) { let line = prefix; let end = i + 16; if (end > bytes.length) end = bytes.length; for (let j = i; j < end; j++) { let byte = bytes[j].toString(16); if (byte.length < 2) byte = "0" + byte; line += byte + " "; } line += " "; for (let j = i; j < end; j++) { let byte = bytes[j]; if ((32 <= byte) && (byte < 128)) line += String.fromCharCode(byte); else line += "."; } console.log(line); } }