lib.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703
  1. "use strict";
  2. var goog = goog || {};
  3. goog.exportSymbol = function() {};
  4. goog.exportProperty = function() {};
  5. var v86util = v86util || {};
  6. // pad string with spaces on the right
  7. v86util.pads = function(str, len)
  8. {
  9. str = (str || str === 0) ? str + "" : "";
  10. return str.padEnd(len, " ");
  11. };
  12. // pad string with zeros on the left
  13. v86util.pad0 = function(str, len)
  14. {
  15. str = (str || str === 0) ? str + "" : "";
  16. return str.padStart(len, "0");
  17. };
  18. // generates array given size with zeros
  19. v86util.zeros = function(size)
  20. {
  21. return Array(size).fill(0);
  22. };
  23. // generates [0, 1, 2, ..., size-1]
  24. v86util.range = function(size)
  25. {
  26. return Array.from(Array(size).keys());
  27. };
  28. v86util.view = function(constructor, memory, offset, length)
  29. {
  30. return new Proxy({},
  31. {
  32. get: function(target, property, receiver)
  33. {
  34. const b = new constructor(memory.buffer, offset, length);
  35. const x = b[property];
  36. if(typeof x === "function")
  37. {
  38. return x.bind(b);
  39. }
  40. dbg_assert(/^\d+$/.test(property) || property === "buffer" || property === "length" ||
  41. property === "BYTES_PER_ELEMENT" || property === "byteOffset");
  42. return x;
  43. },
  44. set: function(target, property, value, receiver)
  45. {
  46. dbg_assert(/^\d+$/.test(property));
  47. new constructor(memory.buffer, offset, length)[property] = value;
  48. return true;
  49. },
  50. });
  51. };
  52. /**
  53. * number to hex
  54. * @param {number} n
  55. * @param {number=} len
  56. * @return {string}
  57. */
  58. function h(n, len)
  59. {
  60. if(!n)
  61. {
  62. var str = "";
  63. }
  64. else
  65. {
  66. var str = n.toString(16);
  67. }
  68. return "0x" + v86util.pad0(str.toUpperCase(), len || 1);
  69. }
  70. function hex_dump(buffer)
  71. {
  72. function hex(n, len)
  73. {
  74. return v86util.pad0(n.toString(16).toUpperCase(), len);
  75. }
  76. const result = [];
  77. let offset = 0;
  78. for(; offset + 15 < buffer.length; offset += 16)
  79. {
  80. let line = hex(offset, 5) + " ";
  81. for(let j = 0; j < 0x10; j++)
  82. {
  83. line += hex(buffer[offset + j], 2) + " ";
  84. }
  85. line += " ";
  86. for(let j = 0; j < 0x10; j++)
  87. {
  88. const x = buffer[offset + j];
  89. line += (x >= 33 && x !== 34 && x !== 92 && x <= 126) ? String.fromCharCode(x) : ".";
  90. }
  91. result.push(line);
  92. }
  93. let line = hex(offset, 5) + " ";
  94. for(; offset < buffer.length; offset++)
  95. {
  96. line += hex(buffer[offset], 2) + " ";
  97. }
  98. const remainder = offset & 0xF;
  99. line += " ".repeat(0x10 - remainder);
  100. line += " ";
  101. for(let j = 0; j < remainder; j++)
  102. {
  103. const x = buffer[offset + j];
  104. line += (x >= 33 && x !== 34 && x !== 92 && x <= 126) ? String.fromCharCode(x) : ".";
  105. }
  106. result.push(line);
  107. return "\n" + result.join("\n") + "\n";
  108. }
  109. if(typeof crypto !== "undefined" && crypto.getRandomValues)
  110. {
  111. let rand_data = new Int32Array(1);
  112. v86util.get_rand_int = function()
  113. {
  114. crypto.getRandomValues(rand_data);
  115. return rand_data[0];
  116. };
  117. }
  118. else if(typeof require !== "undefined")
  119. {
  120. /** @type {{ randomBytes: Function }} */
  121. const crypto = require("crypto");
  122. v86util.get_rand_int = function()
  123. {
  124. return crypto.randomBytes(4)["readInt32LE"](0);
  125. };
  126. }
  127. else
  128. {
  129. dbg_assert(false, "Unsupported platform: No cryptographic random values");
  130. }
  131. (function()
  132. {
  133. if(typeof Math.clz32 === "function" && Math.clz32(0) === 32 &&
  134. Math.clz32(0x12345) === 15 && Math.clz32(-1) === 0)
  135. {
  136. /**
  137. * calculate the integer logarithm base 2 of a byte
  138. * @param {number} x
  139. * @return {number}
  140. */
  141. v86util.int_log2_byte = function(x)
  142. {
  143. dbg_assert(x > 0);
  144. dbg_assert(x < 0x100);
  145. return 31 - Math.clz32(x);
  146. };
  147. /**
  148. * calculate the integer logarithm base 2
  149. * @param {number} x
  150. * @return {number}
  151. */
  152. v86util.int_log2 = function(x)
  153. {
  154. dbg_assert(x > 0);
  155. return 31 - Math.clz32(x);
  156. };
  157. return;
  158. }
  159. var int_log2_table = new Int8Array(256);
  160. for(var i = 0, b = -2; i < 256; i++)
  161. {
  162. if(!(i & i - 1))
  163. b++;
  164. int_log2_table[i] = b;
  165. }
  166. /**
  167. * calculate the integer logarithm base 2 of a byte
  168. * @param {number} x
  169. * @return {number}
  170. */
  171. v86util.int_log2_byte = function(x)
  172. {
  173. dbg_assert(x > 0);
  174. dbg_assert(x < 0x100);
  175. return int_log2_table[x];
  176. };
  177. /**
  178. * calculate the integer logarithm base 2
  179. * @param {number} x
  180. * @return {number}
  181. */
  182. v86util.int_log2 = function(x)
  183. {
  184. x >>>= 0;
  185. dbg_assert(x > 0);
  186. // http://jsperf.com/integer-log2/6
  187. var tt = x >>> 16;
  188. if(tt)
  189. {
  190. var t = tt >>> 8;
  191. if(t)
  192. {
  193. return 24 + int_log2_table[t];
  194. }
  195. else
  196. {
  197. return 16 + int_log2_table[tt];
  198. }
  199. }
  200. else
  201. {
  202. var t = x >>> 8;
  203. if(t)
  204. {
  205. return 8 + int_log2_table[t];
  206. }
  207. else
  208. {
  209. return int_log2_table[x];
  210. }
  211. }
  212. };
  213. })();
  214. /**
  215. * @constructor
  216. *
  217. * Queue wrapper around Uint8Array
  218. * Used by devices such as the PS2 controller
  219. */
  220. function ByteQueue(size)
  221. {
  222. var data = new Uint8Array(size),
  223. start,
  224. end;
  225. dbg_assert((size & size - 1) === 0);
  226. this.length = 0;
  227. this.push = function(item)
  228. {
  229. if(this.length === size)
  230. {
  231. // intentional overwrite
  232. }
  233. else
  234. {
  235. this.length++;
  236. }
  237. data[end] = item;
  238. end = end + 1 & size - 1;
  239. };
  240. this.shift = function()
  241. {
  242. if(!this.length)
  243. {
  244. return -1;
  245. }
  246. else
  247. {
  248. var item = data[start];
  249. start = start + 1 & size - 1;
  250. this.length--;
  251. return item;
  252. }
  253. };
  254. this.peek = function()
  255. {
  256. if(!this.length)
  257. {
  258. return -1;
  259. }
  260. else
  261. {
  262. return data[start];
  263. }
  264. };
  265. this.clear = function()
  266. {
  267. start = 0;
  268. end = 0;
  269. this.length = 0;
  270. };
  271. this.clear();
  272. }
  273. /**
  274. * @constructor
  275. *
  276. * Queue wrapper around Float32Array
  277. * Used by devices such as the sound blaster sound card
  278. */
  279. function FloatQueue(size)
  280. {
  281. this.size = size;
  282. this.data = new Float32Array(size);
  283. this.start = 0;
  284. this.end = 0;
  285. this.length = 0;
  286. dbg_assert((size & size - 1) === 0);
  287. }
  288. FloatQueue.prototype.push = function(item)
  289. {
  290. if(this.length === this.size)
  291. {
  292. // intentional overwrite
  293. this.start = this.start + 1 & this.size - 1;
  294. }
  295. else
  296. {
  297. this.length++;
  298. }
  299. this.data[this.end] = item;
  300. this.end = this.end + 1 & this.size - 1;
  301. };
  302. FloatQueue.prototype.shift = function()
  303. {
  304. if(!this.length)
  305. {
  306. return undefined;
  307. }
  308. else
  309. {
  310. var item = this.data[this.start];
  311. this.start = this.start + 1 & this.size - 1;
  312. this.length--;
  313. return item;
  314. }
  315. };
  316. FloatQueue.prototype.shift_block = function(count)
  317. {
  318. var slice = new Float32Array(count);
  319. if(count > this.length)
  320. {
  321. count = this.length;
  322. }
  323. var slice_end = this.start + count;
  324. var partial = this.data.subarray(this.start, slice_end);
  325. slice.set(partial);
  326. if(slice_end >= this.size)
  327. {
  328. slice_end -= this.size;
  329. slice.set(this.data.subarray(0, slice_end), partial.length);
  330. }
  331. this.start = slice_end;
  332. this.length -= count;
  333. return slice;
  334. };
  335. FloatQueue.prototype.peek = function()
  336. {
  337. if(!this.length)
  338. {
  339. return undefined;
  340. }
  341. else
  342. {
  343. return this.data[this.start];
  344. }
  345. };
  346. FloatQueue.prototype.clear = function()
  347. {
  348. this.start = 0;
  349. this.end = 0;
  350. this.length = 0;
  351. };
  352. /**
  353. * Simple circular queue for logs
  354. *
  355. * @param {number} size
  356. * @constructor
  357. */
  358. function CircularQueue(size)
  359. {
  360. this.data = [];
  361. this.index = 0;
  362. this.size = size;
  363. }
  364. CircularQueue.prototype.add = function(item)
  365. {
  366. this.data[this.index] = item;
  367. this.index = (this.index + 1) % this.size;
  368. };
  369. CircularQueue.prototype.toArray = function()
  370. {
  371. return [].slice.call(this.data, this.index).concat([].slice.call(this.data, 0, this.index));
  372. };
  373. CircularQueue.prototype.clear = function()
  374. {
  375. this.data = [];
  376. this.index = 0;
  377. };
  378. /**
  379. * @param {Array} new_data
  380. */
  381. CircularQueue.prototype.set = function(new_data)
  382. {
  383. this.data = new_data;
  384. this.index = 0;
  385. };
  386. function dump_file(ab, name)
  387. {
  388. if(!(ab instanceof Array))
  389. {
  390. ab = [ab];
  391. }
  392. var blob = new Blob(ab);
  393. download(blob, name);
  394. }
  395. function download(file_or_blob, name)
  396. {
  397. var a = document.createElement("a");
  398. a["download"] = name;
  399. a.href = window.URL.createObjectURL(file_or_blob);
  400. a.dataset["downloadurl"] = ["application/octet-stream", a["download"], a.href].join(":");
  401. if(document.createEvent)
  402. {
  403. var ev = document.createEvent("MouseEvent");
  404. ev.initMouseEvent("click", true, true, window,
  405. 0, 0, 0, 0, 0, false, false, false, false, 0, null);
  406. a.dispatchEvent(ev);
  407. }
  408. else
  409. {
  410. a.click();
  411. }
  412. window.URL.revokeObjectURL(a.href);
  413. }
  414. /**
  415. * A simple 1d bitmap
  416. * @constructor
  417. */
  418. v86util.Bitmap = function(length_or_buffer)
  419. {
  420. if(typeof length_or_buffer === "number")
  421. {
  422. this.view = new Uint8Array(length_or_buffer + 7 >> 3);
  423. }
  424. else if(length_or_buffer instanceof ArrayBuffer)
  425. {
  426. this.view = new Uint8Array(length_or_buffer);
  427. }
  428. else
  429. {
  430. dbg_assert(false, "v86util.Bitmap: Invalid argument");
  431. }
  432. };
  433. v86util.Bitmap.prototype.set = function(index, value)
  434. {
  435. const bit_index = index & 7;
  436. const byte_index = index >> 3;
  437. const bit_mask = 1 << bit_index;
  438. this.view[byte_index] =
  439. value ? this.view[byte_index] | bit_mask : this.view[byte_index] & ~bit_mask;
  440. };
  441. v86util.Bitmap.prototype.get = function(index)
  442. {
  443. const bit_index = index & 7;
  444. const byte_index = index >> 3;
  445. return this.view[byte_index] >> bit_index & 1;
  446. };
  447. v86util.Bitmap.prototype.get_buffer = function()
  448. {
  449. return this.view.buffer;
  450. };
  451. if(typeof XMLHttpRequest === "undefined")
  452. {
  453. v86util.load_file = load_file_nodejs;
  454. }
  455. else
  456. {
  457. v86util.load_file = load_file;
  458. }
  459. /**
  460. * @param {string} filename
  461. * @param {Object} options
  462. * @param {number=} n_tries
  463. */
  464. function load_file(filename, options, n_tries)
  465. {
  466. var http = new XMLHttpRequest();
  467. http.open(options.method || "get", filename, true);
  468. if(options.as_json)
  469. {
  470. http.responseType = "json";
  471. }
  472. else
  473. {
  474. http.responseType = "arraybuffer";
  475. }
  476. if(options.headers)
  477. {
  478. var header_names = Object.keys(options.headers);
  479. for(var i = 0; i < header_names.length; i++)
  480. {
  481. var name = header_names[i];
  482. http.setRequestHeader(name, options.headers[name]);
  483. }
  484. }
  485. if(options.range)
  486. {
  487. let start = options.range.start;
  488. let end = start + options.range.length - 1;
  489. http.setRequestHeader("Range", "bytes=" + start + "-" + end);
  490. // Abort if server responds with complete file in response to range
  491. // request, to prevent downloading large files from broken http servers
  492. http.onreadystatechange = function()
  493. {
  494. if(http.status === 200)
  495. {
  496. http.abort();
  497. }
  498. };
  499. }
  500. http.onload = function(e)
  501. {
  502. if(http.readyState === 4)
  503. {
  504. if(http.status !== 200 && http.status !== 206)
  505. {
  506. console.error("Loading the image " + filename + " failed (status %d)", http.status);
  507. if(http.status >= 500 && http.status < 600)
  508. {
  509. retry();
  510. }
  511. }
  512. else if(http.response)
  513. {
  514. options.done && options.done(http.response, http);
  515. }
  516. }
  517. };
  518. http.onerror = function(e)
  519. {
  520. console.error("Loading the image " + filename + " failed", e);
  521. retry();
  522. };
  523. if(options.progress)
  524. {
  525. http.onprogress = function(e)
  526. {
  527. options.progress(e);
  528. };
  529. }
  530. http.send(null);
  531. function retry()
  532. {
  533. const number_of_tries = n_tries || 0;
  534. const timeout = [1, 1, 2, 3, 5, 8, 13, 21][number_of_tries] || 34;
  535. setTimeout(() => {
  536. load_file(filename, options, number_of_tries + 1);
  537. }, 1000 * timeout);
  538. }
  539. }
  540. function load_file_nodejs(filename, options)
  541. {
  542. let fs = require("fs");
  543. if(options.range)
  544. {
  545. dbg_assert(!options.as_json);
  546. fs["open"](filename, "r", (err, fd) =>
  547. {
  548. if(err) throw err;
  549. let length = options.range.length;
  550. var buffer = Buffer.allocUnsafe(length);
  551. fs["read"](fd, buffer, 0, length, options.range.start, (err, bytes_read) =>
  552. {
  553. if(err) throw err;
  554. dbg_assert(bytes_read === length);
  555. options.done && options.done(new Uint8Array(buffer));
  556. fs["close"](fd, (err) => {
  557. if(err) throw err;
  558. });
  559. });
  560. });
  561. }
  562. else
  563. {
  564. var o = {
  565. encoding: options.as_json ? "utf-8" : null,
  566. };
  567. fs["readFile"](filename, o, function(err, data)
  568. {
  569. if(err)
  570. {
  571. console.log("Could not read file:", filename, err);
  572. }
  573. else
  574. {
  575. var result = data;
  576. if(options.as_json)
  577. {
  578. result = JSON.parse(result);
  579. }
  580. else
  581. {
  582. result = new Uint8Array(result).buffer;
  583. }
  584. options.done(result);
  585. }
  586. });
  587. }
  588. }
  589. // Reads len characters at offset from Memory object mem as a JS string
  590. v86util.read_sized_string_from_mem = function read_sized_string_from_mem(mem, offset, len)
  591. {
  592. offset >>>= 0;
  593. len >>>= 0;
  594. return String.fromCharCode(...new Uint8Array(mem.buffer, offset, len));
  595. };