@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
JavaScript
/*
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);
}
}