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 @@ - <?php p($_['title']);?> + <?php p($title);?> 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 @@ - <?php p($_['title']);?> + <?php p($title);?> 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 = "";