summaryrefslogtreecommitdiffstats
path: root/docs/release/scripts/minimaws/lib/assets
diff options
context:
space:
mode:
Diffstat (limited to 'docs/release/scripts/minimaws/lib/assets')
-rw-r--r--docs/release/scripts/minimaws/lib/assets/common.js149
-rw-r--r--docs/release/scripts/minimaws/lib/assets/digest.js234
-rw-r--r--docs/release/scripts/minimaws/lib/assets/disclosedown.svg4
-rw-r--r--docs/release/scripts/minimaws/lib/assets/discloseup.svg4
-rw-r--r--docs/release/scripts/minimaws/lib/assets/machine.js628
-rw-r--r--docs/release/scripts/minimaws/lib/assets/romident.js641
-rw-r--r--docs/release/scripts/minimaws/lib/assets/sortasc.pngbin0 -> 1157 bytes
-rw-r--r--docs/release/scripts/minimaws/lib/assets/sortasc.svg4
-rw-r--r--docs/release/scripts/minimaws/lib/assets/sortdesc.pngbin0 -> 1195 bytes
-rw-r--r--docs/release/scripts/minimaws/lib/assets/sortdesc.svg4
-rw-r--r--docs/release/scripts/minimaws/lib/assets/sortind.pngbin0 -> 1247 bytes
-rw-r--r--docs/release/scripts/minimaws/lib/assets/sortind.svg5
-rw-r--r--docs/release/scripts/minimaws/lib/assets/style.css18
13 files changed, 1691 insertions, 0 deletions
diff --git a/docs/release/scripts/minimaws/lib/assets/common.js b/docs/release/scripts/minimaws/lib/assets/common.js
new file mode 100644
index 00000000000..7439aaa5a21
--- /dev/null
+++ b/docs/release/scripts/minimaws/lib/assets/common.js
@@ -0,0 +1,149 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+
+function make_collapsible(heading, section)
+{
+ var hidden = false;
+ var display = section.style.display;
+ var icon = heading.insertBefore(document.createElement('img'), heading.firstChild);
+ icon.setAttribute('src', assetsurl + '/disclosedown.svg');
+ icon.setAttribute('class', 'disclosure');
+ icon.addEventListener(
+ 'click',
+ function (event)
+ {
+ hidden = !hidden;
+ if (hidden)
+ {
+ icon.setAttribute('src', assetsurl + '/discloseup.svg');
+ section.style.display = 'none';
+ }
+ else
+ {
+ icon.setAttribute('src', assetsurl + '/disclosedown.svg');
+ section.style.display = display;
+ var headingtop = 0;
+ for (var element = heading; element !== null; element = element.offsetParent)
+ headingtop += element.offsetTop;
+ var sectionbot = section.offsetHeight;
+ for (var element = section; element !== null; element = element.offsetParent)
+ sectionbot += element.offsetTop;
+ if ((window.pageYOffset + window.innerHeight) < sectionbot)
+ {
+ if ((sectionbot - headingtop) > window.innerHeight)
+ heading.scrollIntoView(true);
+ else
+ window.scroll(window.pageXOffset, sectionbot - window.innerHeight);
+ }
+ }
+ });
+
+ return function ()
+ {
+ hidden = true;
+ icon.setAttribute('src', assetsurl + '/discloseup.svg');
+ section.style.display = 'none';
+ };
+}
+
+
+function make_table_sortable(tbl)
+{
+ var sorticons = new Array(tbl.tHead.rows[0].cells.length);
+
+ function TableSortHelper(i)
+ {
+ this.column = i;
+ this.header = tbl.tHead.rows[0].cells[i];
+ this.sorted = false;
+ this.direction = 1;
+
+ var container = this.header.appendChild(document.createElement('div'));
+ container.setAttribute('class', 'sorticon');
+
+ this.icon = container.appendChild(document.createElement('img'));
+ this.icon.setAttribute('src', assetsurl + '/sortind.svg');
+ this.icon.addEventListener('click', this.icon_click.bind(this));
+ }
+
+ TableSortHelper.prototype = (function ()
+ {
+ function set_unsorted(obj)
+ {
+ obj.sorted = false;
+ obj.icon.setAttribute('src', assetsurl + '/sortind.svg');
+ }
+
+ function sort_table(obj)
+ {
+ var c = obj.header.cellIndex;
+ var numeric = obj.header.getAttribute('class') == 'numeric';
+ var tbody = tbl.tBodies[0];
+ var trows;
+ if (numeric)
+ {
+ trows = Array.prototype.slice.call(tbody.rows, 0).sort(
+ function (x, y)
+ {
+ return obj.direction * (parseFloat(x.cells[c].textContent) - parseFloat(y.cells[c].textContent));
+ });
+ }
+ else
+ {
+ trows = Array.prototype.slice.call(tbody.rows, 0).sort(
+ function (x, y)
+ {
+ return obj.direction * x.cells[c].textContent.localeCompare(y.cells[c].textContent);
+ });
+ }
+ trows.forEach(function (row) { tbody.appendChild(row); });
+ }
+
+ return {
+ icon_click : function (event)
+ {
+ if (this.sorted)
+ this.direction = -this.direction;
+ if (this.direction < 0)
+ this.icon.setAttribute('src', assetsurl + '/sortdesc.svg');
+ else
+ this.icon.setAttribute('src', assetsurl + '/sortasc.svg');
+ this.sorted = true;
+ for (var i = 0; i < sorticons.length; i++)
+ {
+ if (i != this.column)
+ set_unsorted(sorticons[i]);
+ }
+ sort_table(this);
+ }
+ };
+ })();
+
+ for (var i = 0; i < sorticons.length; i++)
+ sorticons[i] = new TableSortHelper(i);
+}
+
+
+function make_restore_default_handler(popup, index)
+{
+ return function (event)
+ {
+ if (popup.selectedIndex != index)
+ {
+ popup.selectedIndex = index;
+ popup.dispatchEvent(new Event('change'));
+ }
+ }
+}
+
+
+function make_restore_default_button(title, id, popup, index)
+{
+ var btn = document.createElement('button');
+ btn.setAttribute('id', id);
+ btn.setAttribute('type', 'button');
+ btn.disabled = popup.selectedIndex == index;
+ btn.textContent = title;
+ btn.onclick = make_restore_default_handler(popup, index);
+ return btn;
+}
diff --git a/docs/release/scripts/minimaws/lib/assets/digest.js b/docs/release/scripts/minimaws/lib/assets/digest.js
new file mode 100644
index 00000000000..6739d1440d9
--- /dev/null
+++ b/docs/release/scripts/minimaws/lib/assets/digest.js
@@ -0,0 +1,234 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+
+function Crc32Digester()
+{
+ this.crc = 0xffffffff;
+}
+
+Crc32Digester.prototype = (function ()
+ {
+ var table = new Uint32Array(256);
+ (function ()
+ {
+ for (var i = 0; i < 256; i++)
+ {
+ var crc = i;
+ for (var b = 0; b < 8; b++)
+ crc = (crc >>> 1) ^ ((crc & 1) ? 0xedb88320 : 0x00000000);
+ table[i] = crc;
+ }
+ })();
+
+ return {
+ digest: function (data, view)
+ {
+ for (var i = 0; i < view.length; i++)
+ this.crc = (this.crc >>> 8) ^ table[(this.crc & 0x000000ff) ^ view[i]];
+ },
+
+ finalise: function ()
+ {
+ return this.crc ^ 0xffffffff;
+ }
+ };
+ })();
+
+
+function Sha1Digester()
+{
+ this.st = new Uint32Array(5);
+ this.st[0] = 0xc3d2e1f0;
+ this.st[1] = 0x10325476;
+ this.st[2] = 0x98badcfe;
+ this.st[3] = 0xefcdab89;
+ this.st[4] = 0x67452301;
+
+ this.cnt = new Uint32Array(2);
+ this.cnt[0] = 0;
+ this.cnt[1] = 0;
+
+ this.buf = new DataView(new ArrayBuffer(64));
+}
+
+Sha1Digester.prototype = (function ()
+ {
+ function rol(x, n)
+ {
+ return ((x << n) | (x >>> (32 - n))) & 0xffffffff;
+ }
+
+ function b(data, i)
+ {
+ var r = data.getUint32(((i + 13) & 15) << 2, false);
+ r ^= data.getUint32(((i + 8) & 15) << 2, false);
+ r ^= data.getUint32(((i + 2) & 15) << 2, false);
+ r ^= data.getUint32((i & 15) << 2, false);
+ r = rol(r, 1);
+ data.setUint32((i & 15) << 2, r, false);
+ return r;
+ }
+
+ function r0(data, d, i)
+ {
+ d[i % 5] = 0xffffffff & (d[i % 5] + ((d[(i + 3) % 5] & (d[(i + 2) % 5] ^ d[(i + 1) % 5])) ^ d[(i + 1) % 5]) + data.getUint32(i << 2, false) + 0x5a827999 + rol(d[(i + 4) % 5], 5));
+ d[(i + 3) % 5] = rol(d[(i + 3) % 5], 30);
+ }
+
+ function r1(data, d, i)
+ {
+ d[i % 5] = 0xffffffff & (d[i % 5] + ((d[(i + 3) % 5] & (d[(i + 2) % 5] ^ d[(i + 1) % 5])) ^ d[(i + 1) % 5])+ b(data, i) + 0x5a827999 + rol(d[(i + 4) % 5], 5));
+ d[(i + 3) % 5] = rol(d[(i + 3) % 5], 30);
+ }
+
+ function r2(data, d, i)
+ {
+ d[i % 5] = 0xffffffff & (d[i % 5] + (d[(i + 3) % 5] ^ d[(i + 2) % 5] ^ d[(i + 1) % 5]) + b(data, i) + 0x6ed9eba1 + rol(d[(i + 4) % 5], 5));
+ d[(i + 3) % 5] = rol(d[(i + 3) % 5], 30);
+ }
+
+ function r3(data, d, i)
+ {
+ d[i % 5] = 0xffffffff & (d[i % 5] + (((d[(i + 3) % 5] | d[(i + 2) % 5]) & d[(i + 1) % 5]) | (d[(i + 3) % 5] & d[(i + 2) % 5])) + b(data, i) + 0x8f1bbcdc + rol(d[(i + 4) % 5], 5));
+ d[(i + 3) % 5] = rol(d[(i + 3) % 5], 30);
+ }
+
+ function r4(data, d, i)
+ {
+ d[i % 5] = 0xffffffff & (d[i % 5] + (d[(i + 3) % 5] ^ d[(i + 2) % 5] ^ d[(i + 1) % 5]) + b(data, i) + 0xca62c1d6 + rol(d[(i + 4) % 5], 5));
+ d[(i + 3) % 5] = rol(d[(i + 3) % 5], 30);
+ }
+
+ function process(st, data)
+ {
+ var d = new Uint32Array(st);
+ var i = 0;
+ while (i < 16)
+ r0(data, d, i++);
+ while (i < 20)
+ r1(data, d, i++);
+ while (i < 40)
+ r2(data, d, i++);
+ while (i < 60)
+ r3(data, d, i++);
+ while (i < 80)
+ r4(data, d, i++);
+ for (i = 0; i < st.length; i++)
+ st[i] = (st[i] + d[i]) & 0xffffffff;
+ }
+
+ return {
+ digest: function (data, view)
+ {
+ var residual = this.cnt[0];
+ this.cnt[0] = (this.cnt[0] + (view.length << 3)) & 0xffffffff;
+ if (residual > this.cnt[0])
+ this.cnt[1]++;
+ this.cnt[1] += (view.length >>> 29);
+ residual = (residual >>> 3) & 63;
+ var offset = 0;
+ if ((residual + view.length) >= 64)
+ {
+ if (residual > 0)
+ {
+ for (offset = 0; (offset + residual) < 64; offset++)
+ this.buf.setUint8(offset + residual, view[offset]);
+ process(this.st, this.buf);
+ residual = 0;
+ }
+ for ( ; (view.length - offset) >= 64; offset += 64)
+ process(this.st, new DataView(data, offset, 64));
+ }
+ for ( ; offset < view.length; residual++, offset++)
+ this.buf.setUint8(residual, view[offset]);
+ },
+
+ finalise : function ()
+ {
+ var lenbuf = new ArrayBuffer(8);
+ var lenview = new DataView(lenbuf);
+ lenview.setUint32(0, this.cnt[1], false);
+ lenview.setUint32(4, this.cnt[0], false);
+ var padbuf = new ArrayBuffer(64 - (63 & ((this.cnt[0] >>> 3) + 8)));
+ var padview = new Uint8Array(padbuf);
+ padview[0] = 0x80;
+ for (var i = 1; i < padview.length; i++)
+ padview[i] = 0x00;
+ this.digest(padbuf, padview);
+ this.digest(lenbuf, new Uint8Array(lenbuf));
+ var result = new Array(20);
+ for (var i = 0; i < 20; i++)
+ result[i] = (this.st[4 - (i >>> 2)] >> ((3 - (i & 3)) << 3)) & 0x000000ff;
+ return result.map(x => x.toString(16).padStart(2, '0')).join('');
+ }
+ };
+ })();
+
+
+function StuckBitsDigester(max = 4)
+{
+ this.offset = 0;
+ this.state = [ ];
+ for (var i = 0; i <= max; i++)
+ {
+ var bytes = 1 << i;
+ this.state.push({ filled: 0, bits: new Uint8Array(bytes), mask: new Uint8Array(bytes) });
+ }
+}
+
+StuckBitsDigester.prototype = (function ()
+ {
+ return {
+ digest: function (data, view)
+ {
+ for (var i = 0; i < view.length; i++)
+ {
+ var data = view[i];
+ for (var j = 0; j < this.state.length; j++)
+ {
+ var detail = this.state[j];
+ var bytes = 1 << j;
+ var o = this.offset % bytes;
+ var f = o == (bytes - 1);
+ if (!detail.filled)
+ {
+ detail.bits[o] = data;
+ detail.mask[o] = 0xff;
+ if (f)
+ detail.filled = 1;
+ }
+ else
+ {
+ detail.mask[o] &= ~(data ^ detail.bits[o]);
+ detail.bits[o] &= detail.mask[o];
+ }
+ if (o == (bytes - 1))
+ detail.filled += 1;
+ }
+ this.offset = (this.offset + 1) % (1 << (this.state.length - 1));
+ }
+ },
+
+ finalise: function ()
+ {
+ var result = [ ];
+ var lastresult = null;
+ for (var i = 0; (i < this.state.length) && (this.state[i].filled >= 4); i++)
+ {
+ var detail = this.state[i];
+ var bytes = 1 << i;
+ for (var j = 0; j < bytes; j++)
+ {
+ var mask = detail.mask[j];
+ if (mask && ((lastresult === null) || (mask != lastresult.mask[j % (1 << (lastresult.mask.length - 1))])))
+ {
+ lastresult = { bits: detail.bits, mask: detail.mask };
+ result.push(lastresult);
+ break;
+ }
+ }
+ }
+ return result;
+ }
+ };
+ })();
diff --git a/docs/release/scripts/minimaws/lib/assets/disclosedown.svg b/docs/release/scripts/minimaws/lib/assets/disclosedown.svg
new file mode 100644
index 00000000000..bb66c7f5de9
--- /dev/null
+++ b/docs/release/scripts/minimaws/lib/assets/disclosedown.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1" height="16" width="16">
+ <path d="m 0,5 h 16 l -8,10 z" />
+</svg>
diff --git a/docs/release/scripts/minimaws/lib/assets/discloseup.svg b/docs/release/scripts/minimaws/lib/assets/discloseup.svg
new file mode 100644
index 00000000000..010065a446b
--- /dev/null
+++ b/docs/release/scripts/minimaws/lib/assets/discloseup.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1" height="16" width="16">
+ <path d="m 5,0 v 16 l 10,-8 z" />
+</svg>
diff --git a/docs/release/scripts/minimaws/lib/assets/machine.js b/docs/release/scripts/minimaws/lib/assets/machine.js
new file mode 100644
index 00000000000..b7f600f04d6
--- /dev/null
+++ b/docs/release/scripts/minimaws/lib/assets/machine.js
@@ -0,0 +1,628 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+
+var slot_info = Object.create(null);
+var bios_sets = Object.create(null);
+var machine_flags = Object.create(null);
+var softwarelist_info = Object.create(null);
+
+
+function make_slot_popup_id(name) { return ('select-slot-choice-' + name).replace(/:/g, '-'); }
+function get_slot_popup(name) { return document.getElementById(make_slot_popup_id(name)); }
+
+
+function update_cmd_preview()
+{
+ var inifmt = document.getElementById('select-options-format').value == 'ini';
+ var result = '';
+ var first = true;
+ function add_option(flag, value)
+ {
+ if (first)
+ first = false;
+ else if (inifmt)
+ result += '\n';
+ else
+ result += ' ';
+
+ if (inifmt)
+ {
+ result += flag + ' ';
+ if (flag.length < 25)
+ result += ' '.repeat(25 - flag.length);
+ result += value;
+ }
+ else
+ {
+ result += '-' + flag + ' ';
+ if (value == '')
+ result += '""';
+ else
+ result += value;
+ }
+ }
+ var elide_defaults = !document.getElementById('check-explicit-defaults').checked;
+
+ // add system BIOS if applicable
+ var sysbios = document.getElementById('select-system-bios');
+ if (sysbios && (!elide_defaults || (sysbios.selectedOptions[0].getAttribute('data-isdefault') != 'yes')))
+ add_option('bios', sysbios.value);
+
+ // add RAM option if applicable
+ var ramopt = document.getElementById('select-ram-option');
+ if (ramopt && (!elide_defaults || (ramopt.selectedOptions[0].getAttribute('data-isdefault') != 'yes')))
+ add_option('ramsize', ramopt.value);
+
+ var slotslist = document.getElementById('list-slot-options');
+ if (slotslist)
+ {
+ for (var item = slotslist.firstChild; item; item = item.nextSibling)
+ {
+ if (item.nodeName == 'DT')
+ {
+ // need to set the slot option if it has non-default card and/or non-default card BIOS
+ var slotname = item.getAttribute('data-slotname');
+ var selection = get_slot_popup(slotname).selectedOptions[0];
+ var biospopup = document.getElementById(('select-slot-bios-' + slotname).replace(/:/g, '-'));
+ var defcard = selection.getAttribute('data-isdefault') == 'yes';
+ var defbios = !biospopup || (biospopup.selectedOptions[0].getAttribute('data-isdefault') == 'yes');
+ if (!elide_defaults || !defcard || !defbios)
+ {
+ var card = selection.value;
+ add_option(slotname, card + ((biospopup && (!elide_defaults || !defbios)) ? (',bios=' + biospopup.value) : ''));
+ }
+ }
+ }
+ }
+
+ // replace the preview with appropriate element
+ var target = document.getElementById('para-cmd-preview');
+ var replacement = document.createElement(inifmt ? 'pre' : 'tt');
+ replacement.setAttribute('id', 'para-cmd-preview');
+ replacement.textContent = result;
+ target.parentNode.replaceChild(replacement, target);
+}
+
+
+function set_default_system_bios()
+{
+ // search for an explicit default option
+ var sysbios = document.getElementById('select-system-bios');
+ var len = sysbios.options.length;
+ for (var i = 0; i < len; i++)
+ {
+ if (sysbios.options[i].getAttribute('data-isdefault') == 'yes')
+ {
+ // select it and add a button for restoring it
+ sysbios.selectedIndex = i;
+ var dflt = make_restore_default_button('default', 'btn-def-system-bios', sysbios, i);
+ sysbios.onchange = make_slot_bios_change_handler(dflt);
+ sysbios.parentNode.appendChild(document.createTextNode(' '));
+ sysbios.parentNode.appendChild(dflt);
+ break;
+ }
+ }
+ update_cmd_preview();
+}
+
+
+function set_default_ram_option()
+{
+ // search for an explicit default option
+ var ramopt = document.getElementById('select-ram-option');
+ var len = ramopt.options.length;
+ for (var i = 0; i < len; i++)
+ {
+ if (ramopt.options[i].getAttribute('data-isdefault') == 'yes')
+ {
+ // select it and add a button for restoring it
+ ramopt.selectedIndex = i;
+ var dflt = make_restore_default_button('default', 'btn-def-ram-option', ramopt, i);
+ ramopt.onchange = make_slot_bios_change_handler(dflt);
+ ramopt.parentNode.appendChild(document.createTextNode(' '));
+ ramopt.parentNode.appendChild(dflt);
+ break;
+ }
+ }
+ update_cmd_preview();
+}
+
+
+var fetch_bios_sets = (function ()
+ {
+ var pending = Object.create(null);
+ return function (device)
+ {
+ if (!Object.prototype.hasOwnProperty.call(bios_sets, device) && !Object.prototype.hasOwnProperty.call(pending, device))
+ {
+ pending[device] = true;
+ var req = new XMLHttpRequest();
+ req.open('GET', appurl + 'rpc/bios/' + encodeURIComponent(device), true);
+ req.responseType = 'json';
+ req.onload =
+ function ()
+ {
+ delete pending[device];
+ if (req.status == 200)
+ {
+ bios_sets[device] = req.response;
+ var slotslist = document.getElementById('list-slot-options');
+ if (slotslist)
+ {
+ for (var item = slotslist.firstChild; item; item = item.nextSibling)
+ {
+ if ((item.nodeName == 'DT') && (item.getAttribute('data-slotcard') == device))
+ add_bios_row(item.getAttribute('data-slotname'), item.nextSibling.firstChild, device);
+ }
+ }
+ }
+ };
+ req.send();
+ }
+ };
+ })();
+
+
+var fetch_machine_flags = (function ()
+ {
+ var pending = Object.create(null);
+ return function (device)
+ {
+ if (!Object.prototype.hasOwnProperty.call(machine_flags, device) && !Object.prototype.hasOwnProperty.call(pending, device))
+ {
+ pending[device] = true;
+ var req = new XMLHttpRequest();
+ req.open('GET', appurl + 'rpc/flags/' + encodeURIComponent(device), true);
+ req.responseType = 'json';
+ req.onload =
+ function ()
+ {
+ delete pending[device];
+ if (req.status == 200)
+ {
+ machine_flags[device] = req.response;
+ var slotslist = document.getElementById('list-slot-options');
+ if (slotslist)
+ {
+ for (var item = slotslist.firstChild; item; item = item.nextSibling)
+ {
+ if ((item.nodeName == 'DT') && (item.getAttribute('data-slotcard') == device))
+ add_flag_rows(item.nextSibling.firstChild, device);
+ }
+ }
+ }
+ };
+ req.send();
+ }
+ };
+ })();
+
+
+var fetch_softwarelist_info = (function ()
+ {
+ var pending = Object.create(null);
+ return function (device)
+ {
+ if (!Object.prototype.hasOwnProperty.call(softwarelist_info, device) && !Object.prototype.hasOwnProperty.call(pending, device))
+ {
+ pending[device] = true;
+ var req = new XMLHttpRequest();
+ req.open('GET', appurl + 'rpc/softwarelists/' + encodeURIComponent(device), true);
+ req.responseType = 'json';
+ req.onload =
+ function ()
+ {
+ delete pending[device];
+ if (req.status == 200)
+ {
+ softwarelist_info[device] = req.response;
+ var slotslist = document.getElementById('list-slot-options');
+ if (slotslist)
+ {
+ for (var item = slotslist.firstChild; item; item = item.nextSibling)
+ {
+ if ((item.nodeName == 'DT') && (item.getAttribute('data-slotcard') == device))
+ {
+ var slotname = item.getAttribute('data-slotname');
+ add_softwarelist_rows(
+ device,
+ slotname,
+ document.getElementById('select-slot-choice-' + slotname.replace(/:/g, '-')).value);
+ }
+ }
+ }
+ }
+ };
+ req.send();
+ }
+ };
+ })();
+
+
+function add_flag_rows(table, device)
+{
+ var sorted_features = Object.keys(machine_flags[device].features).sort();
+ var imperfect = [], unemulated = [];
+ var len = sorted_features.length;
+ for (var i = 0; i < len; i++)
+ ((machine_flags[device].features[sorted_features[i]].overall == 'unemulated') ? unemulated : imperfect).push(sorted_features[i]);
+
+ function add_one(flags, title)
+ {
+ var len = flags.length;
+ if (len > 0)
+ {
+ var row = document.createElement('tr');
+ row.appendChild(document.createElement('th')).textContent = title;
+ var cell = row.appendChild(document.createElement('td'));
+ cell.textContent = flags[0];
+ for (i = 1; i < len; i++)
+ cell.textContent += ', ' + flags[i];
+ if (table.lastChild.getAttribute('class') == 'devbios')
+ table.insertBefore(row, table.lastChild);
+ else
+ table.appendChild(row);
+ }
+ }
+
+ add_one(unemulated, 'Unemulated features:');
+ add_one(imperfect, 'Imperfect features:');
+}
+
+
+function add_softwarelist_rows(device, slot, card)
+{
+ var sorted_softwarelists = Object.keys(softwarelist_info[device]).sort();
+ var cardtag = slot + ':' + card;
+ if (sorted_softwarelists.length)
+ {
+ var table = document.getElementById('tbl-softwarelists').tBodies[0];
+ sorted_softwarelists.forEach(
+ function (tag)
+ {
+ var cell, link;
+ var info = softwarelist_info[device][tag];
+ var href = appurl + 'softwarelist/' + encodeURIComponent(info.shortname);
+ var row = table.appendChild(document.createElement('tr'));
+ row.setAttribute('id', 'row-softwarelists-' + (cardtag + tag).replace(/:/g, '-'))
+ row.appendChild(document.createElement('td')).textContent = cardtag;
+ link = row.appendChild(document.createElement('td')).appendChild(document.createElement('a'));
+ link.setAttribute('href', href);
+ link.textContent = info.shortname;
+ link = row.appendChild(document.createElement('td')).appendChild(document.createElement('a'));
+ link.setAttribute('href', href);
+ link.textContent = info.description;
+ row.appendChild(document.createElement('td')).textContent = info.status;
+ total = info.total;
+ cell = row.appendChild(document.createElement('td'));
+ cell.textContent = total.toString(10);
+ cell.style.textAlign = 'right';
+ if (!total)
+ total = 1;
+ cell = row.appendChild(document.createElement('td'));
+ cell.textContent = (info.supported * 100.0 / total).toFixed(1) + '%';
+ cell.style.textAlign = 'right';
+ cell = row.appendChild(document.createElement('td'));
+ cell.textContent = (info.partiallysupported * 100.0 / total).toFixed(1) + '%';
+ cell.style.textAlign = 'right';
+ cell = row.appendChild(document.createElement('td'));
+ cell.textContent = (info.unsupported * 100.0 / total).toFixed(1) + '%';
+ cell.style.textAlign = 'right';
+ });
+ document.getElementById('heading-softwarelists').style.removeProperty('display');
+ document.getElementById('tbl-softwarelists').style.removeProperty('display');
+ }
+}
+
+
+function add_bios_row(slot, table, device)
+{
+ var sorted_sets = Object.keys(bios_sets[device]).sort();
+ var len = sorted_sets.length;
+ if (len > 0)
+ {
+ // create table row, add heading
+ var row = document.createElement('tr');
+ row.setAttribute('class', 'devbios');
+ row.appendChild(document.createElement('th')).textContent = 'BIOS:';
+ var cell = document.createElement('td');
+
+ // make the BIOS popul itself
+ var popup = document.createElement('select');
+ popup.setAttribute('id', ('select-slot-bios-' + slot).replace(/:/g, '-'));
+ for (var i = 0; i < len; i++)
+ {
+ var set = sorted_sets[i];
+ var detail = bios_sets[device][set];
+ var option = document.createElement('option');
+ option.setAttribute('value', set);
+ option.setAttribute('data-isdefault', detail.isdefault ? 'yes' : 'no');
+ option.textContent = set + ' - ' + detail.description;
+ popup.appendChild(option);
+ if (detail.isdefault)
+ popup.selectedIndex = i;
+ }
+ cell.appendChild(popup);
+
+ // make a button to restore the default
+ var dflt;
+ if (popup.selectedOptions[0].getAttribute('data-isdefault') == 'yes')
+ {
+ dflt = make_restore_default_button('default', ('btn-def-slot-bios-' + slot).replace(/:/g, '-'), popup, popup.selectedIndex);
+ cell.appendChild(document.createTextNode(' '))
+ cell.appendChild(dflt);
+ }
+
+ // drop the controls into a cell, add it to the table, keep the command line preview up-to-date
+ popup.onchange = make_slot_bios_change_handler(dflt);
+ row.appendChild(cell);
+ table.appendChild(row);
+ update_cmd_preview();
+ }
+}
+
+
+function make_slot_term(name, slot, defaults)
+{
+ var len, i;
+
+ // see if we can find a default, outer layers take precedence
+ var defcard = '';
+ len = defaults.length;
+ for (i = 0; (i < len) && (defcard == ''); i++)
+ {
+ if (Object.prototype.hasOwnProperty.call(defaults[i], name))
+ defcard = defaults[i][name];
+ }
+
+ // create a container with the slot name and popup
+ var term = document.createElement('dt');
+ term.setAttribute('id', ('item-slot-choice-' + name).replace(/:/g, '-'));
+ term.setAttribute('data-slotname', name);
+ term.setAttribute('data-slotcard', '');
+ term.textContent = name + ': ';
+ var popup = document.createElement('select');
+ popup.setAttribute('id', make_slot_popup_id(name));
+ term.appendChild(popup);
+
+ // add the empty slot option
+ var option = document.createElement('option');
+ option.setAttribute('value', '');
+ option.setAttribute('data-isdefault', ('' == defcard) ? 'yes' : 'no');
+ option.textContent = '-';
+ popup.appendChild(option);
+ popup.selectedIndex = 0;
+
+ // add options for the cards
+ var sorted_choices = Object.keys(slot).sort();
+ len = sorted_choices.length;
+ for (i = 0; i < len; i++)
+ {
+ var choice = sorted_choices[i];
+ var card = slot[choice];
+ option = document.createElement('option');
+ option.setAttribute('value', choice);
+ option.setAttribute('data-isdefault', (choice == defcard) ? 'yes' : 'no');
+ option.textContent = choice + ' - ' + card.description;
+ popup.appendChild(option);
+ if (choice == defcard)
+ popup.selectedIndex = i + 1;
+ }
+
+ // make a button for restoring the default and hook up events
+ var dflt = make_restore_default_button('default', ('btn-def-slot-choice-' + name).replace(/:/g, '-'), popup, popup.selectedIndex);
+ term.appendChild(document.createTextNode(' '));
+ term.appendChild(dflt);
+ popup.onchange = make_slot_change_handler(name, slot, defaults, dflt);
+ return term;
+}
+
+
+function add_slot_items(root, device, defaults, slotslist, pos)
+{
+ // add another layer of defaults for this device
+ var defvals = Object.create(null);
+ for (var key in slot_info[device].defaults)
+ defvals[root + key] = slot_info[device].defaults[key];
+ defaults = defaults.slice();
+ defaults.push(defvals);
+ var defcnt = defaults.length;
+
+ // add controls for each subslot
+ var slots = slot_info[device].slots;
+ var sorted_slots = Object.keys(slots).sort();
+ var len = sorted_slots.length;
+ for (var i = 0; i < len; i++)
+ {
+ var slotname = sorted_slots[i];
+ var slotabs = root + slotname;
+ var slot = slots[slotname];
+ var term = make_slot_term(slotabs, slot, defaults);
+ var def = document.createElement('dd');
+ def.setAttribute('id', ('item-slot-detail-' + slotabs).replace(/:/g, '-'));
+ if (pos)
+ {
+ slotslist.insertBefore(term, pos);
+ slotslist.insertBefore(def, pos);
+ }
+ else
+ {
+ slotslist.appendChild(term);
+ slotslist.appendChild(def);
+ }
+
+ // force a change event to populate subslot controls if the default isn't empty
+ get_slot_popup(slotabs).dispatchEvent(new Event('change'));
+ }
+
+ update_cmd_preview();
+}
+
+
+function make_slot_change_handler(name, slot, defaults, dfltbtn)
+{
+ var selection = null;
+ return function (event)
+ {
+ var choice = event.target.value;
+ var slotslist = event.target.parentNode.parentNode;
+ var def = event.target.parentNode.nextSibling;
+ var slotname = event.target.parentNode.getAttribute('data-slotname');
+ selection = (choice == '') ? null : slot[choice];
+ dfltbtn.disabled = event.target.selectedOptions[0].getAttribute('data-isdefault') == 'yes';
+
+ // clear out any subslots from previous selection
+ var prefix = slotname + ':';
+ for (var candidate = def.nextSibling; candidate && candidate.getAttribute('data-slotname').startsWith(prefix); )
+ {
+ var next = candidate.nextSibling;
+ slotslist.removeChild(candidate);
+ candidate = next.nextSibling;
+ slotslist.removeChild(next);
+ }
+
+ // clear out any software lists from previous selection
+ var softwarelist_rowid_prefix = 'row-softwarelists-' + prefix.replace(/:/g, '-');
+ for (var candidate = document.getElementById('tbl-softwarelists').tBodies[0].rows[0]; candidate; )
+ {
+ var next = candidate.nextSibling;
+ if ((candidate.nodeName == 'TR') && candidate.getAttribute('id').startsWith(softwarelist_rowid_prefix))
+ candidate.parentNode.removeChild(candidate);
+ candidate = next;
+ }
+
+ if (selection === null)
+ {
+ // no selection, remove the slot card details table
+ event.target.parentNode.setAttribute('data-slotcard', '');
+ if (def.firstChild)
+ def.removeChild(def.firstChild);
+ }
+ else
+ {
+ // stash the selected device where it's easy to find
+ event.target.parentNode.setAttribute('data-slotcard', selection.device);
+
+ // create details table and add a link to the device's page
+ var tbl = document.createElement('table');
+ tbl.setAttribute('class', 'sysinfo');
+ var row = tbl.appendChild(document.createElement('tr'));
+ row.appendChild(document.createElement('th')).textContent = 'Short name:';
+ var link = row.appendChild(document.createElement('td')).appendChild(document.createElement('a'));
+ link.textContent = selection.device;
+ link.setAttribute('href', appurl + 'machine/' + encodeURIComponent(selection.device));
+
+ // if we have emulation flags, populate now, otherwise fetch asynchronously
+ if (!Object.prototype.hasOwnProperty.call(machine_flags, selection.device))
+ fetch_machine_flags(selection.device);
+ else
+ add_flag_rows(tbl, selection.device);
+
+ // if we have BIOS details, populate now, otherwise fetch asynchronously
+ if (!Object.prototype.hasOwnProperty.call(bios_sets, selection.device))
+ fetch_bios_sets(selection.device);
+ else
+ add_bios_row(slotname, tbl, selection.device);
+
+ // if we have software list info, populate now, otherweise fetch asynchronously
+ if (!Object.prototype.hasOwnProperty.call(softwarelist_info, selection.device))
+ fetch_softwarelist_info(selection.device);
+ else
+ add_softwarelist_rows(selection.device, slotname, choice);
+
+ // drop the details table into the list
+ if (def.firstChild)
+ def.replaceChild(tbl, def.firstChild);
+ else
+ def.appendChild(tbl);
+
+ // create controls for subslots
+ add_slot_items(slotname + ':' + choice, selection.device, defaults, slotslist, def.nextSibling);
+ }
+ update_cmd_preview();
+ };
+}
+
+
+function make_slot_bios_change_handler(dflt)
+{
+ return function (event)
+ {
+ if (dflt)
+ dflt.disabled = event.target.selectedOptions[0].getAttribute('data-isdefault') == 'yes';
+ update_cmd_preview();
+ }
+}
+
+
+function populate_slots(machine)
+{
+ var placeholder = document.getElementById('para-slots-placeholder');
+ var slotslist = document.createElement('dl');
+ slotslist.setAttribute('id', 'list-slot-options');
+ placeholder.parentNode.replaceChild(slotslist, placeholder);
+ add_slot_items('', machine, [], slotslist, null);
+}
+
+
+function slot_retrieve_error(device)
+{
+ var errors;
+ var placeholder = document.getElementById('para-slots-placeholder');
+ if (placeholder)
+ {
+ errors = document.createElement('div');
+ errors.setAttribute('id', 'div-slots-errors');
+ placeholder.parentNode.replaceChild(errors, placeholder);
+ }
+ else
+ {
+ errors = document.getElementById('div-slots-errors');
+ }
+ var message = document.createElement('p');
+ message.textContent = 'Error retrieving slot information for ' + device + '.';
+ errors.appendChild(message);
+}
+
+
+function fetch_slots(machine)
+{
+ function make_request(device)
+ {
+ var req = new XMLHttpRequest();
+ req.open('GET', appurl + 'rpc/slots/' + encodeURIComponent(device), true);
+ req.responseType = 'json';
+ req.onload =
+ function ()
+ {
+ if (req.status == 200)
+ {
+ slot_info[device] = req.response;
+ delete pending[device];
+ for (var slotname in req.response.slots)
+ {
+ var slot = req.response.slots[slotname];
+ for (var choice in slot)
+ {
+ var card = slot[choice].device
+ if (!Object.prototype.hasOwnProperty.call(slot_info, card) && !Object.prototype.hasOwnProperty.call(pending, card))
+ {
+ pending[card] = true;
+ make_request(card);
+ }
+ }
+ }
+ if (!Object.keys(pending).length)
+ populate_slots(machine);
+ }
+ else
+ {
+ slot_retrieve_error(device);
+ }
+ };
+ req.send();
+ }
+ var pending = Object.create(null);
+ pending[machine] = true;
+ make_request(machine);
+}
diff --git a/docs/release/scripts/minimaws/lib/assets/romident.js b/docs/release/scripts/minimaws/lib/assets/romident.js
new file mode 100644
index 00000000000..599b0674007
--- /dev/null
+++ b/docs/release/scripts/minimaws/lib/assets/romident.js
@@ -0,0 +1,641 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+
+var matched_names = new Array();
+var unmatched_names = new Array();
+var dump_info = Object.create(null);
+var machine_info = Object.create(null);
+var softwarelist_info = Object.create(null);
+var software_info = Object.create(null);
+
+
+function get_chd_sha1(header)
+{
+ if (header.byteLength < 16)
+ return null;
+ if (String.fromCharCode.apply(null, new Uint8Array(header, 0, 8)) != 'MComprHD')
+ return null;
+ var view = new DataView(header);
+ var headerlen = view.getUint32(8, false);
+ var version = view.getUint32(12, false);
+ var sha1offs;
+ if (version == 3)
+ {
+ if (headerlen != 120)
+ return null;
+ sha1offs = 80;
+ }
+ else if (version == 4)
+ {
+ if (headerlen != 108)
+ return null;
+ sha1offs = 48;
+ }
+ else if (version == 5)
+ {
+ if (headerlen != 124)
+ return null;
+ sha1offs = 84;
+ }
+ else
+ {
+ return null;
+ }
+ if (header.byteLength < (sha1offs + 20))
+ return null;
+ return Array.from(new Uint8Array(header, sha1offs, 20)).map(x => x.toString(16).padStart(2, '0')).join('');
+}
+
+
+function get_sha1_group(sha1)
+{
+ if (Object.hasOwnProperty.call(dump_info, sha1))
+ {
+ return dump_info[sha1];
+ }
+ else
+ {
+ var result = new Object();
+ result.crc = Object.create(null);
+ dump_info[sha1] = result;
+ return result;
+ }
+}
+
+
+function add_matches(table, names, matches)
+{
+ for (var i = 0; i < names.length; i++)
+ {
+ var row = table.appendChild(document.createElement('tr'));
+ var name = row.appendChild(document.createElement('th'));
+ name.textContent = names[i];
+ if (matches !== null)
+ {
+ name.setAttribute('rowspan', matches.length);
+ row.appendChild(document.createElement('td')).textContent = matches[0].name;
+ var bad = row.appendChild(document.createElement('td'));
+ if (matches[0].bad)
+ bad.textContent = 'BAD';
+ for (var j = 1; j < matches.length; j++)
+ {
+ row = table.appendChild(document.createElement('tr'));
+ row.appendChild(document.createElement('td')).textContent = matches[j].name;
+ bad = row.appendChild(document.createElement('td'));
+ if (matches[j].bad)
+ bad.textContent = 'BAD';
+ }
+ }
+ }
+}
+
+
+function add_software_matches(table, names, part, matches)
+{
+ for (var i = 0; i < names.length; i++)
+ {
+ var row = table.appendChild(document.createElement('tr'));
+ var name = row.appendChild(document.createElement('th'));
+ name.textContent = names[i];
+ if (matches !== null)
+ {
+ name.setAttribute('rowspan', matches.length);
+ row.appendChild(document.createElement('td')).textContent = part;
+ row.appendChild(document.createElement('td')).textContent = matches[0].name;
+ var bad = row.appendChild(document.createElement('td'));
+ if (matches[0].bad)
+ bad.textContent = 'BAD';
+ for (var j = 1; j < matches.length; j++)
+ {
+ row = table.appendChild(document.createElement('tr'));
+ row.appendChild(document.createElement('td')).textContent = matches[j].name;
+ bad = row.appendChild(document.createElement('td'));
+ if (matches[j].bad)
+ bad.textContent = 'BAD';
+ }
+ }
+ }
+}
+
+
+function add_unmatched(names, crc, sha1)
+{
+ var table;
+ if (unmatched_names.length > 0)
+ {
+ table = document.getElementById('table-unmatched');
+ }
+ else
+ {
+ var div = document.getElementById('div-unmatched');
+ var heading = div.appendChild(document.createElement('h2'));
+ heading.textContent = 'Unmatched';
+ table = div.appendChild(document.createElement('table'));
+ table.setAttribute('id', 'table-unmatched');
+ make_collapsible(heading, table);
+ }
+ var content;
+ if (crc === null)
+ {
+ content = 'SHA1(' + sha1 + ')';
+ }
+ else
+ {
+ if (crc < 0)
+ crc = 0xffffffff + crc + 1;
+ content = 'CRC(' + crc.toString(16).padStart(8, '0') + ') SHA1(' + sha1 + ')';
+ }
+ for (var i = 0; i < names.length; i++)
+ {
+ var row = table.appendChild(document.createElement('tr'));
+ row.appendChild(document.createElement('th')).textContent = names[i];
+ row.appendChild(document.createElement('td')).textContent = content;
+ unmatched_names.push(names[i]);
+ }
+}
+
+
+function add_issues(name)
+{
+ var div = document.getElementById('div-issues');
+ var list;
+ if (!div.hasChildNodes())
+ {
+ var heading = div.appendChild(document.createElement('h2'));
+ heading.textContent = 'Potential Issues';
+ list = div.appendChild(document.createElement('dl'));
+ make_collapsible(heading, list);
+ }
+ else
+ {
+ list = div.lastChild;
+ }
+ list.appendChild(document.createElement('dt')).textContent = name;
+ var table = list.appendChild(document.createElement('dd')).appendChild(document.createElement('table'));
+ table.setAttribute('class', 'sysinfo');
+ return table;
+}
+
+
+function add_stuck_bits(table, stuck)
+{
+ function format_stuck_bits(bits, mask)
+ {
+ var result = '';
+ for (var i = 0; i < bits.length; i++)
+ {
+ if (i > 0)
+ result += ' ';
+ for (var j = 0; j < 8; j++)
+ {
+ if (!((mask[i] >> (7 - j)) & 0x01))
+ result += '-';
+ else if ((bits[i] >> (7 - j)) & 0x01)
+ result += '1';
+ else
+ result += '0';
+ }
+ }
+ var cell = document.createElement('td');
+ cell.appendChild(document.createElement('tt')).textContent = result;
+ return cell;
+ }
+
+ var row = table.appendChild(document.createElement('tr'));
+ var header = row.appendChild(document.createElement('th'));
+ header.textContent = 'Fixed data bits:';
+ header.setAttribute('rowspan', stuck.length);
+ row.appendChild(format_stuck_bits(stuck[0].bits, stuck[0].mask));
+ for (var i = 1; i < stuck.length; i++)
+ {
+ row = table.appendChild(document.createElement('tr'));
+ row.appendChild(format_stuck_bits(stuck[i].bits, stuck[i].mask));
+ }
+}
+
+
+function get_machine_table(shortname, description)
+{
+ if (Object.hasOwnProperty.call(machine_info, shortname))
+ {
+ return machine_info[shortname];
+ }
+ else
+ {
+ var div = document.getElementById('div-machines');
+ if (!div.hasChildNodes())
+ div.appendChild(document.createElement('h2')).textContent = 'Machines';
+ var heading = div.appendChild(document.createElement('h3'));
+ var link = heading.appendChild(document.createElement('a'));
+ link.textContent = description;
+ link.setAttribute('href', appurl + 'machine/' + encodeURIComponent(shortname));
+ var table = div.appendChild(document.createElement('table'));
+ machine_info[shortname] = table;
+ add_matches(table, matched_names, null);
+ make_collapsible(heading, table);
+ return table;
+ }
+}
+
+
+function get_softwarelist_div(shortname, description)
+{
+ if (Object.hasOwnProperty.call(softwarelist_info, shortname))
+ {
+ return softwarelist_info[shortname];
+ }
+ else
+ {
+ software_info[shortname] = Object.create(null)
+ var div = document.getElementById('div-software');
+ if (!div.hasChildNodes())
+ div.appendChild(document.createElement('h2')).textContent = 'Software';
+ div = div.appendChild(document.createElement('div'));
+ var heading = div.appendChild(document.createElement('h3'));
+ var link = heading.appendChild(document.createElement('a'));
+ link.textContent = description;
+ link.setAttribute('href', appurl + 'softwarelist/' + encodeURIComponent(shortname));
+ softwarelist_info[shortname] = div;
+ return div;
+ }
+}
+
+
+function get_software_table(softwarelist, shortname, description)
+{
+ if (Object.hasOwnProperty.call(software_info, softwarelist) && Object.hasOwnProperty.call(software_info[softwarelist], shortname))
+ {
+ return software_info[softwarelist][shortname];
+ }
+ else
+ {
+ var div = softwarelist_info[softwarelist];
+ var heading = div.appendChild(document.createElement('h4'));
+ var link = heading.appendChild(document.createElement('a'));
+ link.textContent = description;
+ link.setAttribute('href', appurl + 'softwarelist/' + encodeURIComponent(softwarelist) + '/' + encodeURIComponent(shortname));
+ var table = div.appendChild(document.createElement('table'));
+ software_info[softwarelist][shortname] = table;
+ add_software_matches(table, matched_names, null, null);
+ return table;
+ }
+}
+
+
+function request_dumps(name, group, crc, sha1, url, progress)
+{
+ var req = new XMLHttpRequest();
+ req.responseType = 'json';
+ req.onload =
+ function ()
+ {
+ if (req.status == 200)
+ {
+ var machines = Object.create(null);
+ var software = Object.create(null);
+ var matched = Object.keys(req.response.machines);
+ var softwarelists = Object.keys(req.response.software);
+
+ if (matched.length > 0)
+ {
+ Object.keys(machine_info).forEach(
+ function (shortname)
+ {
+ var machine_table = machine_info[shortname];
+ if (Object.hasOwnProperty.call(req.response.machines, shortname))
+ {
+ machines[shortname] = req.response.machines[shortname].matches;
+ add_matches(machine_table, group.names, req.response.machines[shortname].matches);
+ }
+ else
+ {
+ add_matches(machine_table, group.names, null);
+ }
+ });
+ if (softwarelists.length <= 0)
+ {
+ Object.keys(software_info).forEach(
+ function (listname)
+ {
+ Object.keys(software_info[listname]).forEach(
+ function (softwarename)
+ {
+ var software_table = software_info[listname][softwarename];
+ add_software_matches(software_table, group.names, null, null);
+ });
+ });
+ }
+ matched.forEach(
+ function (shortname)
+ {
+ if (!Object.hasOwnProperty.call(machine_info, shortname))
+ {
+ var machine_details = req.response.machines[shortname];
+ var machine_table = get_machine_table(shortname, machine_details.description);
+ machines[shortname] = req.response.machines[shortname].matches;
+ add_matches(machine_table, group.names, machine_details.matches);
+ }
+ });
+ }
+
+ if (softwarelists.length > 0)
+ {
+ if (matched.length <= 0)
+ {
+ Object.keys(machine_info).forEach(
+ function (shortname)
+ {
+ var machine_table = machine_info[shortname];
+ add_matches(machine_table, group.names, null);
+ });
+ }
+ Object.keys(software_info).forEach(
+ function (listname)
+ {
+ var haslist = Object.hasOwnProperty.call(req.response.software, listname);
+ Object.keys(software_info[listname]).forEach(
+ function (softwarename)
+ {
+ var software_table = software_info[listname][softwarename];
+ if (haslist && Object.hasOwnProperty.call(req.response.software[listname].software, softwarename))
+ {
+ if (!Object.hasOwnProperty.call(software, listname))
+ software[listname] = Object.create(null);
+ if (!Object.hasOwnProperty.call(software[listname], softwarename))
+ software[listname][softwarename] = Object.create(null);
+ var software_details = req.response.software[listname].software[softwarename];
+ Object.keys(software_details.parts).forEach(
+ function (partname)
+ {
+ var matches = software_details.parts[partname].matches;
+ software[listname][softwarename][partname] = matches;
+ add_software_matches(software_table, group.names, partname, matches);
+ });
+ }
+ else
+ {
+ add_software_matches(software_table, group.names, null, null);
+ }
+ });
+ });
+ softwarelists.forEach(
+ function (listname)
+ {
+ var haslist = Object.hasOwnProperty.call(software_info, listname);
+ var softwarelist_details = req.response.software[listname];
+ var softwarelist_div = get_softwarelist_div(listname, softwarelist_details.description);
+ Object.keys(softwarelist_details.software).forEach(
+ function (softwarename)
+ {
+ if (!haslist || !Object.hasOwnProperty.call(software_info[listname], softwarename))
+ {
+ if (!Object.hasOwnProperty.call(software, listname))
+ software[listname] = Object.create(null);
+ if (!Object.hasOwnProperty.call(software[listname], softwarename))
+ software[listname][softwarename] = Object.create(null);
+ var software_details = softwarelist_details.software[softwarename];
+ var software_table = get_software_table(listname, softwarename, software_details.description);
+ Object.keys(software_details.parts).forEach(
+ function (partname)
+ {
+ var matches = software_details.parts[partname].matches;
+ software[listname][softwarename][partname] = matches;
+ add_software_matches(software_table, group.names, partname, matches);
+ });
+ }
+ });
+ });
+ }
+
+ if ((matched.length > 0) | (softwarelists.length > 0))
+ {
+ for (var i = 0; i < group.names.length; i++)
+ matched_names.push(group.names[i]);
+ }
+ else
+ {
+ add_unmatched(group.names, crc, sha1);
+ }
+
+ group.machines = machines;
+ group.software = software;
+ progress.parentNode.removeChild(progress);
+ }
+ else
+ {
+ progress.textContent = 'Error identifying \u201C' + name + '\u201D: HTTP status code ' + req.status.toString(10);
+ if (crc !== null)
+ delete dump_info[sha1].crc[crc];
+ else
+ delete dump_info[sha1].disk;
+ }
+ };
+ req.onerror =
+ function ()
+ {
+ progress.textContent = 'Error identifying \u201C' + name + '\u201D';
+ if (crc !== null)
+ delete dump_info[sha1].crc[crc];
+ else
+ delete dump_info[sha1].disk;
+ };
+ req.onabort =
+ function ()
+ {
+ progress.textContent = 'Error identifying \u201C' + name + '\u201D: server request aborted';
+ if (crc !== null)
+ delete dump_info[sha1].crc[crc];
+ else
+ delete dump_info[sha1].disk;
+ };
+ req.open('GET', url);
+ req.send();
+}
+
+
+function add_name(group, name, crc, sha1)
+{
+ if (group.names.indexOf(name) < 0)
+ {
+ group.names.push(name);
+
+ if (group.hasOwnProperty('issues'))
+ {
+ var issues = group.issues;
+ if (issues !== null)
+ issues.parentNode.parentNode.insertBefore(document.createElement('dt'), issues.parentNode).textContent = name;
+ }
+
+ if (group.hasOwnProperty('machines'))
+ {
+ var machines = group.machines;
+ var software = group.software;
+ var names = [ name ];
+ if ((Object.keys(machines).length > 0) || (Object.keys(software).length > 0))
+ {
+ Object.keys(machine_info).forEach(
+ function (shortname)
+ {
+ var machine_table = machine_info[shortname];
+ if (Object.hasOwnProperty.call(machines, shortname))
+ add_matches(machine_table, names, machines[shortname]);
+ else
+ add_matches(machine_table, names, null);
+ });
+
+ Object.keys(software_info).forEach(
+ function (listname)
+ {
+ var haslist = Object.hasOwnProperty.call(software, listname);
+ Object.keys(software_info[listname]).forEach(
+ function (softwarename)
+ {
+ var software_table = software_info[listname][softwarename];
+ if (haslist && Object.hasOwnProperty.call(software[listname], softwarename))
+ {
+ Object.keys(software[listname][softwarename]).forEach(
+ function (partname)
+ {
+ add_software_matches(software_table, names, partname, software[listname][softwarename][partname]);
+ });
+ }
+ else
+ {
+ add_software_matches(software_table, names, null, null);
+ }
+ });
+ });
+
+ matched_names.push(name);
+ }
+ else
+ {
+ add_unmatched(names, crc, sha1);
+ }
+ }
+ }
+}
+
+
+function identify_file(file, trychd, progress)
+{
+ var digested = 0;
+ var crcproc = new Crc32Digester();
+ var sha1proc = new Sha1Digester();
+ var stuckbitsproc = new StuckBitsDigester();
+
+ function process_chunk(e)
+ {
+ if (e.target.error === null)
+ {
+ var data = e.target.result;
+ if ((digested == 0) && trychd)
+ {
+ var chdsha1 = get_chd_sha1(data);
+ if (chdsha1 !== null)
+ {
+ var sha1grp = get_sha1_group(chdsha1);
+ if (!sha1grp.hasOwnProperty('disk'))
+ {
+ var diskgrp = new Object();
+ diskgrp.names = [ file.name ];
+ sha1grp.disk = diskgrp;
+ request_dumps(file.name, diskgrp, null, chdsha1, appurl + 'rpc/diskdumps?sha1=' + chdsha1, progress);
+ }
+ else
+ {
+ add_name(sha1grp.disk, file.name, crc, sha1);
+ progress.parentNode.removeChild(progress);
+ }
+ return;
+ }
+ }
+ var view = new Uint8Array(data);
+ crcproc.digest(data, view);
+ sha1proc.digest(data, view);
+ stuckbitsproc.digest(data, view);
+ digested += data.byteLength;
+ if (digested < file.size)
+ {
+ read_block();
+ }
+ else
+ {
+ var crc = crcproc.finalise();
+ var sha1 = sha1proc.finalise();
+ var stuckbits = stuckbitsproc.finalise();
+
+ var sha1grp = get_sha1_group(sha1);
+ var crcgrp;
+ if (!Object.hasOwnProperty.call(sha1grp.crc, crc))
+ {
+ crcgrp = new Object();
+ crcgrp.names = [ file.name ];
+ sha1grp.crc[crc] = crcgrp;
+ if (stuckbits.length)
+ {
+ crcgrp.issues = add_issues(file.name);
+ add_stuck_bits(crcgrp.issues, stuckbits);
+ }
+ else
+ {
+ crcgrp.issues = null;
+ }
+ var crcstr = ((crc < 0) ? (0xffffffff + crc + 1) : crc).toString(16).padStart(8, '0');
+ request_dumps(file.name, crcgrp, crc, sha1, appurl + 'rpc/romdumps?crc=' + crcstr + '&sha1=' + sha1, progress);
+ }
+ else
+ {
+ crcgrp = sha1grp.crc[crc];
+ add_name(crcgrp, file.name, crc, sha1);
+ progress.parentNode.removeChild(progress);
+ }
+ }
+ }
+ else
+ {
+ progress.textContent = 'Error identifying \u201C' + file.name + '\u201D: ' + e.target.error;
+ }
+ }
+
+ function read_block()
+ {
+ var remaining = file.size - digested;
+ var chunk = file.slice(digested, digested + Math.min(remaining, 65536));
+ var reader = new FileReader();
+ reader.onload = process_chunk;
+ reader.readAsArrayBuffer(chunk);
+ };
+
+ progress.textContent = 'Identifying \u201C' + file.name + '\u201D\u2026';
+ read_block();
+}
+
+
+function add_dump_files(files)
+{
+ var prog = document.getElementById('div-progress');
+ for (var i = 0; i < files.length; i++)
+ {
+ var name = files[i].name;
+ var dot = name.lastIndexOf('.');
+ var trychd = (dot > 0) && (name.substring(dot + 1).toLowerCase() == 'chd');
+ identify_file(files[i], trychd, prog.appendChild(document.createElement('p')));
+ }
+}
+
+
+function div_dropzone_dragover(e)
+{
+ e.stopPropagation();
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'link';
+}
+
+
+function div_dropzone_drop(e)
+{
+ e.stopPropagation();
+ e.preventDefault();
+ add_dump_files(e.dataTransfer.files);
+}
diff --git a/docs/release/scripts/minimaws/lib/assets/sortasc.png b/docs/release/scripts/minimaws/lib/assets/sortasc.png
new file mode 100644
index 00000000000..29b893cdd02
--- /dev/null
+++ b/docs/release/scripts/minimaws/lib/assets/sortasc.png
Binary files differ
diff --git a/docs/release/scripts/minimaws/lib/assets/sortasc.svg b/docs/release/scripts/minimaws/lib/assets/sortasc.svg
new file mode 100644
index 00000000000..c2ff41afccb
--- /dev/null
+++ b/docs/release/scripts/minimaws/lib/assets/sortasc.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1" height="16" width="30">
+ <path d="m 14,16 h 16 l -8,-16 z" />
+</svg>
diff --git a/docs/release/scripts/minimaws/lib/assets/sortdesc.png b/docs/release/scripts/minimaws/lib/assets/sortdesc.png
new file mode 100644
index 00000000000..63f48fc7213
--- /dev/null
+++ b/docs/release/scripts/minimaws/lib/assets/sortdesc.png
Binary files differ
diff --git a/docs/release/scripts/minimaws/lib/assets/sortdesc.svg b/docs/release/scripts/minimaws/lib/assets/sortdesc.svg
new file mode 100644
index 00000000000..4a10b3f6e47
--- /dev/null
+++ b/docs/release/scripts/minimaws/lib/assets/sortdesc.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1" height="16" width="30">
+ <path d="m 14,0 h 16 l -8,16 z" />
+</svg>
diff --git a/docs/release/scripts/minimaws/lib/assets/sortind.png b/docs/release/scripts/minimaws/lib/assets/sortind.png
new file mode 100644
index 00000000000..773dd6aec02
--- /dev/null
+++ b/docs/release/scripts/minimaws/lib/assets/sortind.png
Binary files differ
diff --git a/docs/release/scripts/minimaws/lib/assets/sortind.svg b/docs/release/scripts/minimaws/lib/assets/sortind.svg
new file mode 100644
index 00000000000..6e351b6a6fb
--- /dev/null
+++ b/docs/release/scripts/minimaws/lib/assets/sortind.svg
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1" height="16" width="30">
+ <path d="m 0,16 h 16 l -8,-16 z" />
+ <path d="m 14,0 h 16 l -8,16 z" />
+</svg>
diff --git a/docs/release/scripts/minimaws/lib/assets/style.css b/docs/release/scripts/minimaws/lib/assets/style.css
new file mode 100644
index 00000000000..9ba6631a783
--- /dev/null
+++ b/docs/release/scripts/minimaws/lib/assets/style.css
@@ -0,0 +1,18 @@
+/* license:BSD-3-Clause
+ * copyright-holders:Vas Crabb
+ */
+
+th { text-align: left; background-color: #ddd; padding: 0.25em }
+td { padding-left: 0.25em; padding-right: 0.25em }
+
+img[class=disclosure] { height: 0.7em; margin-right: 0.25em; vertical-align: baseline }
+
+div[class=sorticon] { float: right; height: 100%; margin-left: 0.5em }
+div[class=sorticon] img { height: 0.7em; vertical-align: baseline }
+
+table[class=sysinfo] th { text-align: right }
+
+div[class=dropzone] { padding: 0 1em; border-width: 2px; border-radius: 1em; border-style: dashed }
+
+dl[id=list-slot-options] dt { font-weight: bold; margin-top: 1em }
+dl[id=list-slot-options] dd table { margin-top: 0.5em; margin-bottom: 1em }