diff --git a/dist/files_reader-1.1.4.tar.gz b/dist/files_reader-1.1.4.tar.gz
new file mode 100644
index 0000000..3c3e86f
Binary files /dev/null and b/dist/files_reader-1.1.4.tar.gz differ
diff --git a/files_reader/appinfo/info.xml b/files_reader/appinfo/info.xml
index 7f86dfe..57a1780 100644
--- a/files_reader/appinfo/info.xml
+++ b/files_reader/appinfo/info.xml
@@ -30,7 +30,7 @@ See [README] for more exhaustive information on features and potential misfeatur
[README]: https://github.com/Yetangitu/owncloud-apps/blob/master/files_reader/README.md
]]>
- 1.1.3
+ 1.1.4
AGPL
Frank de Lange
diff --git a/files_reader/appinfo/signature.json b/files_reader/appinfo/signature.json
index 9f51e4e..2dd1e44 100644
--- a/files_reader/appinfo/signature.json
+++ b/files_reader/appinfo/signature.json
@@ -5,7 +5,7 @@
"TODO": "abec9035615be381c012c12229835c14f27a19ce92a82e6286f068376a6bbad0fa1dff4d091810fed694b51702359f147bf63c54ac755d0433df36fcaa415e3c",
"appinfo\/app.php": "c1289bdfe9cfd5c83a1f2768bf052cc36a222e99c96740f528b8a537c70a55c42751662f8fc1b027c610a59ad4f6aa21237dde827d1e41f4ba3538d857716c25",
"appinfo\/database.xml": "111a18a81749237ad4e23c94f0b0986e08964c8b1ae5495b0dfe082f4801dc8951eb7c862f96edc8493eba1c4808c2227cde1ab48572d806b026c37abab07a85",
- "appinfo\/info.xml": "b2d79fa682427bac7b88f38b4d928c44d7ea8621496759f1846a0e123dea41d03260e3d289aebbc1d88556cfdb79f25505a33e4ab15e50d1e63a8233c7a2cea2",
+ "appinfo\/info.xml": "76c7e6f211a519044b6f808c2621c01a248f3636900aab8059292aeda52868a479bb7982190e0d1d03dfda4ca3fe45fa287b37fa76bce2976bf392c0d8a23f7b",
"appinfo\/routes.php": "57153da351163ca15b81e023a548257e7d31baeb32785fbf5132418b0393949d3590a8ee119b2f6ae8c0b65b05ac4b5ae97cd9e9b2889576efc2bfb890684c85",
"img\/app.svg": "e4232de28db93900ea90cc91e60e26daccd137b5ffed7243edf9bc45b51edba39e917ca2875fcfcbf0a3b741173e99b8c2eff7ad371c40d5617aec74b56e6a68",
"img\/book.png": "9804b463779b32d1b8d9ced85a11f7e3201d9aef9a48b55b1381ae6addf19a78f5a3990c7201ce5702e798fd6bfad36c7a8e11e699f851351adf9b5b85cb3e68",
@@ -33,15 +33,15 @@
"lib\/Service\/PreferenceService.php": "2b52a28a074eba355154cbdad80358aaac394635ca91698ec3d7876eee0dc7e154064d8fbbe5465e7bbc783e618fbbf9ea1f07acd3cc4ce5b84116f7d8aed81c",
"lib\/Service\/Service.php": "cdf97b612fde0bec7846c0005280f1492d63e705b753c4aeb1df2c2ad2612a122d49eb15b6e7dfbe7fd7320762171a6ac6324181511dc28ec0e1e27092872eae",
"lib\/Utility\/Time.php": "58f760099eaf647718b491a7c21012398906bbe3ac7363aeceffff1a98d952f9a76697ddff2c00abe25e6c8db1cdf4d5994c1ab7d9474a05afb76b444ab9fed6",
- "templates\/cbreader.php": "fcc9db0e1c195a1718d47fac3a1134211ec9049d6a91df49132c6cd87660a85d931fd3c4105d2da48129e60f08aaeab679f6672fb74fc9a464fc892aa1fc4cbf",
- "templates\/epubreader.php": "2cf4fc9e81ff7627b3a920b4616284bd546a72c2441a9dfe2be974d21a686fab45dbda7535a0c58a67ea7af3defdfc319553e483497360ff29a7985788ba1e4e",
+ "templates\/cbreader.php": "0e825cda7619556a3faf7e87bc53d7d9eebe271f39cc4188c4e24ee61e5a48d58cd6b3672bacf39f3316203370d9792092d3bbc08cb39b2f7cccc67bf088f0e0",
+ "templates\/epubreader.php": "ce889687d8ef5f66a617fc329f3bf1ad69cc46362e90ebeab7b437500de05d9742fda2e4e3adf4aaadc004255bf48988b38c6c43c6072ab6a9f1f38e05a39d13",
"templates\/pdfreader.php": "c53cfcc0edb8b269dbf8d2cf5d9291ac5fa00cbed52a3ce5413464ba8d72bd43c75b0753cd03cb8c4f685fe72399aa0f4146b43256895ccfc4731a38d7b34d1e",
"vendor\/bartaz\/jquery.highlight.js": "2071d929cf62d8ef6cb85df2bef1d556d21c06fd23233e4938e8ba43df794ebb167b7ef75a50c10f01c9c59af76ae4278f5d254d1b0dcfca1a7201c35cfe2e33",
"vendor\/bgrins\/spectrum.css": "0d51df46221e84245609bf96f64fcf3aafdb9d2588a14e94382e78e605763bca1fd652adbfdacfce2324ea2d264baa3f1922a628e8c6ff55d4cc221ddbe05c15",
"vendor\/bgrins\/spectrum.js": "a7e0d8302e03348effecea4cad9cb59ed861d5268552d4829af68b4d77f2022ddb46817b5a419e57f17c19e8eae48d43ae6ba858258c71f9bd658930b4484b21",
"vendor\/bitjs\/archive\/archive.js": "e1d47d6129c54faea42c572e37502bd44f4af70710e9897c0dd6f1e064edcaac9e6bb77d9eb6b93e659769d87c52381875ee4e00b9f1e4e66c8ddd976951a1d6",
"vendor\/bitjs\/archive\/rarvm.js": "7257ecafd56b2cae8e2ed4abdad429ad133c3ddd9271505750248a9008915a619d996c583e832b722c5757f6f3ca8b8dd20043af076a9ddc93e043917d685f69",
- "vendor\/bitjs\/archive\/unrar.js": "be2368d114be905df3f32be2928fc7204446d0ffd60bd68d50624e79cfd58f6684279f6b3f1a7d43413f1ae25f9bd0b88c398a9229e31a1c80ee58d54f1a5775",
+ "vendor\/bitjs\/archive\/unrar.js": "35fa0539f64141c6812a440f829e0a2c99ec9470a462cd561addb3eb8ab61df1681881a2c6d87ab2046e6b656fedba28bd21975bf38d1769aa5bf85da90232a3",
"vendor\/bitjs\/archive\/untar.js": "c76b6d7bf6e9ebb0c0837a083adb059c88f4054521e6d0e00e6e6b83119f194127b76473e7f61c507ad7c0802cf94089f07971c0ad61450ae2b5f1745758a691",
"vendor\/bitjs\/archive\/unzip.js": "6e96afc92d455895f4172a932cec5829e67b9366d01cb57992a844ed494c98b550b4ca61b2ffaca0b7b9094cf2da9ba6054e939b55732e040be87685e7760cdd",
"vendor\/bitjs\/io\/bitstream.js": "526d7463d45bbe9d05461becfbbd6d8d397b46262400196714fe7a93110b36766ad224ee55de8a8c1952e337336ba735a36530877c06de85aa3e691a3370ff67",
@@ -127,6 +127,6 @@
"vendor\/pixastic\/pixastic_combined.js": "a69bff7c76e6cdce7de3184184114e10f2ddcb369a2de03b1986c9a74d71f67f038d452d60df4a638e12420314bdc5c052a791a98d22ae5347a5f43bffd8b2f2",
"vendor\/sindresorhus\/screenfull.js": "29d23b28b76827f037265ce60ef5cf76c0d560c7aece03593021feae9f6220c3170abf5a8b5f380885fe15e9204425fb5446fef61c3b0ca83d6f332eb1bd1571"
},
- "signature": "tYN7z\/ye042ru5KSdR9qgEjwOLA69s1XUe2t1KpZOO0AL2GgIrM9WmOivVpNOEkVuH\/SbWQh28fkxMlkDvsEvxmFJ7ZO3g1V3sktz2YdgMjXvXsTMhy1\/d+hl5Aq3e6eYgROAWpNWvF3zwzgrpxaSoQ78KQWGHU6uooN8RTBCMuk1yW812hZOzRFVxicNmthZXPPMlEEwD3PK7JmcNq66o3qvkGEkWx73nN1tuErrFf4qaDWxCI2gBqueFmHUVZi0yH3cwozSPYlEZyD0xFp9aV30TLpSfXXPPHh6Iu2iB3TcRLWHb2+Vwau\/RvZ9s+GWp9vKd0vOeVv5jdL2mQHorqMLtBN5VXtifLAGNBTkeU7R0O\/Ru7I7vypv4g37qwgbFYLh2DC95KLAwa0qmQDlQvpIyxvQu5l2w6CGQPcQ\/B0qL\/MZ82pxdnLETljl5IsgZcKfuhMDEX61gU6Tf6j3IhdamuMiVHL5FwFB2igMKhDou5LkJHkQtKUA9HRnp4weElN0o97hU13+tjlLel\/6PCQY1jMOSaMAw1d6lcqqe1s2BmdK+blkj5HahTgGwBT\/3qOoPxmF\/CevrusV1kg9tIAGLPIYce1YazJDI9lJTsAvpHQoFXHIiO1tvtWQoBwgjk71r0rteoUHq5pnmTAlVAmB8gdyx7xjZWTQa2KHW8=",
+ "signature": "dtHMPFFwXQVRLcziel0xjlsUMvHIxNr770i3EI2yQMa4w8BLTqGTOwCF2Y+M8TvhNPEpLqm3BZuyQShJ\/y\/j+vWEz+OCP19tgts8uJ7btsxDsZRBSWDEYQ6V51mVaGDaSAHXcgPFDDCHcfxAz29rPwopyOU4XwGnOd8GcH+M2sNydIkDj7rJhfR2vJOFe0kgU36bQhKa7BM3Ot+Uu4rLVFFRa1pRUXT1XF9FgpBwkbZVOi9kYKHChihAvNn2c4vE41lPGovmJkqHri0t01dgjCSURrYxc9Z3EVcT6q5PaL+YOeEEsn\/391C4C9OggaIF8nd6pvrZ+YgKBO3pSKbq9rMgbS3y7RWsckFDOE65fsFvUIICWo\/olXg7fbfIZs80go3iyzF+E3BgbApcQIiaQ2kFYfsoxIbR8Mvo+IlFSOWXTCYdx23cP7I7apbngrTURilEEwLXHDNPs4G7qFzHpU1csu1jaLCR7Y3NA\/pDDOgwRquHaFfhxtqdVMMgnuMUteBoQHhBcnWzTk6sGKycrQn\/kH1A8yUwalbDQiVSs0G3fZrzVuPgC+zglZ58KiPf8O5D7eCQ2gws\/EJq6aDWLvr0Hq3duZSJE52JyOjgUw0MbFbIsMOb+wcgszn\/7sL3um59WvYb6GtMSPGru4HUe0\/wXo9o99E8QMQ7Km7rgi8=",
"certificate": "-----BEGIN CERTIFICATE-----\r\nMIIE+TCCAuECAhBPMA0GCSqGSIb3DQEBCwUAMG0xCzAJBgNVBAYTAlVTMQ8wDQYD\r\nVQQIDAZCb3N0b24xFjAUBgNVBAoMDW93bkNsb3VkIEluYy4xNTAzBgNVBAMMLG93\r\nbkNsb3VkIENvZGUgU2lnbmluZyBJbnRlcm1lZGlhdGUgQXV0aG9yaXR5MB4XDTE4\r\nMDExODIxMzMyNFoXDTI4MDExNjIxMzMyNFowFzEVMBMGA1UEAwwMZmlsZXNfcmVh\r\nZGVyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA2Dro3KQnJEnKeJVG\r\nnhKvrQSzLNyphcF5UNj4j3Km6wLcb86RkWtq6uX5eR6g0HI9NFZF3qxBLUGN8bpM\r\ngGyalTLwj5IsDYO\/naSZe\/wXNCBl82vZq+jjnDCYll7r2yNjTFVzRtH+o9AIQlmM\r\npt8+PCcw8n8QqlXUIq9A3kb8wggczEZnw6bCudDLQWXHYKD5\/tn7n06h9fA4VxfB\r\nQMyuv8hIjKEQqun3Qqvi3DfaR52sbeDvv9bGACxWqjiE3P6sZvL7MlDqJ5KeqWoM\r\n4qPGkgDusTtXuO7h3ro2H4NMydWXcrbUAPTXcAPo2jzTfhV8m9xQDc+45VlONjIp\r\nTFwV1oF53hnu81TlNniz1RTbDYMkExvPYtobNjNIR+VsOQs8Gq9iEDEIxyqCm2it\r\ncuMjeFhYr1rjyeS21i6cNtD\/kMxSFwKnluQPrb49pK6g2Nq5Go1iP8WgT12hAQhr\r\ni7wwH32bLe11xnD53ko6pAzhqmciaSHLxkZCm+eyTNwJzQa0uQ+gAD8gJ7bVQUxg\r\nPSjm1amfhMAzsHIraMFqzlz7IWjWA7vJGkR9DcweYBXsyt6ZloLekPsNxEKnuh3F\r\ngjBHEoy7iPLmDxGvTfPW76r6vBwBF9JgIrhJzRMtTHTsYX9olblQr957SLyiZaqJ\r\n\/kKCQZ3cKPhWBh1KydMjPlXbGFUCAwEAATANBgkqhkiG9w0BAQsFAAOCAgEAcYdp\r\ntoKFBZV7rswQ9yL6Y8F\/\/VUM1J1jincG3soCz5\/n5rL0TDekS0FI8eNWX0tay0ne\r\n3\/FZ93\/fb+gkQf7KutU\/9EWZwnc90XNq+Q3\/3DWz+nIm3EFttz6GioVYz7XEAx3A\r\nSMWeWY05ei9V7SVSnqglhouhLENrKKI8ilzGI\/pEtCs3RUv3xi7GPBdCDnvqqeXy\r\nRHrP7ZFe3v1go84v1MwQOt4\/OtaYk\/4HU51oxzUI8yDMNtLNmZm7gqLbT9bRsWCz\r\n5gqFa15K4X7sVL\/ECI72zEDZsF0RtmTCp9fJLoYXIPPQ1CACi0K0hB\/ssE6BC9Zl\r\naUXcbZ2BLwvQiZmEUhyyr0WYK4D\/dE4LbYqYpDDGRhXpf4cRhQahsYL8aMzZtZfl\r\nNDE4PN\/4sV6id6MnwrtDmsW3frMlkhzrsm8ftzwkbIyJD1Io5OAOJn6oxN2sjlWD\r\ngof0tuweAlTGuAI7\/CUA2yMZb45MFkLWDExzZsiVy9UtU641cDzOxAbg7UIeTBRZ\r\nYUdl5ci1f8299Yridc4n70yQg2GHwa8YJ6p42f93sTOo0E1UAX1+eBmuAmc\/eBq0\r\nFjjmMyPZy7EhElAUa2sqw5QS2\/AK34P0rccCaJerRJ0mU54neL5qSEuuPQnVcn\/\/\r\n3LGndYF8t5kHI3iXV3TJ2vyagUkWeDl6z9pyW0Y=\r\n-----END CERTIFICATE-----"
}
\ No newline at end of file
diff --git a/files_reader/templates/cbreader.php b/files_reader/templates/cbreader.php
index 0a2c124..e48e6fe 100644
--- a/files_reader/templates/cbreader.php
+++ b/files_reader/templates/cbreader.php
@@ -12,7 +12,7 @@
$preferences = $_['preferences'];
$metadata = $_['metadata'];
$annotations = $_['annotations'];
- $title = htmlentities(basename($dllink));
+ $title = htmlentities(basename($downloadLink));
$revision = '0047';
$version = \OCP\App::getAppVersion('files_reader') . '.' . $revision;
@@ -34,7 +34,7 @@
-
+
diff --git a/files_reader/templates/epubreader.php b/files_reader/templates/epubreader.php
index 16032e7..e3b0522 100644
--- a/files_reader/templates/epubreader.php
+++ b/files_reader/templates/epubreader.php
@@ -34,7 +34,7 @@
-
+
diff --git a/files_reader/vendor/bitjs/archive/unrar.js b/files_reader/vendor/bitjs/archive/unrar.js
index fcad87e..a2e4c7c 100644
--- a/files_reader/vendor/bitjs/archive/unrar.js
+++ b/files_reader/vendor/bitjs/archive/unrar.js
@@ -8,10 +8,1707 @@
*/
// This file expects to be invoked as a Worker (see onmessage below).
-importScripts('../io/bitstream.js');
-importScripts('../io/bytebuffer.js');
-importScripts('archive.js');
-importScripts('rarvm.js');
+//importScripts('../io/bitstream.js');
+
+/*
+ * bitstream.js
+ *
+ * Provides readers for bitstreams.
+ *
+ * Licensed under the MIT License
+ *
+ * Copyright(c) 2011 Google Inc.
+ * Copyright(c) 2011 antimatter15
+ */
+
+var bitjs = bitjs || {};
+bitjs.io = bitjs.io || {};
+
+
+/**
+ * This bit stream peeks and consumes bits out of a binary stream.
+ */
+bitjs.io.BitStream = class {
+ /**
+ * @param {ArrayBuffer} ab An ArrayBuffer object or a Uint8Array.
+ * @param {boolean} rtl Whether the stream reads bits from the byte starting
+ * from bit 7 to 0 (true) or bit 0 to 7 (false).
+ * @param {Number} opt_offset The offset into the ArrayBuffer
+ * @param {Number} opt_length The length of this BitStream
+ */
+ constructor(ab, rtl, opt_offset, opt_length) {
+ if (!ab || !ab.toString || ab.toString() !== "[object ArrayBuffer]") {
+ throw "Error! BitArray constructed with an invalid ArrayBuffer object";
+ }
+
+ const offset = opt_offset || 0;
+ const length = opt_length || ab.byteLength;
+ this.bytes = new Uint8Array(ab, offset, length);
+ this.bytePtr = 0; // tracks which byte we are on
+ this.bitPtr = 0; // tracks which bit we are on (can have values 0 through 7)
+ this.peekBits = rtl ? this.peekBits_rtl : this.peekBits_ltr;
+ }
+
+ /**
+ * byte0 byte1 byte2 byte3
+ * 7......0 | 7......0 | 7......0 | 7......0
+ *
+ * The bit pointer starts at bit0 of byte0 and moves left until it reaches
+ * bit7 of byte0, then jumps to bit0 of byte1, etc.
+ * @param {number} n The number of bits to peek.
+ * @param {boolean=} movePointers Whether to move the pointer, defaults false.
+ * @return {number} The peeked bits, as an unsigned number.
+ */
+ peekBits_ltr(n, opt_movePointers) {
+ if (n <= 0 || typeof n != typeof 1) {
+ return 0;
+ }
+
+ const movePointers = opt_movePointers || false;
+ const bytes = this.bytes;
+ let bytePtr = this.bytePtr;
+ let bitPtr = this.bitPtr;
+ let result = 0;
+ let bitsIn = 0;
+
+ // keep going until we have no more bits left to peek at
+ // TODO: Consider putting all bits from bytes we will need into a variable and then
+ // shifting/masking it to just extract the bits we want.
+ // This could be considerably faster when reading more than 3 or 4 bits at a time.
+ while (n > 0) {
+ if (bytePtr >= bytes.length) {
+ throw "Error! Overflowed the bit stream! n=" + n + ", bytePtr=" + bytePtr + ", bytes.length=" +
+ bytes.length + ", bitPtr=" + bitPtr;
+ return -1;
+ }
+
+ const numBitsLeftInThisByte = (8 - bitPtr);
+ if (n >= numBitsLeftInThisByte) {
+ const mask = (bitjs.io.BitStream.BITMASK[numBitsLeftInThisByte] << bitPtr);
+ result |= (((bytes[bytePtr] & mask) >> bitPtr) << bitsIn);
+
+ bytePtr++;
+ bitPtr = 0;
+ bitsIn += numBitsLeftInThisByte;
+ n -= numBitsLeftInThisByte;
+ }
+ else {
+ const mask = (bitjs.io.BitStream.BITMASK[n] << bitPtr);
+ result |= (((bytes[bytePtr] & mask) >> bitPtr) << bitsIn);
+
+ bitPtr += n;
+ bitsIn += n;
+ n = 0;
+ }
+ }
+
+ if (movePointers) {
+ this.bitPtr = bitPtr;
+ this.bytePtr = bytePtr;
+ }
+
+ return result;
+ }
+
+ /**
+ * byte0 byte1 byte2 byte3
+ * 7......0 | 7......0 | 7......0 | 7......0
+ *
+ * The bit pointer starts at bit7 of byte0 and moves right until it reaches
+ * bit0 of byte0, then goes to bit7 of byte1, etc.
+ * @param {number} n The number of bits to peek.
+ * @param {boolean=} movePointers Whether to move the pointer, defaults false.
+ * @return {number} The peeked bits, as an unsigned number.
+ */
+ peekBits_rtl(n, opt_movePointers) {
+ if (n <= 0 || typeof n != typeof 1) {
+ return 0;
+ }
+
+ const movePointers = opt_movePointers || false;
+ const bytes = this.bytes;
+ let bytePtr = this.bytePtr;
+ let bitPtr = this.bitPtr;
+ let result = 0;
+
+ // keep going until we have no more bits left to peek at
+ // TODO: Consider putting all bits from bytes we will need into a variable and then
+ // shifting/masking it to just extract the bits we want.
+ // This could be considerably faster when reading more than 3 or 4 bits at a time.
+ while (n > 0) {
+ if (bytePtr >= bytes.length) {
+ throw "Error! Overflowed the bit stream! n=" + n + ", bytePtr=" + bytePtr + ", bytes.length=" +
+ bytes.length + ", bitPtr=" + bitPtr;
+ return -1;
+ }
+
+ const numBitsLeftInThisByte = (8 - bitPtr);
+ if (n >= numBitsLeftInThisByte) {
+ result <<= numBitsLeftInThisByte;
+ result |= (bitjs.io.BitStream.BITMASK[numBitsLeftInThisByte] & bytes[bytePtr]);
+ bytePtr++;
+ bitPtr = 0;
+ n -= numBitsLeftInThisByte;
+ }
+ else {
+ result <<= n;
+ result |= ((bytes[bytePtr] & (bitjs.io.BitStream.BITMASK[n] << (8 - n - bitPtr))) >> (8 - n - bitPtr));
+
+ bitPtr += n;
+ n = 0;
+ }
+ }
+
+ if (movePointers) {
+ this.bitPtr = bitPtr;
+ this.bytePtr = bytePtr;
+ }
+
+ return result;
+ }
+
+ /**
+ * Peek at 16 bits from current position in the buffer.
+ * Bit at (bytePtr,bitPtr) has the highest position in returning data.
+ * Taken from getbits.hpp in unrar.
+ * TODO: Move this out of BitStream and into unrar.
+ */
+ getBits() {
+ return (((((this.bytes[this.bytePtr] & 0xff) << 16) +
+ ((this.bytes[this.bytePtr+1] & 0xff) << 8) +
+ ((this.bytes[this.bytePtr+2] & 0xff))) >>> (8-this.bitPtr)) & 0xffff);
+ }
+
+ /**
+ * Reads n bits out of the stream, consuming them (moving the bit pointer).
+ * @param {number} n The number of bits to read.
+ * @return {number} The read bits, as an unsigned number.
+ */
+ readBits(n) {
+ return this.peekBits(n, true);
+ }
+
+ /**
+ * This returns n bytes as a sub-array, advancing the pointer if movePointers
+ * is true. Only use this for uncompressed blocks as this throws away remaining
+ * bits in the current byte.
+ * @param {number} n The number of bytes to peek.
+ * @param {boolean=} movePointers Whether to move the pointer, defaults false.
+ * @return {Uint8Array} The subarray.
+ */
+ peekBytes(n, opt_movePointers) {
+ if (n <= 0 || typeof n != typeof 1) {
+ return 0;
+ }
+
+ // from http://tools.ietf.org/html/rfc1951#page-11
+ // "Any bits of input up to the next byte boundary are ignored."
+ while (this.bitPtr != 0) {
+ this.readBits(1);
+ }
+
+ const movePointers = opt_movePointers || false;
+ let bytePtr = this.bytePtr;
+ let bitPtr = this.bitPtr;
+
+ const result = this.bytes.subarray(bytePtr, bytePtr + n);
+
+ if (movePointers) {
+ this.bytePtr += n;
+ }
+
+ return result;
+ }
+
+ /**
+ * @param {number} n The number of bytes to read.
+ * @return {Uint8Array} The subarray.
+ */
+ readBytes(n) {
+ return this.peekBytes(n, true);
+ }
+}
+
+// mask for getting N number of bits (0-8)
+bitjs.io.BitStream.BITMASK = [0, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF ];
+
+
+//importScripts('../io/bytebuffer.js');
+
+/*
+ * bytestream.js
+ *
+ * Provides a writer for bytes.
+ *
+ * Licensed under the MIT License
+ *
+ * Copyright(c) 2011 Google Inc.
+ * Copyright(c) 2011 antimatter15
+ */
+
+var bitjs = bitjs || {};
+bitjs.io = bitjs.io || {};
+
+
+/**
+ * A write-only Byte buffer which uses a Uint8 Typed Array as a backing store.
+ */
+bitjs.io.ByteBuffer = class {
+ /**
+ * @param {number} numBytes The number of bytes to allocate.
+ */
+ constructor(numBytes) {
+ if (typeof numBytes != typeof 1 || numBytes <= 0) {
+ throw "Error! ByteBuffer initialized with '" + numBytes + "'";
+ }
+ this.data = new Uint8Array(numBytes);
+ this.ptr = 0;
+ }
+
+
+ /**
+ * @param {number} b The byte to insert.
+ */
+ insertByte(b) {
+ // TODO: throw if byte is invalid?
+ this.data[this.ptr++] = b;
+ }
+
+ /**
+ * @param {Array.|Uint8Array|Int8Array} bytes The bytes to insert.
+ */
+ insertBytes(bytes) {
+ // TODO: throw if bytes is invalid?
+ this.data.set(bytes, this.ptr);
+ this.ptr += bytes.length;
+ }
+
+ /**
+ * Writes an unsigned number into the next n bytes. If the number is too large
+ * to fit into n bytes or is negative, an error is thrown.
+ * @param {number} num The unsigned number to write.
+ * @param {number} numBytes The number of bytes to write the number into.
+ */
+ writeNumber(num, numBytes) {
+ if (numBytes < 1) {
+ throw 'Trying to write into too few bytes: ' + numBytes;
+ }
+ if (num < 0) {
+ throw 'Trying to write a negative number (' + num +
+ ') as an unsigned number to an ArrayBuffer';
+ }
+ if (num > (Math.pow(2, numBytes * 8) - 1)) {
+ throw 'Trying to write ' + num + ' into only ' + numBytes + ' bytes';
+ }
+
+ // Roll 8-bits at a time into an array of bytes.
+ const bytes = [];
+ while (numBytes-- > 0) {
+ const eightBits = num & 255;
+ bytes.push(eightBits);
+ num >>= 8;
+ }
+
+ this.insertBytes(bytes);
+ }
+
+ /**
+ * Writes a signed number into the next n bytes. If the number is too large
+ * to fit into n bytes, an error is thrown.
+ * @param {number} num The signed number to write.
+ * @param {number} numBytes The number of bytes to write the number into.
+ */
+ writeSignedNumber(num, numBytes) {
+ if (numBytes < 1) {
+ throw 'Trying to write into too few bytes: ' + numBytes;
+ }
+
+ const HALF = Math.pow(2, (numBytes * 8) - 1);
+ if (num >= HALF || num < -HALF) {
+ throw 'Trying to write ' + num + ' into only ' + numBytes + ' bytes';
+ }
+
+ // Roll 8-bits at a time into an array of bytes.
+ const bytes = [];
+ while (numBytes-- > 0) {
+ const eightBits = num & 255;
+ bytes.push(eightBits);
+ num >>= 8;
+ }
+
+ this.insertBytes(bytes);
+ }
+
+ /**
+ * @param {string} str The ASCII string to write.
+ */
+ writeASCIIString(str) {
+ for (let i = 0; i < str.length; ++i) {
+ const curByte = str.charCodeAt(i);
+ if (curByte < 0 || curByte > 255) {
+ throw 'Trying to write a non-ASCII string!';
+ }
+ this.insertByte(curByte);
+ }
+ };
+}
+
+//importScripts('archive.js');
+
+/**
+ * archive.js
+ *
+ * Provides base functionality for unarchiving.
+ *
+ * Licensed under the MIT License
+ *
+ * Copyright(c) 2011 Google Inc.
+ */
+
+var bitjs = bitjs || {};
+bitjs.archive = bitjs.archive || {};
+
+/**
+ * An unarchive event.
+ */
+bitjs.archive.UnarchiveEvent = class {
+ /**
+ * @param {string} type The event type.
+ */
+ constructor(type) {
+ /**
+ * The event type.
+ * @type {string}
+ */
+ this.type = type;
+ }
+}
+
+/**
+ * The UnarchiveEvent types.
+ */
+bitjs.archive.UnarchiveEvent.Type = {
+ START: 'start',
+ PROGRESS: 'progress',
+ EXTRACT: 'extract',
+ FINISH: 'finish',
+ INFO: 'info',
+ ERROR: 'error'
+};
+
+/**
+ * Useful for passing info up to the client (for debugging).
+ */
+bitjs.archive.UnarchiveInfoEvent = class extends bitjs.archive.UnarchiveEvent {
+ /**
+ * @param {string} msg The info message.
+ */
+ constructor(msg) {
+ super(bitjs.archive.UnarchiveEvent.Type.INFO);
+
+ /**
+ * The information message.
+ * @type {string}
+ */
+ this.msg = msg;
+ }
+}
+
+/**
+ * An unrecoverable error has occured.
+ */
+bitjs.archive.UnarchiveErrorEvent = class extends bitjs.archive.UnarchiveEvent {
+ /**
+ * @param {string} msg The error message.
+ */
+ constructor(msg) {
+ super(bitjs.archive.UnarchiveEvent.Type.ERROR);
+
+ /**
+ * The information message.
+ * @type {string}
+ */
+ this.msg = msg;
+ }
+}
+
+/**
+ * Start event.
+ */
+bitjs.archive.UnarchiveStartEvent = class extends bitjs.archive.UnarchiveEvent {
+ constructor() {
+ super(bitjs.archive.UnarchiveEvent.Type.START);
+ }
+}
+
+/**
+ * Finish event.
+ */
+bitjs.archive.UnarchiveFinishEvent = class extends bitjs.archive.UnarchiveEvent {
+ constructor() {
+ super(bitjs.archive.UnarchiveEvent.Type.FINISH);
+ }
+}
+
+/**
+ * Progress event.
+ */
+bitjs.archive.UnarchiveProgressEvent = class extends bitjs.archive.UnarchiveEvent {
+ /**
+ * @param {string} currentFilename
+ * @param {number} currentFileNumber
+ * @param {number} currentBytesUnarchivedInFile
+ * @param {number} currentBytesUnarchived
+ * @param {number} totalUncompressedBytesInArchive
+ * @param {number} totalFilesInArchive
+ */
+ constructor(currentFilename, currentFileNumber, currentBytesUnarchivedInFile,
+ currentBytesUnarchived, totalUncompressedBytesInArchive, totalFilesInArchive) {
+ super(bitjs.archive.UnarchiveEvent.Type.PROGRESS);
+
+ this.currentFilename = currentFilename;
+ this.currentFileNumber = currentFileNumber;
+ this.currentBytesUnarchivedInFile = currentBytesUnarchivedInFile;
+ this.totalFilesInArchive = totalFilesInArchive;
+ this.currentBytesUnarchived = currentBytesUnarchived;
+ this.totalUncompressedBytesInArchive = totalUncompressedBytesInArchive;
+ }
+}
+
+/**
+ * Extract event.
+ */
+bitjs.archive.UnarchiveExtractEvent = class extends bitjs.archive.UnarchiveEvent {
+ /**
+ * @param {UnarchivedFile} unarchivedFile
+ */
+ constructor(unarchivedFile) {
+ super(bitjs.archive.UnarchiveEvent.Type.EXTRACT);
+
+ /**
+ * @type {UnarchivedFile}
+ */
+ this.unarchivedFile = unarchivedFile;
+ }
+}
+
+/**
+ * All extracted files returned by an Unarchiver will implement
+ * the following interface:
+ *
+ * interface UnarchivedFile {
+ * string filename
+ * TypedArray fileData
+ * }
+ *
+ */
+
+/**
+ * Base class for all Unarchivers.
+ */
+bitjs.archive.Unarchiver = class {
+ /**
+ * @param {ArrayBuffer} arrayBuffer The Array Buffer.
+ * @param {string} opt_pathToBitJS Optional string for where the BitJS files are located.
+ */
+ constructor(arrayBuffer, opt_pathToBitJS) {
+ /**
+ * The ArrayBuffer object.
+ * @type {ArrayBuffer}
+ * @protected
+ */
+ this.ab = arrayBuffer;
+
+ /**
+ * The path to the BitJS files.
+ * @type {string}
+ * @private
+ */
+ this.pathToBitJS_ = opt_pathToBitJS || '/';
+
+ /**
+ * A map from event type to an array of listeners.
+ * @type {Map.}
+ */
+ this.listeners_ = {};
+ for (let type in bitjs.archive.UnarchiveEvent.Type) {
+ this.listeners_[bitjs.archive.UnarchiveEvent.Type[type]] = [];
+ }
+
+ /**
+ * Private web worker initialized during start().
+ * @type {Worker}
+ * @private
+ */
+ this.worker_ = null;
+ }
+
+ /**
+ * This method must be overridden by the subclass to return the script filename.
+ * @return {string} The script filename.
+ * @protected.
+ */
+ getScriptFileName() {
+ throw 'Subclasses of AbstractUnarchiver must overload getScriptFileName()';
+ }
+
+ /**
+ * Adds an event listener for UnarchiveEvents.
+ *
+ * @param {string} Event type.
+ * @param {function} An event handler function.
+ */
+ addEventListener(type, listener) {
+ if (type in this.listeners_) {
+ if (this.listeners_[type].indexOf(listener) == -1) {
+ this.listeners_[type].push(listener);
+ }
+ }
+ }
+
+ /**
+ * Removes an event listener.
+ *
+ * @param {string} Event type.
+ * @param {EventListener|function} An event listener or handler function.
+ */
+ removeEventListener(type, listener) {
+ if (type in this.listeners_) {
+ const index = this.listeners_[type].indexOf(listener);
+ if (index != -1) {
+ this.listeners_[type].splice(index, 1);
+ }
+ }
+ }
+
+ /**
+ * Receive an event and pass it to the listener functions.
+ *
+ * @param {bitjs.archive.UnarchiveEvent} e
+ * @private
+ */
+ handleWorkerEvent_(e) {
+ if ((e instanceof bitjs.archive.UnarchiveEvent || e.type) &&
+ this.listeners_[e.type] instanceof Array) {
+ this.listeners_[e.type].forEach(function (listener) { listener(e) });
+ if (e.type == bitjs.archive.UnarchiveEvent.Type.FINISH) {
+ this.worker_.terminate();
+ }
+ } else {
+ console.log(e);
+ }
+ }
+
+ /**
+ * Starts the unarchive in a separate Web Worker thread and returns immediately.
+ */
+ start() {
+ const me = this;
+ const scriptFileName = this.pathToBitJS_ + this.getScriptFileName();
+ if (scriptFileName) {
+ this.worker_ = new Worker(scriptFileName);
+
+ this.worker_.onerror = function(e) {
+ console.log('Worker error: message = ' + e.message);
+ throw e;
+ };
+
+ this.worker_.onmessage = function(e) {
+ if (typeof e.data == 'string') {
+ // Just log any strings the workers pump our way.
+ console.log(e.data);
+ } else {
+ // Assume that it is an UnarchiveEvent. Some browsers preserve the 'type'
+ // so that instanceof UnarchiveEvent returns true, but others do not.
+ me.handleWorkerEvent_(e.data);
+ }
+ };
+
+ this.worker_.postMessage({file: this.ab});
+ }
+ }
+
+ /**
+ * Terminates the Web Worker for this Unarchiver and returns immediately.
+ */
+ stop() {
+ if (this.worker_) {
+ this.worker_.terminate();
+ }
+ }
+}
+
+
+/**
+ * Unzipper
+ */
+bitjs.archive.Unzipper = class extends bitjs.archive.Unarchiver {
+ constructor(arrayBuffer, opt_pathToBitJS) {
+ super(arrayBuffer, opt_pathToBitJS);
+ }
+
+ getScriptFileName() { return 'archive/unzip.js'; }
+}
+
+
+/**
+ * Unrarrer
+ */
+bitjs.archive.Unrarrer = class extends bitjs.archive.Unarchiver {
+ constructor(arrayBuffer, opt_pathToBitJS) {
+ super(arrayBuffer, opt_pathToBitJS);
+ }
+
+ getScriptFileName() { return 'archive/unrar.js'; }
+}
+
+/**
+ * Untarrer
+ * @extends {bitjs.archive.Unarchiver}
+ * @constructor
+ */
+bitjs.archive.Untarrer = class extends bitjs.archive.Unarchiver {
+ constructor(arrayBuffer, opt_pathToBitJS) {
+ super(arrayBuffer, opt_pathToBitJS);
+ }
+
+ getScriptFileName() { return 'archive/untar.js'; };
+}
+
+/**
+ * Factory method that creates an unarchiver based on the byte signature found
+ * in the arrayBuffer.
+ * @param {ArrayBuffer} ab
+ * @param {string=} opt_pathToBitJS Path to the unarchiver script files.
+ * @return {bitjs.archive.Unarchiver}
+ */
+bitjs.archive.GetUnarchiver = function(ab, opt_pathToBitJS) {
+ let unarchiver = null;
+ const pathToBitJS = opt_pathToBitJS || '';
+ const h = new Uint8Array(ab, 0, 10);
+
+ if (h[0] == 0x52 && h[1] == 0x61 && h[2] == 0x72 && h[3] == 0x21) { // Rar!
+ unarchiver = new bitjs.archive.Unrarrer(ab, pathToBitJS);
+ } else if (h[0] == 0x50 && h[1] == 0x4B) { // PK (Zip)
+ unarchiver = new bitjs.archive.Unzipper(ab, pathToBitJS);
+ } else { // Try with tar
+ unarchiver = new bitjs.archive.Untarrer(ab, pathToBitJS);
+ }
+ return unarchiver;
+};
+
+//importScripts('rarvm.js');
+
+/**
+ * rarvm.js
+ *
+ * Licensed under the MIT License
+ *
+ * Copyright(c) 2017 Google Inc.
+ */
+
+/**
+ * CRC Implementation.
+ */
+const CRCTab = new Array(256).fill(0);
+
+// Helper functions between signed and unsigned integers.
+
+/**
+ * -1 becomes 0xffffffff
+ */
+function fromSigned32ToUnsigned32(val) {
+ return (val < 0) ? (val += 0x100000000) : val;
+}
+
+/**
+ * 0xffffffff becomes -1
+ */
+function fromUnsigned32ToSigned32(val) {
+ return (val >= 0x80000000) ? (val -= 0x100000000) : val;
+}
+
+/**
+ * -1 becomes 0xff
+ */
+function fromSigned8ToUnsigned8(val) {
+ return (val < 0) ? (val += 0x100) : val;
+}
+
+/**
+ * 0xff becomes -1
+ */
+function fromUnsigned8ToSigned8(val) {
+ return (val >= 0x80) ? (val -= 0x100) : val;
+}
+
+function InitCRC() {
+ for (let i = 0; i < 256; ++i) {
+ let c = i;
+ for (let j = 0; j < 8; ++j) {
+ // Read http://stackoverflow.com/questions/6798111/bitwise-operations-on-32-bit-unsigned-ints
+ // for the bitwise operator issue (JS interprets operands as 32-bit signed
+ // integers and we need to deal with unsigned ones here).
+ c = ((c & 1) ? ((c >>> 1) ^ 0xEDB88320) : (c >>> 1)) >>> 0;
+ }
+ CRCTab[i] = c;
+ }
+}
+
+/**
+ * @param {number} startCRC
+ * @param {Uint8Array} arr
+ * @return {number}
+ */
+function CRC(startCRC, arr) {
+ if (CRCTab[1] == 0) {
+ InitCRC();
+ }
+
+/*
+#if defined(LITTLE_ENDIAN) && defined(PRESENT_INT32) && defined(ALLOW_NOT_ALIGNED_INT)
+ while (Size>0 && ((long)Data & 7))
+ {
+ StartCRC=CRCTab[(byte)(StartCRC^Data[0])]^(StartCRC>>8);
+ Size--;
+ Data++;
+ }
+ while (Size>=8)
+ {
+ StartCRC^=*(uint32 *)Data;
+ StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8);
+ StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8);
+ StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8);
+ StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8);
+ StartCRC^=*(uint32 *)(Data+4);
+ StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8);
+ StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8);
+ StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8);
+ StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8);
+ Data+=8;
+ Size-=8;
+ }
+#endif
+*/
+
+ for (let i = 0; i < arr.length; ++i) {
+ const byte = ((startCRC ^ arr[i]) >>> 0) & 0xff;
+ startCRC = (CRCTab[byte] ^ (startCRC >>> 8)) >>> 0;
+ }
+
+ return startCRC;
+}
+
+// ============================================================================================== //
+
+
+/**
+ * RarVM Implementation.
+ */
+const VM_MEMSIZE = 0x40000;
+const VM_MEMMASK = (VM_MEMSIZE - 1);
+const VM_GLOBALMEMADDR = 0x3C000;
+const VM_GLOBALMEMSIZE = 0x2000;
+const VM_FIXEDGLOBALSIZE = 64;
+const MAXWINSIZE = 0x400000;
+const MAXWINMASK = (MAXWINSIZE - 1);
+
+/**
+ */
+const VM_Commands = {
+ VM_MOV: 0,
+ VM_CMP: 1,
+ VM_ADD: 2,
+ VM_SUB: 3,
+ VM_JZ: 4,
+ VM_JNZ: 5,
+ VM_INC: 6,
+ VM_DEC: 7,
+ VM_JMP: 8,
+ VM_XOR: 9,
+ VM_AND: 10,
+ VM_OR: 11,
+ VM_TEST: 12,
+ VM_JS: 13,
+ VM_JNS: 14,
+ VM_JB: 15,
+ VM_JBE: 16,
+ VM_JA: 17,
+ VM_JAE: 18,
+ VM_PUSH: 19,
+ VM_POP: 20,
+ VM_CALL: 21,
+ VM_RET: 22,
+ VM_NOT: 23,
+ VM_SHL: 24,
+ VM_SHR: 25,
+ VM_SAR: 26,
+ VM_NEG: 27,
+ VM_PUSHA: 28,
+ VM_POPA: 29,
+ VM_PUSHF: 30,
+ VM_POPF: 31,
+ VM_MOVZX: 32,
+ VM_MOVSX: 33,
+ VM_XCHG: 34,
+ VM_MUL: 35,
+ VM_DIV: 36,
+ VM_ADC: 37,
+ VM_SBB: 38,
+ VM_PRINT: 39,
+
+/*
+#ifdef VM_OPTIMIZE
+ VM_MOVB, VM_MOVD, VM_CMPB, VM_CMPD,
+
+ VM_ADDB, VM_ADDD, VM_SUBB, VM_SUBD, VM_INCB, VM_INCD, VM_DECB, VM_DECD,
+ VM_NEGB, VM_NEGD,
+#endif
+*/
+
+ // TODO: This enum value would be much larger if VM_OPTIMIZE.
+ VM_STANDARD: 40,
+};
+
+/**
+ */
+const VM_StandardFilters = {
+ VMSF_NONE: 0,
+ VMSF_E8: 1,
+ VMSF_E8E9: 2,
+ VMSF_ITANIUM: 3,
+ VMSF_RGB: 4,
+ VMSF_AUDIO: 5,
+ VMSF_DELTA: 6,
+ VMSF_UPCASE: 7,
+};
+
+/**
+ */
+const VM_Flags = {
+ VM_FC: 1,
+ VM_FZ: 2,
+ VM_FS: 0x80000000,
+};
+
+/**
+ */
+const VM_OpType = {
+ VM_OPREG: 0,
+ VM_OPINT: 1,
+ VM_OPREGMEM: 2,
+ VM_OPNONE: 3,
+};
+
+/**
+ * Finds the key that maps to a given value in an object. This function is useful in debugging
+ * variables that use the above enums.
+ * @param {Object} obj
+ * @param {number} val
+ * @return {string} The key/enum value as a string.
+ */
+function findKeyForValue(obj, val) {
+ for (let key in obj) {
+ if (obj[key] === val) {
+ return key;
+ }
+ }
+ return null;
+}
+
+function getDebugString(obj, val) {
+ let s = 'Unknown.';
+ if (obj === VM_Commands) {
+ s = 'VM_Commands.';
+ } else if (obj === VM_StandardFilters) {
+ s = 'VM_StandardFilters.';
+ } else if (obj === VM_Flags) {
+ s = 'VM_OpType.';
+ } else if (obj === VM_OpType) {
+ s = 'VM_OpType.';
+ }
+
+ return s + findKeyForValue(obj, val);
+}
+
+/**
+ */
+class VM_PreparedOperand {
+ constructor() {
+ /** @type {VM_OpType} */
+ this.Type;
+
+ /** @type {number} */
+ this.Data = 0;
+
+ /** @type {number} */
+ this.Base = 0;
+
+ // TODO: In C++ this is a uint*
+ /** @type {Array} */
+ this.Addr = null;
+ };
+
+ /** @return {string} */
+ toString() {
+ if (this.Type === null) {
+ return 'Error: Type was null in VM_PreparedOperand';
+ }
+ return '{ '
+ + 'Type: ' + getDebugString(VM_OpType, this.Type)
+ + ', Data: ' + this.Data
+ + ', Base: ' + this.Base
+ + ' }';
+ }
+}
+
+/**
+ */
+class VM_PreparedCommand {
+ constructor() {
+ /** @type {VM_Commands} */
+ this.OpCode;
+
+ /** @type {boolean} */
+ this.ByteMode = false;
+
+ /** @type {VM_PreparedOperand} */
+ this.Op1 = new VM_PreparedOperand();
+
+ /** @type {VM_PreparedOperand} */
+ this.Op2 = new VM_PreparedOperand();
+ }
+
+ /** @return {string} */
+ toString(indent) {
+ if (this.OpCode === null) {
+ return 'Error: OpCode was null in VM_PreparedCommand';
+ }
+ indent = indent || '';
+ return indent + '{\n'
+ + indent + ' OpCode: ' + getDebugString(VM_Commands, this.OpCode) + ',\n'
+ + indent + ' ByteMode: ' + this.ByteMode + ',\n'
+ + indent + ' Op1: ' + this.Op1.toString() + ',\n'
+ + indent + ' Op2: ' + this.Op2.toString() + ',\n'
+ + indent + '}';
+ }
+}
+
+/**
+ */
+class VM_PreparedProgram {
+ constructor() {
+ /** @type {Array} */
+ this.Cmd = [];
+
+ /** @type {Array} */
+ this.AltCmd = null;
+
+ /** @type {Uint8Array} */
+ this.GlobalData = new Uint8Array();
+
+ /** @type {Uint8Array} */
+ this.StaticData = new Uint8Array(); // static data contained in DB operators
+
+ /** @type {Uint32Array} */
+ this.InitR = new Uint32Array(7);
+
+ /**
+ * A pointer to bytes that have been filtered by a program.
+ * @type {Uint8Array}
+ */
+ this.FilteredData = null;
+ }
+
+ /** @return {string} */
+ toString() {
+ let s = '{\n Cmd: [\n';
+ for (let i = 0; i < this.Cmd.length; ++i) {
+ s += this.Cmd[i].toString(' ') + ',\n';
+ }
+ s += '],\n';
+ // TODO: Dump GlobalData, StaticData, InitR?
+ s += ' }\n';
+ return s;
+ }
+}
+
+/**
+ */
+class UnpackFilter {
+ constructor() {
+ /** @type {number} */
+ this.BlockStart = 0;
+
+ /** @type {number} */
+ this.BlockLength = 0;
+
+ /** @type {number} */
+ this.ExecCount = 0;
+
+ /** @type {boolean} */
+ this.NextWindow = false;
+
+ // position of parent filter in Filters array used as prototype for filter
+ // in PrgStack array. Not defined for filters in Filters array.
+ /** @type {number} */
+ this.ParentFilter = null;
+
+ /** @type {VM_PreparedProgram} */
+ this.Prg = new VM_PreparedProgram();
+ }
+}
+
+const VMCF_OP0 = 0;
+const VMCF_OP1 = 1;
+const VMCF_OP2 = 2;
+const VMCF_OPMASK = 3;
+const VMCF_BYTEMODE = 4;
+const VMCF_JUMP = 8;
+const VMCF_PROC = 16;
+const VMCF_USEFLAGS = 32;
+const VMCF_CHFLAGS = 64;
+
+const VM_CmdFlags = [
+ /* VM_MOV */ VMCF_OP2 | VMCF_BYTEMODE ,
+ /* VM_CMP */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS ,
+ /* VM_ADD */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS ,
+ /* VM_SUB */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS ,
+ /* VM_JZ */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS ,
+ /* VM_JNZ */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS ,
+ /* VM_INC */ VMCF_OP1 | VMCF_BYTEMODE | VMCF_CHFLAGS ,
+ /* VM_DEC */ VMCF_OP1 | VMCF_BYTEMODE | VMCF_CHFLAGS ,
+ /* VM_JMP */ VMCF_OP1 | VMCF_JUMP ,
+ /* VM_XOR */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS ,
+ /* VM_AND */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS ,
+ /* VM_OR */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS ,
+ /* VM_TEST */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS ,
+ /* VM_JS */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS ,
+ /* VM_JNS */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS ,
+ /* VM_JB */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS ,
+ /* VM_JBE */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS ,
+ /* VM_JA */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS ,
+ /* VM_JAE */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS ,
+ /* VM_PUSH */ VMCF_OP1 ,
+ /* VM_POP */ VMCF_OP1 ,
+ /* VM_CALL */ VMCF_OP1 | VMCF_PROC ,
+ /* VM_RET */ VMCF_OP0 | VMCF_PROC ,
+ /* VM_NOT */ VMCF_OP1 | VMCF_BYTEMODE ,
+ /* VM_SHL */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS ,
+ /* VM_SHR */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS ,
+ /* VM_SAR */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS ,
+ /* VM_NEG */ VMCF_OP1 | VMCF_BYTEMODE | VMCF_CHFLAGS ,
+ /* VM_PUSHA */ VMCF_OP0 ,
+ /* VM_POPA */ VMCF_OP0 ,
+ /* VM_PUSHF */ VMCF_OP0 | VMCF_USEFLAGS ,
+ /* VM_POPF */ VMCF_OP0 | VMCF_CHFLAGS ,
+ /* VM_MOVZX */ VMCF_OP2 ,
+ /* VM_MOVSX */ VMCF_OP2 ,
+ /* VM_XCHG */ VMCF_OP2 | VMCF_BYTEMODE ,
+ /* VM_MUL */ VMCF_OP2 | VMCF_BYTEMODE ,
+ /* VM_DIV */ VMCF_OP2 | VMCF_BYTEMODE ,
+ /* VM_ADC */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_USEFLAGS | VMCF_CHFLAGS ,
+ /* VM_SBB */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_USEFLAGS | VMCF_CHFLAGS ,
+ /* VM_PRINT */ VMCF_OP0 ,
+];
+
+
+/**
+ */
+class StandardFilterSignature {
+ /**
+ * @param {number} length
+ * @param {number} crc
+ * @param {VM_StandardFilters} type
+ */
+ constructor(length, crc, type) {
+ /** @type {number} */
+ this.Length = length;
+
+ /** @type {number} */
+ this.CRC = crc;
+
+ /** @type {VM_StandardFilters} */
+ this.Type = type;
+ }
+}
+
+/**
+ * @type {Array}
+ */
+const StdList = [
+ new StandardFilterSignature(53, 0xad576887, VM_StandardFilters.VMSF_E8),
+ new StandardFilterSignature(57, 0x3cd7e57e, VM_StandardFilters.VMSF_E8E9),
+ new StandardFilterSignature(120, 0x3769893f, VM_StandardFilters.VMSF_ITANIUM),
+ new StandardFilterSignature(29, 0x0e06077d, VM_StandardFilters.VMSF_DELTA),
+ new StandardFilterSignature(149, 0x1c2c5dc8, VM_StandardFilters.VMSF_RGB),
+ new StandardFilterSignature(216, 0xbc85e701, VM_StandardFilters.VMSF_AUDIO),
+ new StandardFilterSignature(40, 0x46b9c560, VM_StandardFilters.VMSF_UPCASE),
+];
+
+/**
+ * @constructor
+ */
+class RarVM {
+ constructor() {
+ /** @private {Uint8Array} */
+ this.mem_ = null;
+
+ /** @private {Uint32Array} */
+ this.R_ = new Uint32Array(8);
+
+ /** @private {number} */
+ this.flags_ = 0;
+ }
+
+ /**
+ * Initializes the memory of the VM.
+ */
+ init() {
+ if (!this.mem_) {
+ this.mem_ = new Uint8Array(VM_MEMSIZE);
+ }
+ }
+
+ /**
+ * @param {Uint8Array} code
+ * @return {VM_StandardFilters}
+ */
+ isStandardFilter(code) {
+ const codeCRC = (CRC(0xffffffff, code, code.length) ^ 0xffffffff) >>> 0;
+ for (let i = 0; i < StdList.length; ++i) {
+ if (StdList[i].CRC == codeCRC && StdList[i].Length == code.length)
+ return StdList[i].Type;
+ }
+
+ return VM_StandardFilters.VMSF_NONE;
+ }
+
+ /**
+ * @param {VM_PreparedOperand} op
+ * @param {boolean} byteMode
+ * @param {bitjs.io.BitStream} bstream A rtl bit stream.
+ */
+ decodeArg(op, byteMode, bstream) {
+ const data = bstream.peekBits(16);
+ if (data & 0x8000) {
+ op.Type = VM_OpType.VM_OPREG; // Operand is register (R[0]..R[7])
+ bstream.readBits(1); // 1 flag bit and...
+ op.Data = bstream.readBits(3); // ... 3 register number bits
+ op.Addr = [this.R_[op.Data]] // TODO &R[Op.Data] // Register address
+ } else {
+ if ((data & 0xc000) == 0) {
+ op.Type = VM_OpType.VM_OPINT; // Operand is integer
+ bstream.readBits(2); // 2 flag bits
+ if (byteMode) {
+ op.Data = bstream.readBits(8); // Byte integer.
+ } else {
+ op.Data = RarVM.readData(bstream); // 32 bit integer.
+ }
+ } else {
+ // Operand is data addressed by register data, base address or both.
+ op.Type = VM_OpType.VM_OPREGMEM;
+ if ((data & 0x2000) == 0) {
+ bstream.readBits(3); // 3 flag bits
+ // Base address is zero, just use the address from register.
+ op.Data = bstream.readBits(3); // (Data>>10)&7
+ op.Addr = [this.R_[op.Data]]; // TODO &R[op.Data]
+ op.Base = 0;
+ } else {
+ bstream.readBits(4); // 4 flag bits
+ if ((data & 0x1000) == 0) {
+ // Use both register and base address.
+ op.Data = bstream.readBits(3);
+ op.Addr = [this.R_[op.Data]]; // TODO &R[op.Data]
+ } else {
+ // Use base address only. Access memory by fixed address.
+ op.Data = 0;
+ }
+ op.Base = RarVM.readData(bstream); // Read base address.
+ }
+ }
+ }
+ }
+
+ /**
+ * @param {VM_PreparedProgram} prg
+ */
+ execute(prg) {
+ this.R_.set(prg.InitR);
+
+ const globalSize = Math.min(prg.GlobalData.length, VM_GLOBALMEMSIZE);
+ if (globalSize) {
+ this.mem_.set(prg.GlobalData.subarray(0, globalSize), VM_GLOBALMEMADDR);
+ }
+
+ const staticSize = Math.min(prg.StaticData.length, VM_GLOBALMEMSIZE - globalSize);
+ if (staticSize) {
+ this.mem_.set(prg.StaticData.subarray(0, staticSize), VM_GLOBALMEMADDR + globalSize);
+ }
+
+ this.R_[7] = VM_MEMSIZE;
+ this.flags_ = 0;
+
+ const preparedCodes = prg.AltCmd ? prg.AltCmd : prg.Cmd;
+ if (prg.Cmd.length > 0 && !this.executeCode(preparedCodes)) {
+ // Invalid VM program. Let's replace it with 'return' command.
+ preparedCode.OpCode = VM_Commands.VM_RET;
+ }
+
+ const dataView = new DataView(this.mem_.buffer, VM_GLOBALMEMADDR);
+ let newBlockPos = dataView.getUint32(0x20, true /* little endian */) & VM_MEMMASK;
+ const newBlockSize = dataView.getUint32(0x1c, true /* little endian */) & VM_MEMMASK;
+ if (newBlockPos + newBlockSize >= VM_MEMSIZE) {
+ newBlockPos = newBlockSize = 0;
+ }
+ prg.FilteredData = this.mem_.subarray(newBlockPos, newBlockPos + newBlockSize);
+
+ prg.GlobalData = new Uint8Array(0);
+
+ const dataSize = Math.min(dataView.getUint32(0x30), (VM_GLOBALMEMSIZE - VM_FIXEDGLOBALSIZE));
+ if (dataSize != 0) {
+ const len = dataSize + VM_FIXEDGLOBALSIZE;
+ prg.GlobalData = new Uint8Array(len);
+ prg.GlobalData.set(mem.subarray(VM_GLOBALMEMADDR, VM_GLOBALMEMADDR + len));
+ }
+ }
+
+ /**
+ * @param {Array} preparedCodes
+ * @return {boolean}
+ */
+ executeCode(preparedCodes) {
+ let codeIndex = 0;
+ let cmd = preparedCodes[codeIndex];
+ // TODO: Why is this an infinite loop instead of just returning
+ // when a VM_RET is hit?
+ while (1) {
+ switch (cmd.OpCode) {
+ case VM_Commands.VM_RET:
+ if (this.R_[7] >= VM_MEMSIZE) {
+ return true;
+ }
+ //SET_IP(GET_VALUE(false,(uint *)&Mem[R[7] & VM_MEMMASK]));
+ this.R_[7] += 4;
+ continue;
+
+ case VM_Commands.VM_STANDARD:
+ this.executeStandardFilter(cmd.Op1.Data);
+ break;
+
+ default:
+ console.error('RarVM OpCode not supported: ' + getDebugString(VM_Commands, cmd.OpCode));
+ break;
+ } // switch (cmd.OpCode)
+ codeIndex++;
+ cmd = preparedCodes[codeIndex];
+ }
+ }
+
+ /**
+ * @param {number} filterType
+ */
+ executeStandardFilter(filterType) {
+ switch (filterType) {
+ case VM_StandardFilters.VMSF_RGB: {
+ const dataSize = this.R_[4];
+ const width = this.R_[0] - 3;
+ const posR = this.R_[1];
+ const Channels = 3;
+ let srcOffset = 0;
+ let destOffset = dataSize;
+
+ // byte *SrcData=Mem,*DestData=SrcData+DataSize;
+ // SET_VALUE(false,&Mem[VM_GLOBALMEMADDR+0x20],DataSize);
+ const dataView = new DataView(this.mem_.buffer, VM_GLOBALMEMADDR /* offset */);
+ dataView.setUint32(0x20 /* byte offset */,
+ dataSize /* value */,
+ true /* little endian */);
+
+ if (dataSize >= (VM_GLOBALMEMADDR / 2) || posR < 0) {
+ break;
+ }
+
+ for (let curChannel = 0; curChannel < Channels; ++curChannel) {
+ let prevByte=0;
+
+ for (let i = curChannel; i < dataSize; i += Channels) {
+ let predicted;
+ const upperPos = i - width;
+ if (upperPos >= 3) {
+ const upperByte = this.mem_[destOffset + upperPos];
+ const upperLeftByte = this.mem_[destOffset + upperPos - 3];
+ predicted = prevByte + upperByte - upperLeftByte;
+
+ const pa = Math.abs(predicted - prevByte);
+ const pb = Math.abs(predicted - upperByte);
+ const pc = Math.abs(predicted - upperLeftByte);
+ if (pa <= pb && pa <= pc) {
+ predicted = prevByte;
+ } else if (pb <= pc) {
+ predicted = upperByte;
+ } else {
+ predicted = upperLeftByte;
+ }
+ } else {
+ predicted = prevByte;
+ }
+ //DestData[I]=PrevByte=(byte)(Predicted-*(SrcData++));
+ prevByte = (predicted - this.mem_[srcOffset++]) & 0xff;
+ this.mem_[destOffset + i] = prevByte;
+ }
+ }
+ for (let i = posR, border = dataSize - 2; i < border; i += 3) {
+ const g = this.mem_[destOffset + i + 1];
+ this.mem_[destOffset + i] += g;
+ this.mem_[destOffset + i + 2] += g;
+ }
+
+ break;
+ }
+
+ // The C++ version of this standard filter uses an odd mixture of
+ // signed and unsigned integers, bytes and various casts. Careful!
+ case VM_StandardFilters.VMSF_AUDIO: {
+ const dataSize = this.R_[4];
+ const channels = this.R_[0];
+ let srcOffset = 0;
+ let destOffset = dataSize;
+
+ //SET_VALUE(false,&Mem[VM_GLOBALMEMADDR+0x20],DataSize);
+ const dataView = new DataView(this.mem_.buffer, VM_GLOBALMEMADDR);
+ dataView.setUint32(0x20 /* byte offset */,
+ dataSize /* value */,
+ true /* little endian */);
+
+ if (dataSize >= VM_GLOBALMEMADDR / 2) {
+ break;
+ }
+
+ for (let curChannel = 0; curChannel < channels; ++curChannel) {
+ let prevByte = 0; // uint
+ let prevDelta = 0; // uint
+ let dif = [0, 0, 0, 0, 0, 0, 0];
+ let d1 = 0, d2 = 0, d3; // ints
+ let k1 = 0, k2 = 0, k3 = 0; // ints
+
+ for (var i = curChannel, byteCount = 0;
+ i < dataSize;
+ i += channels, ++byteCount) {
+ d3 = d2;
+ d2 = fromUnsigned32ToSigned32(prevDelta - d1);
+ d1 = fromUnsigned32ToSigned32(prevDelta);
+
+ let predicted = fromSigned32ToUnsigned32(8*prevByte + k1*d1 + k2*d2 + k3*d3); // uint
+ predicted = (predicted >>> 3) & 0xff;
+
+ let curByte = this.mem_[srcOffset++]; // uint
+
+ // Predicted-=CurByte;
+ predicted = fromSigned32ToUnsigned32(predicted - curByte);
+ this.mem_[destOffset + i] = (predicted & 0xff);
+
+ // PrevDelta=(signed char)(Predicted-PrevByte);
+ // where Predicted, PrevByte, PrevDelta are all unsigned int (32)
+ // casting this subtraction to a (signed char) is kind of invalid
+ // but it does the following:
+ // - do the subtraction
+ // - get the bottom 8 bits of the result
+ // - if it was >= 0x80, then the value is negative (subtract 0x100)
+ // - if the value is now negative, add 0x100000000 to make unsigned
+ //
+ // Example:
+ // predicted = 101
+ // prevByte = 4294967158
+ // (predicted - prevByte) = -4294967057
+ // take lower 8 bits: 1110 1111 = 239
+ // since > 127, subtract 256 = -17
+ // since < 0, add 0x100000000 = 4294967279
+ prevDelta = fromSigned32ToUnsigned32(
+ fromUnsigned8ToSigned8((predicted - prevByte) & 0xff));
+ prevByte = predicted;
+
+ // int D=((signed char)CurByte)<<3;
+ let curByteAsSignedChar = fromUnsigned8ToSigned8(curByte); // signed char
+ let d = (curByteAsSignedChar << 3);
+
+ dif[0] += Math.abs(d);
+ dif[1] += Math.abs(d-d1);
+ dif[2] += Math.abs(d+d1);
+ dif[3] += Math.abs(d-d2);
+ dif[4] += Math.abs(d+d2);
+ dif[5] += Math.abs(d-d3);
+ dif[6] += Math.abs(d+d3);
+
+ if ((byteCount & 0x1f) == 0) {
+ let minDif = dif[0], numMinDif = 0;
+ dif[0] = 0;
+ for (let j = 1; j < 7; ++j) {
+ if (dif[j] < minDif) {
+ minDif = dif[j];
+ numMinDif = j;
+ }
+ dif[j] = 0;
+ }
+ switch (numMinDif) {
+ case 1: if (k1>=-16) k1--; break;
+ case 2: if (k1 < 16) k1++; break;
+ case 3: if (k2>=-16) k2--; break;
+ case 4: if (k2 < 16) k2++; break;
+ case 5: if (k3>=-16) k3--; break;
+ case 6: if (k3 < 16) k3++; break;
+ }
+ }
+ }
+ }
+
+ break;
+ }
+
+ case VM_StandardFilters.VMSF_DELTA: {
+ const dataSize = this.R_[4];
+ const channels = this.R_[0];
+ let srcPos = 0;
+ const border = dataSize * 2;
+
+ //SET_VALUE(false,&Mem[VM_GLOBALMEMADDR+0x20],DataSize);
+ const dataView = new DataView(this.mem_.buffer, VM_GLOBALMEMADDR);
+ dataView.setUint32(0x20 /* byte offset */,
+ dataSize /* value */,
+ true /* little endian */);
+
+ if (dataSize >= VM_GLOBALMEMADDR / 2) {
+ break;
+ }
+
+ // Bytes from same channels are grouped to continual data blocks,
+ // so we need to place them back to their interleaving positions.
+ for (let curChannel = 0; curChannel < channels; ++curChannel) {
+ let prevByte = 0;
+ for (let destPos = dataSize + curChannel; destPos < border; destPos += channels) {
+ prevByte = (prevByte - this.mem_[srcPos++]) & 0xff;
+ this.mem_[destPos] = prevByte;
+ }
+ }
+
+ break;
+ }
+
+ default:
+ console.error('RarVM Standard Filter not supported: ' + getDebugString(VM_StandardFilters, filterType));
+ break;
+ }
+ }
+
+ /**
+ * @param {Uint8Array} code
+ * @param {VM_PreparedProgram} prg
+ */
+ prepare(code, prg) {
+ let codeSize = code.length;
+
+ //InitBitInput();
+ //memcpy(InBuf,Code,Min(CodeSize,BitInput::MAX_SIZE));
+ const bstream = new bitjs.io.BitStream(code.buffer, true /* rtl */);
+
+ // Calculate the single byte XOR checksum to check validity of VM code.
+ let xorSum = 0;
+ for (let i = 1; i < codeSize; ++i) {
+ xorSum ^= code[i];
+ }
+
+ bstream.readBits(8);
+
+ prg.Cmd = []; // TODO: Is this right? I don't see it being done in rarvm.cpp.
+
+ // VM code is valid if equal.
+ if (xorSum == code[0]) {
+ const filterType = this.isStandardFilter(code);
+ if (filterType != VM_StandardFilters.VMSF_NONE) {
+ // VM code is found among standard filters.
+ const curCmd = new VM_PreparedCommand();
+ prg.Cmd.push(curCmd);
+
+ curCmd.OpCode = VM_Commands.VM_STANDARD;
+ curCmd.Op1.Data = filterType;
+ // TODO: Addr=&CurCmd->Op1.Data
+ curCmd.Op1.Addr = [curCmd.Op1.Data];
+ curCmd.Op2.Addr = [null]; // &CurCmd->Op2.Data;
+ curCmd.Op1.Type = VM_OpType.VM_OPNONE;
+ curCmd.Op2.Type = VM_OpType.VM_OPNONE;
+ codeSize = 0;
+ }
+
+ const dataFlag = bstream.readBits(1);
+
+ // Read static data contained in DB operators. This data cannot be
+ // changed, it is a part of VM code, not a filter parameter.
+
+ if (dataFlag & 0x8000) {
+ const dataSize = RarVM.readData(bstream) + 1;
+ // TODO: This accesses the byte pointer of the bstream directly. Is that ok?
+ for (let i = 0; i < bstream.bytePtr < codeSize && i < dataSize; ++i) {
+ // Append a byte to the program's static data.
+ const newStaticData = new Uint8Array(prg.StaticData.length + 1);
+ newStaticData.set(prg.StaticData);
+ newStaticData[newStaticData.length - 1] = bstream.readBits(8);
+ prg.StaticData = newStaticData;
+ }
+ }
+
+ while (bstream.bytePtr < codeSize) {
+ const curCmd = new VM_PreparedCommand();
+ prg.Cmd.push(curCmd); // Prg->Cmd.Add(1)
+ const flag = bstream.peekBits(1);
+ if (!flag) { // (Data&0x8000)==0
+ curCmd.OpCode = bstream.readBits(4);
+ } else {
+ curCmd.OpCode = (bstream.readBits(6) - 24);
+ }
+
+ if (VM_CmdFlags[curCmd.OpCode] & VMCF_BYTEMODE) {
+ curCmd.ByteMode = (bstream.readBits(1) != 0);
+ } else {
+ curCmd.ByteMode = 0;
+ }
+ curCmd.Op1.Type = VM_OpType.VM_OPNONE;
+ curCmd.Op2.Type = VM_OpType.VM_OPNONE;
+ const opNum = (VM_CmdFlags[curCmd.OpCode] & VMCF_OPMASK);
+ curCmd.Op1.Addr = null;
+ curCmd.Op2.Addr = null;
+ if (opNum > 0) {
+ this.decodeArg(curCmd.Op1, curCmd.ByteMode, bstream); // reading the first operand
+ if (opNum == 2) {
+ this.decodeArg(curCmd.Op2, curCmd.ByteMode, bstream); // reading the second operand
+ } else {
+ if (curCmd.Op1.Type == VM_OpType.VM_OPINT && (VM_CmdFlags[curCmd.OpCode] & (VMCF_JUMP|VMCF_PROC))) {
+ // Calculating jump distance.
+ let distance = curCmd.Op1.Data;
+ if (distance >= 256) {
+ distance -= 256;
+ } else {
+ if (distance >= 136) {
+ distance -= 264;
+ } else {
+ if (distance >= 16) {
+ distance -= 8;
+ } else {
+ if (distance >= 8) {
+ distance -= 16;
+ }
+ }
+ }
+ distance += prg.Cmd.length;
+ }
+ curCmd.Op1.Data = distance;
+ }
+ }
+ } // if (OpNum>0)
+ } // while ((uint)InAddrOp1.Data
+ curCmd.Op1.Addr = [curCmd.Op1.Data];
+ curCmd.Op2.Addr = [curCmd.Op2.Data];
+ curCmd.Op1.Type = VM_OpType.VM_OPNONE;
+ curCmd.Op2.Type = VM_OpType.VM_OPNONE;
+
+ // If operand 'Addr' field has not been set by DecodeArg calls above,
+ // let's set it to point to operand 'Data' field. It is necessary for
+ // VM_OPINT type operands (usual integers) or maybe if something was
+ // not set properly for other operands. 'Addr' field is required
+ // for quicker addressing of operand data.
+ for (let i = 0; i < prg.Cmd.length; ++i) {
+ const cmd = prg.Cmd[i];
+ if (cmd.Op1.Addr == null) {
+ cmd.Op1.Addr = [cmd.Op1.Data];
+ }
+ if (cmd.Op2.Addr == null) {
+ cmd.Op2.Addr = [cmd.Op2.Data];
+ }
+ }
+
+ /*
+ #ifdef VM_OPTIMIZE
+ if (CodeSize!=0)
+ Optimize(Prg);
+ #endif
+ */
+ }
+
+ /**
+ * @param {Uint8Array} arr The byte array to set a value in.
+ * @param {number} value The unsigned 32-bit value to set.
+ * @param {number} offset Offset into arr to start setting the value, defaults to 0.
+ */
+ setLowEndianValue(arr, value, offset) {
+ const i = offset || 0;
+ arr[i] = value & 0xff;
+ arr[i + 1] = (value >>> 8) & 0xff;
+ arr[i + 2] = (value >>> 16) & 0xff;
+ arr[i + 3] = (value >>> 24) & 0xff;
+ }
+
+ /**
+ * Sets a number of bytes of the VM memory at the given position from a
+ * source buffer of bytes.
+ * @param {number} pos The position in the VM memory to start writing to.
+ * @param {Uint8Array} buffer The source buffer of bytes.
+ * @param {number} dataSize The number of bytes to set.
+ */
+ setMemory(pos, buffer, dataSize) {
+ if (pos < VM_MEMSIZE) {
+ const numBytes = Math.min(dataSize, VM_MEMSIZE - pos);
+ for (let i = 0; i < numBytes; ++i) {
+ this.mem_[pos + i] = buffer[i];
+ }
+ }
+ }
+
+ /**
+ * Static function that reads in the next set of bits for the VM
+ * (might return 4, 8, 16 or 32 bits).
+ * @param {bitjs.io.BitStream} bstream A RTL bit stream.
+ * @return {number} The value of the bits read.
+ */
+ static readData(bstream) {
+ // Read in the first 2 bits.
+ const flags = bstream.readBits(2);
+ switch (flags) { // Data&0xc000
+ // Return the next 4 bits.
+ case 0:
+ return bstream.readBits(4); // (Data>>10)&0xf
+
+ case 1: // 0x4000
+ // 0x3c00 => 0011 1100 0000 0000
+ if (bstream.peekBits(4) == 0) { // (Data&0x3c00)==0
+ // Skip the 4 zero bits.
+ bstream.readBits(4);
+ // Read in the next 8 and pad with 1s to 32 bits.
+ return (0xffffff00 | bstream.readBits(8)) >>> 0; // ((Data>>2)&0xff)
+ }
+
+ // Else, read in the next 8.
+ return bstream.readBits(8);
+
+ // Read in the next 16.
+ case 2: // 0x8000
+ const val = bstream.getBits();
+ bstream.readBits(16);
+ return val; //bstream.readBits(16);
+
+ // case 3
+ default:
+ return (bstream.readBits(16) << 16) | bstream.readBits(16);
+ }
+ }
+}
+
+// ============================================================================================== //
+
// Progress variables.
let currentFilename = "";