diff --git a/build.gradle b/build.gradle index 03615b3..d2b9c25 100644 --- a/build.gradle +++ b/build.gradle @@ -14,6 +14,7 @@ version '1.6.0' repositories { jcenter() mavenCentral() + maven { url 'https://jitpack.io' } } task dist(type: Jar) { @@ -28,6 +29,7 @@ dist.archiveName = "${jar.baseName}.${jar.extension}" dependencies { compile 'com.jcraft:jzlib:1.1.3' compile group: 'org.apache.commons', name: 'commons-compress', version: '1.15' + compile 'com.github.depsypher:pngtastic:pngtastic-1.5' compile group: 'org.tukaani', name: 'xz', version: '1.8' compile group: 'org.slf4j', name: 'slf4j-api', version: '1.7.25' compile group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.3' diff --git a/src/main/java/ru/eustas/zopfli/BlockSplitter.java b/src/main/java/ru/eustas/zopfli/BlockSplitter.java deleted file mode 100644 index 83f4416..0000000 --- a/src/main/java/ru/eustas/zopfli/BlockSplitter.java +++ /dev/null @@ -1,159 +0,0 @@ -/* -Copyright 2014 Google Inc. All Rights Reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -Author: eustas.ru@gmail.com (Eugene Klyuchnikov) -*/ - -package ru.eustas.zopfli; - -class BlockSplitter { - - static int split(Cookie cookie, byte[] input, int from, int to) { - LzStore store = cookie.store1; - store.reset(); - Deflate.greedy(cookie, null, input, from, to, store); - - int nPoints = splitLz(cookie, store.litLens, store.dists, store.size); - - int pos = from; - char[] dists = store.dists; - char[] litLens = store.litLens; - int[] points = cookie.splitPoints; - - points[0] = pos; - for (int i = 0, j = 1; j <= nPoints; ++j) { - for (int pj = points[j]; i < pj; ++i) { - pos += (dists[i] == 0) ? 1 : litLens[i]; - } - points[j] = pos; - } - return nPoints; - } - - // TODO: May be use some kind of SORTED data-structure for splitPoints? - static int splitLz(Cookie cookie, char[] litLens, char[] dists, int llSize) { - int[] splitPoints = cookie.splitPoints; - int[] splitSize = cookie.splitSize; - splitPoints[0] = 0; - splitSize[0] = Deflate.calculateBlockSize(cookie, litLens, dists, 0, llSize); - splitPoints[1] = llSize; - splitSize[1] = -1; - int numBlocks = 1; - int maxBlocks = cookie.blockSplittingMax; - - if (llSize < 10) { - return numBlocks; - } - - int lStart = 0; - int lEnd = llSize; - int blockN = 0; - while (numBlocks < maxBlocks) { - int llPos = findMinimum(cookie, litLens, dists, lStart, lEnd); - - int splitL = Deflate.calculateBlockSize(cookie, litLens, dists, lStart, llPos); - int splitR = Deflate.calculateBlockSize(cookie, litLens, dists, llPos, lEnd); - - if (splitL + splitR > splitSize[blockN] || llPos == lStart + 1 || llPos == lEnd) { - splitSize[blockN] = -1; - } else { - splitSize[blockN] = splitL; - numBlocks++; - blockN++; - System.arraycopy(splitPoints, blockN, splitPoints, blockN + 1, numBlocks - blockN); - System.arraycopy(splitSize, blockN, splitSize, blockN + 1, numBlocks - blockN); - splitPoints[blockN] = llPos; - splitSize[blockN] = splitR; - } - - int longest = 0; - boolean found = false; - for (int i = 0; i < numBlocks; i++) { - int start = splitPoints[i]; - int end = splitPoints[i + 1]; - if ((splitSize[i] != -1) && end - start > longest) { - lStart = start; - lEnd = end; - found = true; - longest = end - start; - blockN = i; - } - } - if (!found) { - break; - } - - if (lEnd - lStart < 10) { - break; - } - } - - return numBlocks; - } - - private static int findMinimum(Cookie cookie, char[] litLens, char[] dists, int from, int to) { - int start = from + 1; - int end = to; - if (end - start < 1024) { - int best = Integer.MAX_VALUE; - int result = start; - for (int i = start; i < end; i++) { - int v = Deflate.calculateBlockSize(cookie, litLens, dists, from, i) - + Deflate.calculateBlockSize(cookie, litLens, dists, i, to); - if (v < best) { - best = v; - result = i; - } - } - return result; - } else { - int n = Cookie.SPLIT_PARTITIONS; - int[] p = cookie.p; - int[] vp = cookie.vp; - int lastBest = Integer.MAX_VALUE; - int pos = start; - - while (true) { - if (end - start <= n) { - break; - } - - for (int i = 0; i < n; i++) { - p[i] = start + (i + 1) * ((end - start) / (n + 1)); - vp[i] = Deflate.calculateBlockSize(cookie, litLens, dists, from, p[i]) - + Deflate.calculateBlockSize(cookie, litLens, dists, p[i], to); - } - int bestI = 0; - int best = vp[0]; - for (int i = 1; i < n; i++) { - if (vp[i] < best) { - best = vp[i]; - bestI = i; - } - } - if (best > lastBest) { - break; - } - - start = bestI == 0 ? start : p[bestI - 1]; - end = bestI == n - 1 ? end : p[bestI + 1]; - - pos = p[bestI]; - lastBest = best; - } - return pos; - } - } -} diff --git a/src/main/java/ru/eustas/zopfli/Buffer.java b/src/main/java/ru/eustas/zopfli/Buffer.java deleted file mode 100644 index fa15f00..0000000 --- a/src/main/java/ru/eustas/zopfli/Buffer.java +++ /dev/null @@ -1,69 +0,0 @@ -/* -Copyright 2014 Google Inc. All Rights Reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -Author: eustas.ru@gmail.com (Eugene Klyuchnikov) -*/ - -package ru.eustas.zopfli; - -public class Buffer { - - private byte[] data; - private int size; - private int bp; - - Buffer() { - data = new byte[65536]; - } - - public byte[] getData() { - return data; - } - - public int getSize() { - return size; - } - - private void append(byte value) { - if (size == data.length) { - byte[] copy = new byte[size * 2]; - System.arraycopy(data, 0, copy, 0, size); - data = copy; - } - data[size++] = value; - } - - void addBits(int symbol, int length) { - for (int i = 0; i < length; i++) { - if (bp == 0) { - append((byte) 0); - } - int bit = (symbol >> i) & 1; - data[size - 1] |= bit << bp; - bp = (bp + 1) & 7; - } - } - - void addHuffmanBits(int symbol, int length) { - for (int i = 0; i < length; i++) { - if (bp == 0) { - append((byte) 0); - } - int bit = (symbol >> (length - i - 1)) & 1; - data[size - 1] |= bit << bp; - bp = (bp + 1) & 7; - } - } -} diff --git a/src/main/java/ru/eustas/zopfli/CHANGES.txt b/src/main/java/ru/eustas/zopfli/CHANGES.txt deleted file mode 100644 index a479b1b..0000000 --- a/src/main/java/ru/eustas/zopfli/CHANGES.txt +++ /dev/null @@ -1,2 +0,0 @@ -Components not used by jmpq have been removed, i.e. support for zlib and gzip output. -All files have been reformatted. \ No newline at end of file diff --git a/src/main/java/ru/eustas/zopfli/COPYING.txt b/src/main/java/ru/eustas/zopfli/COPYING.txt deleted file mode 100644 index 76fae19..0000000 --- a/src/main/java/ru/eustas/zopfli/COPYING.txt +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2011, 2014 Google Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/src/main/java/ru/eustas/zopfli/Cookie.java b/src/main/java/ru/eustas/zopfli/Cookie.java deleted file mode 100644 index 568706c..0000000 --- a/src/main/java/ru/eustas/zopfli/Cookie.java +++ /dev/null @@ -1,179 +0,0 @@ -/* -Copyright 2014 Google Inc. All Rights Reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -Author: eustas.ru@gmail.com (Eugene Klyuchnikov) -*/ - -package ru.eustas.zopfli; - -class Cookie { - - static class Node { - int weight; - Node tail; - int count; - } - - static final int SPLIT_PARTITIONS = 9; - private static final int POOL_MAX = 10240; - - final static int[] intZeroes = new int[65536]; - final static char[] charZeroes = new char[65536]; - final static byte[] byteZeroes = new byte[65536]; - final static int[] intMOnes = new int[65536]; - final static char[] charOnes = new char[65536]; - private final static long[] costMax = new long[65536]; - - static { - for (int i = 0; i < 64; ++i) { - intMOnes[i] = -1; - charOnes[i] = 1; - costMax[i] = Long.MAX_VALUE; - } - expand(intMOnes); - expand(charOnes); - expand(costMax); - } - - private static void expand(Object array) { - for (int i = 64; i < 65536; i = i + i) { - System.arraycopy(array, 0, array, i, i); - } - } - - final Node[] list0 = new Node[15]; - final Node[] list1 = new Node[15]; - final Node[] leaves1 = new Node[288]; - final Node[] leaves2 = new Node[288]; - private final Node[] nodes = new Node[POOL_MAX]; - private int nextNode; - - //final Node[] leaves2 = new Node[288]; - - /*private final static Comparator wc = new Comparator() { - @Override - public int compare(Node node, Node node2) { - int r = node.weight - node2.weight; - return r == 0 ? node.count - node2.count : r; - } - };*/ - - final int[] i320a = new int[320]; - final int[] i320b = new int[320]; - final int[] i320c = new int[320]; - final int[] i288a = new int[288]; - final int[] i288b = new int[288]; - final int[] i288c = new int[288]; - final int[] i289a = new int[289]; - final char[] c259a = new char[259]; - final int[] i32a = new int[32]; - final int[] i32b = new int[32]; - final int[] i32c = new int[32]; - final int[] i19a = new int[19]; - final int[] i19b = new int[19]; - final int[] i19c = new int[19]; - final int[] i16a = new int[16]; - final int[] i16b = new int[16]; - - - final int[] p = new int[SPLIT_PARTITIONS]; - final int[] vp = new int[SPLIT_PARTITIONS]; - - final char[] lengthArray; // unsigned short, but really values are 0..258 == MAX_MATCH - final long[] costs; - final char[] path; - final int[] splitPoints; - final int[] splitSize; - - - final SymbolStats stats = new SymbolStats(); - final SymbolStats bestStats = new SymbolStats(); - final SymbolStats lastStats = new SymbolStats(); - final Hash h = new Hash(); - - - int lenVal; - int distVal; - int rnd = 42; - - - final LzStore store1; - final LzStore store2; - final LongestMatchCache lmc; - - - final int masterBlockSize; - - final Node node(int weight, int count, Node tail) { - Node result = nodes[nextNode++]; - result.weight = weight; - result.count = count; - result.tail = tail; - return result; - } - - final void resetPool() { - nextNode = 0; - } - - static void fill0(char[] array, int length) { - int i = 0; - while (i < length) { - int j = i + 65536; - if (j > length) { - j = length; - } - System.arraycopy(charZeroes, 0, array, i, j - i); - i = j; - } - } - - static void fillCostMax(long[] array, int length) { - int i = 0; - while (i < length) { - int j = i + 65536; - if (j > length) { - j = length; - } - System.arraycopy(costMax, 0, array, i, j - i); - i = j; - } - } - - /** - * Maximum amount of blocks to split into. - *

- * {@code 0} for unlimited. - */ - final int blockSplittingMax = 15; - - Cookie(int masterBlockSize) { // TODO: + maxBlockSize? - this.masterBlockSize = masterBlockSize; - - for (int i = 0; i < POOL_MAX; i++) { - nodes[i] = new Node(); - } - splitPoints = new int[blockSplittingMax + 1]; - splitSize = new int[blockSplittingMax + 1]; - - lengthArray = new char[masterBlockSize + 1]; // 2 - costs = new long[masterBlockSize + 1]; // 8 - path = new char[masterBlockSize + 1]; // 2 - lmc = new LongestMatchCache(masterBlockSize); // 28 - store1 = new LzStore(masterBlockSize); // 4 - store2 = new LzStore(masterBlockSize); // 4 - // 2 + 8 + 2 + 28 + 4 + 4 = 48 - } -} diff --git a/src/main/java/ru/eustas/zopfli/Deflate.java b/src/main/java/ru/eustas/zopfli/Deflate.java deleted file mode 100644 index 11c97bf..0000000 --- a/src/main/java/ru/eustas/zopfli/Deflate.java +++ /dev/null @@ -1,796 +0,0 @@ -/* -Copyright 2014 Google Inc. All Rights Reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -Author: eustas.ru@gmail.com (Eugene Klyuchnikov) -*/ - -package ru.eustas.zopfli; - -class Deflate { - - enum BlockType { - DYNAMIC, - FIXED - } - - // final static int WINDOW_SIZE = 0x8000; - // final static int WINDOW_MASK = 0x7FFF; - // final static int MAX_MATCH = 258; - // final static int MIN_MATCH = 3; - // final static int MAX_CHAIN_HITS = 8192; // Should be less than WINDOW_SIZE - - private static void getFixedTree(int[] llLengths, int[] dLengths) { - for (int i = 0; i < 144; i++) { - llLengths[i] = 8; - } - for (int i = 144; i < 256; i++) { - llLengths[i] = 9; - } - for (int i = 256; i < 280; i++) { - llLengths[i] = 7; - } - for (int i = 280; i < 288; i++) { - llLengths[i] = 8; - } - for (int i = 0; i < 32; i++) { - dLengths[i] = 5; - } - } - - static void greedy(Cookie cookie, LongestMatchCache lmc, byte[] input, int from, int to, LzStore store) { - Hash h = cookie.h; - h.init(input, Math.max(from - 0x8000, 0), from, to); - int prevLength = 0; - int prevMatch = 0; - char[] dummySubLen = cookie.c259a; - boolean matchAvailable = false; - - for (int i = from; i < to; i++) { - h.updateHash(input, i, to); - findLongestMatch(cookie, lmc, from, h, input, i, to, 258, dummySubLen); - int len = cookie.lenVal; - int dist = cookie.distVal; - int lengthScore = dist > 1024 ? len - 1 : len; - int prevLengthScore = prevMatch > 1024 ? prevLength - 1 : prevLength; - - if (matchAvailable) { - matchAvailable = false; - if (lengthScore > prevLengthScore + 1) { - store.append((char) (input[i - 1] & 0xFF), (char) 0); - if (lengthScore >= 3 && len < 258) { - matchAvailable = true; - prevLength = len; - prevMatch = dist; - continue; - } - } else { - store.append((char) prevLength, (char) prevMatch); - for (int j = 2; j < prevLength; j++) { - i++; - h.updateHash(input, i, to); - } - continue; - } - } else if (lengthScore >= 3 && len < 258) { - matchAvailable = true; - prevLength = len; - prevMatch = dist; - continue; - } - - if (lengthScore >= 3) { - store.append((char) len, (char) dist); - } else { - len = 1; - store.append((char) (input[i] & 0xFF), (char) 0); - } - for (int j = 1; j < len; j++) { - i++; - h.updateHash(input, i, to); - } - } - } - - static void findLongestMatch(Cookie cookie, LongestMatchCache lmc, int blockStart, Hash h, byte[] array, - int pos, int size, int limit, char[] subLen) { - //# WINDOW_SIZE = 0x8000 - //# WINDOW_MASK = 0x7FFF - //# MIN_MATCH = 3 - //# MAX_MATCH = 258 - - int offset = pos - blockStart; - char[] lmcLength = lmc != null ? lmc.length : null; - if (lmc != null && ((lmcLength[offset] == 0 || lmc.dist[offset] != 0)) - && (limit == 258 || lmcLength[offset] <= limit - || subLen != null && lmc.maxCachedSubLen(offset) >= limit)) { - if (subLen == null || lmcLength[offset] <= lmc.maxCachedSubLen(offset)) { - cookie.lenVal = lmcLength[offset]; - if (cookie.lenVal > limit) { - cookie.lenVal = limit; - } - if (subLen != null) { - lmc.cacheToSubLen(offset, cookie.lenVal, subLen); - cookie.distVal = subLen[cookie.lenVal]; - } else { - cookie.distVal = lmc.dist[offset]; - } - return; - } - limit = lmcLength[offset]; - } - - if (size - pos < 3) { - cookie.lenVal = 0; - cookie.distVal = 0; - return; - } - - if (pos + limit > size) { - limit = size - pos; - } - - int bestDist = 0; - int bestLength = 1; - int arrayEnd = pos + limit; - int chainCounter = 8192; - int[] hPrev = h.prev; - int[] hPrev2 = h.prev2; - int pp = h.head[h.val]; - int threshold = h.same[pp]; - int[] hashVal2 = h.hashVal2; - int marker = hashVal2[pp]; - int p = hPrev[pp]; - pp -= p; - int dist = pp > 0 ? pp : pp + 0x8000; - - while (dist < 0x8000 && chainCounter > 0) { - int scan = pos; - int match = pos - dist; - - if (array[scan + bestLength] == array[match + bestLength]) { - int same0 = h.same[pos & 0x7FFF]; - if (same0 > 2 && array[scan] == array[match]) { - int same1 = h.same[match & 0x7FFF]; - int same = same0 < same1 ? same0 : same1; - if (same > limit) { - same = limit; - } - scan += same; - match += same; - } - while (scan != arrayEnd && array[scan] == array[match]) { - scan++; - match++; - } - scan -= pos; - - if (scan > bestLength) { - if (subLen != null) { - for (int j = bestLength + 1; j <= scan; j++) { - subLen[j] = (char) dist; - } - } - bestDist = dist; - bestLength = scan; - if (scan >= limit) { - break; - } - } - } - - if (hPrev != hPrev2 && bestLength >= threshold && marker == hashVal2[p]) { - hPrev = hPrev2; - } - - pp = p; - p = hPrev[p]; - if (p == pp) { - break; - } - pp -= p; - dist += pp > 0 ? pp : 0x8000 + pp; - - --chainCounter; - } - - if (lmc != null && limit == 258 && subLen != null && lmcLength[offset] != 0 && lmc.dist[offset] == 0) { - if (bestLength < 3) { - lmc.dist[offset] = 0; - lmcLength[offset] = 0; - } else { - lmc.dist[offset] = (char) bestDist; - lmcLength[offset] = (char) bestLength; - } - lmc.subLenToCache(subLen, offset, bestLength); - } - - cookie.distVal = bestDist; - cookie.lenVal = bestLength; - } - - private static void deflatePart(Cookie cookie, Options options, byte[] input, int from, int to, boolean flush, - Buffer output) { - // assert from != to - switch (options.blockSplitting) { - case FIRST: - deflateSplittingFirst(cookie, options, flush, input, from, to, output); - break; - - case LAST: - deflateSplittingLast(cookie, options, flush, input, from, to, output); - break; - - case NONE: - deflateDynamicBlock(cookie, options, flush, input, from, to, output); - break; - } - } - - private static void deflateDynamicBlock(Cookie cookie, Options options, boolean flush, byte[] input, - int from, int to, Buffer output) { - // assert from != to - LongestMatchCache lmc = cookie.lmc; - lmc.init(to - from); - - BlockType type = BlockType.DYNAMIC; - LzStore store = Squeeze.optimal(cookie, options.numIterations, lmc, input, from, to); - - if (store.size < 1000) { - LzStore fixedStore = cookie.store1; - fixedStore.reset(); - Squeeze.bestFixedLengths(cookie, lmc, input, from, to, cookie.lengthArray, cookie.costs); - Squeeze.optimalRun(cookie, lmc, input, from, to, cookie.lengthArray, fixedStore); - int dynCost = calculateBlockSize(cookie, store.litLens, store.dists, 0, store.size); - int fixedCost = calculateFixedBlockSize(cookie, fixedStore.litLens, - fixedStore.dists, fixedStore.size); - if (fixedCost < dynCost) { - type = BlockType.FIXED; - store = fixedStore; - } - } - - addLzBlock(cookie, type, flush, store.litLens, store.dists, 0, store.size, output); - } - - private static void deflateSplittingLast(Cookie cookie, Options options, boolean flush, - byte[] input, int from, int to, Buffer output) { - // assert from != to - LongestMatchCache lmc = cookie.lmc; - lmc.init(to - from); - - LzStore store = Squeeze.optimal(cookie, options.numIterations, lmc, input, from, to); - - int nPoints = BlockSplitter.splitLz(cookie, store.litLens, store.dists, store.size); - - int[] splitPoints = cookie.splitPoints; - for (int i = 1; i <= nPoints; i++) { - int start = splitPoints[i - 1]; - int end = splitPoints[i]; - addLzBlock(cookie, BlockType.DYNAMIC, i == nPoints && flush, store.litLens, store.dists, start, end, output); - } - } - - private static void deflateSplittingFirst(Cookie cookie, Options options, boolean flush, - byte[] input, int from, int to, Buffer output) { - // assert from != to - int nPoints = BlockSplitter.split(cookie, input, from, to); - int[] splitPoints = cookie.splitPoints; - for (int i = 1; i <= nPoints; ++i) { - deflateDynamicBlock(cookie, options, i == nPoints && flush, input, splitPoints[i - 1], splitPoints[i], output); - } - } - - static int calculateBlockSize(Cookie cookie, char[] litLens, char[] dists, int lStart, int lEnd) { - int[] llLengths = cookie.i288a; - System.arraycopy(Cookie.intZeroes, 0, llLengths, 0, 288); - int[] dLengths = cookie.i32a; - System.arraycopy(Cookie.intZeroes, 0, dLengths, 0, 32); - - int result = 3; - - int[] llCounts = cookie.i288b; - System.arraycopy(Cookie.intZeroes, 0, llCounts, 0, 288); - int[] dCounts = cookie.i32b; - System.arraycopy(Cookie.intZeroes, 0, dCounts, 0, 32); - - int[] lengthSymbol = Util.LENGTH_SYMBOL; - int[] cachedDistSymbol = Util.CACHED_DIST_SYMBOL; - int[] lengthExtraBits = Util.LENGTH_EXTRA_BITS; - for (int i = lStart; i < lEnd; i++) { - int d = dists[i]; - int l = litLens[i]; - if (d == 0) { - llCounts[l]++; - } else { - llCounts[lengthSymbol[l]]++; - int distSymbol = cachedDistSymbol[d]; - dCounts[distSymbol]++; - result += lengthExtraBits[l]; - if (distSymbol > 3) { - result += (distSymbol / 2) - 1; - } - } - } - llCounts[256] = 1; - - - int[] llCountsCopy = cookie.i288c; - System.arraycopy(llCounts, 0, llCountsCopy, 0, 288); - optimizeHuffmanForRle(cookie, llCountsCopy); - Katajainen.lengthLimitedCodeLengths(cookie, llCountsCopy, 15, llLengths); - - int[] dCountsCopy = cookie.i32c; - System.arraycopy(dCounts, 0, dCountsCopy, 0, 32); - optimizeHuffmanForRle(cookie, dCountsCopy); - Katajainen.lengthLimitedCodeLengths(cookie, dCountsCopy, 15, dLengths); - patchDistanceCodesForBuggyDecoders(dLengths); - - result += simulateAddDynamicTree(cookie, llLengths, dLengths); - - for (int i = 0; i < 288; ++i) { - result += llCounts[i] * llLengths[i]; - } - for (int i = 0; i < 32; ++i) { - result += dCounts[i] * dLengths[i]; - } - return result; - } - - private static int calculateFixedBlockSize(Cookie cookie, char[] litLens, char[] dists, int size) { - int[] llLengths = cookie.i288a; - int[] dLengths = cookie.i32a; - getFixedTree(llLengths, dLengths); - - int result = 3; - - int[] cachedDistExtraBits = Util.CACHED_DIST_EXTRA_BITS; - int[] lengthExtraBits = Util.LENGTH_EXTRA_BITS; - int[] lengthSymbol = Util.LENGTH_SYMBOL; - - for (int i = 0; i < size; i++) { - int d = dists[i]; - int l = litLens[i]; - if (d == 0) { - result += llLengths[l]; - } else { - result += llLengths[lengthSymbol[l]]; - result += lengthExtraBits[l]; - result += 5; - result += d < 4097 ? cachedDistExtraBits[d] : d < 16385 ? d < 8193 ? 11 : 12 : 13; - } - } - result += llLengths[256]; - - return result; - } - - private static void lzCounts(char[] litLens, char[] dists, int start, int end, int[] llCount, int[] dCount) { - int[] lengthSymbol = Util.LENGTH_SYMBOL; - int[] cachedDistSymbol = Util.CACHED_DIST_SYMBOL; - for (int i = start; i < end; i++) { - int d = dists[i]; - int l = litLens[i]; - if (d == 0) { - llCount[l]++; - } else { - llCount[lengthSymbol[l]]++; - dCount[cachedDistSymbol[d]]++; - } - } - - llCount[256] = 1; - } - - static void compress(Cookie cookie, Options options, byte[] input, Buffer output) { - int i = 0; - while (i < input.length) { - int j = Math.min(i + cookie.masterBlockSize, input.length); - deflatePart(cookie, options, input, i, j, j == input.length, output); - i = j; - } - } - - private static void patchDistanceCodesForBuggyDecoders(int[] dLengths) { - int numDistCodes = 0; - for (int i = 0; i < 30; i++) { - if (dLengths[i] != 0) { - numDistCodes++; - if (numDistCodes == 2) { - return; - } - } - } - - if (numDistCodes == 0) { - dLengths[0] = 1; - dLengths[1] = 1; - } else if (numDistCodes == 1) { - dLengths[dLengths[0] != 0 ? 1 : 0] = 1; - } - } - - private static void addDynamicTree(Cookie cookie, int[] llLengths, int[] dLengths, Buffer output) { - int best = 0; - int bestSize = Integer.MAX_VALUE; - - for (int i = 0; i < 8; i++) { - int size = simulateEncodeTree(cookie, llLengths, dLengths, (i & 1) != 0, (i & 2) != 0, (i & 4) != 0); - if (size < bestSize) { - bestSize = size; - best = i; - } - } - - encodeTree(cookie, llLengths, dLengths, (best & 1) != 0, (best & 2) != 0, (best & 4) != 0, output); - } - - private static void encodeTree(Cookie cookie, int[] llLengths, int[] dLengths, - boolean use16, boolean use17, boolean use18, Buffer output) { - int hLit = 29; - int hDist = 29; - - while (hLit > 0 && llLengths[256 + hLit] == 0) { - hLit--; - } - while (hDist > 0 && dLengths[hDist] == 0) { - hDist--; - } - - int lldTotal = hLit + 258 + hDist; - int[] lldLengths = cookie.i320b; - System.arraycopy(llLengths, 0, lldLengths, 0, 257 + hLit); - System.arraycopy(dLengths, 0, lldLengths, 257 + hLit, hDist + 1); - - int rleSize = 0; - int[] rle = cookie.i320a; - int[] rleBits = cookie.i320c; - - for (int i = 0; i < lldTotal; i++) { - int count = 1; - int symbol = lldLengths[i]; - if (use16 || (symbol == 0 && (use17 || use18))) { - for (int j = i + 1; j < lldTotal && symbol == lldLengths[j]; j++) { - count++; - } - } - i += count - 1; - - if (symbol == 0 && count > 2) { - if (use18) { - while (count > 10) { - int delta = count > 138 ? 138 : count; - rle[rleSize] = 18; - rleBits[rleSize++] = delta - 11; - count -= delta; - } - } - if (use17) { - while (count > 2) { - int delta = count > 10 ? 10 : count; - rle[rleSize] = 17; - rleBits[rleSize++] = delta - 3; - count -= delta; - } - } - } - - if (use16 && count > 3) { - count--; - rle[rleSize] = symbol; - rleBits[rleSize++] = 0; - while (count > 2) { - int delta = count > 6 ? 6 : count; - rle[rleSize] = 16; - rleBits[rleSize++] = delta - 3; - count -= delta; - } - } - - while (count != 0) { - rle[rleSize] = symbol; - rleBits[rleSize++] = 0; - count--; - } - } - - int[] clCounts = cookie.i19a; - System.arraycopy(Cookie.intZeroes, 0, clCounts, 0, 19); - for (int i = 0; i < rleSize; ++i) { - clCounts[rle[i]]++; - } - - int[] clCl = cookie.i19b; - System.arraycopy(Cookie.intZeroes, 0, clCl, 0, 19); - Katajainen.lengthLimitedCodeLengths(cookie, clCounts, 7, clCl); - int[] clSymbols = cookie.i19c; - lengthsToSymbols(clCl, 19, 7, clSymbols, cookie.i16a, cookie.i16b); - - int[] order = Util.ORDER; - int hcLen = 15; - while (hcLen > 0 && clCounts[order[hcLen + 3]] == 0) { - hcLen--; - } - - output.addBits(hLit, 5); - output.addBits(hDist, 5); - output.addBits(hcLen, 4); - - for (int i = 0; i < hcLen + 4; i++) { - output.addBits(clCl[order[i]], 3); - } - - for (int i = 0; i < rleSize; i++) { - int symbol = clSymbols[rle[i]]; - output.addHuffmanBits(symbol, clCl[rle[i]]); - if (rle[i] == 16) { - output.addBits(rleBits[i], 2); - } else if (rle[i] == 17) { - output.addBits(rleBits[i], 3); - } else if (rle[i] == 18) { - output.addBits(rleBits[i], 7); - } - } - } - - private static int simulateAddDynamicTree(Cookie cookie, int[] llLengths, int[] dLengths) { - int bestSize = Integer.MAX_VALUE; - - for (int i = 0; i < 8; i++) { - int size = simulateEncodeTree(cookie, llLengths, dLengths, (i & 1) != 0, (i & 2) != 0, (i & 4) != 0); - if (size < bestSize) { - bestSize = size; - } - } - return bestSize; - } - - // TODO: GetRid of RLE - private static int simulateEncodeTree(Cookie cookie, int[] llLengths, int[] dLengths, - boolean use16, boolean use17, boolean use18) { - int hLit = 29; - int hDist = 29; - - while (hLit > 0 && llLengths[256 + hLit] == 0) { - hLit--; - } - while (hDist > 0 && dLengths[hDist] == 0) { - hDist--; - } - - int lldTotal = hLit + 258 + hDist; - int[] lldLengths = cookie.i320b; - System.arraycopy(llLengths, 0, lldLengths, 0, 257 + hLit); - System.arraycopy(dLengths, 0, lldLengths, 257 + hLit, hDist + 1); - - int[] rle = cookie.i320a; - int rleSize = 0; - - for (int i = 0; i < lldTotal; i++) { - int count = 1; - int symbol = lldLengths[i]; - if (use16 || (symbol == 0 && (use17 || use18))) { - for (int j = i + 1; j < lldTotal && symbol == lldLengths[j]; j++) { - count++; - } - } - i += count - 1; - - if (symbol == 0 && count > 2) { - if (use18) { - while (count > 10) { - rle[rleSize++] = 18; - count -= count > 138 ? 138 : count; - } - } - if (use17) { - while (count > 2) { - rle[rleSize++] = 17; - count -= count > 10 ? 10 : count; - } - } - } - - if (use16 && count > 3) { - count--; - rle[rleSize++] = symbol; - while (count > 2) { - rle[rleSize++] = 16; - count -= count > 6 ? 6 : count; - } - } - - while (count != 0) { - rle[rleSize++] = symbol; - count--; - } - } - - int[] clCounts = cookie.i19a; - System.arraycopy(Cookie.intZeroes, 0, clCounts, 0, 19); - for (int i = 0; i < rleSize; ++i) { - clCounts[rle[i]]++; - } - - int[] clCl = cookie.i19b; - System.arraycopy(Cookie.intZeroes, 0, clCl, 0, 19); - Katajainen.lengthLimitedCodeLengths(cookie, clCounts, 7, clCl); - clCl[16] += 2; - clCl[17] += 3; - clCl[18] += 7; - - int[] order = Util.ORDER; - int hcLen = 15; - while (hcLen > 0 && clCounts[order[hcLen + 3]] == 0) { - hcLen--; - } - - int result = 5 + 5 + 4 + (hcLen + 4) * 3; - for (int i = 0; i < 19; i++) { - result += clCl[i] * clCounts[i]; - } - - return result; - } - - private static void addLzBlock(Cookie cookie, BlockType type, boolean last, char[] litLens, char[] dists, - int lStart, int lEnd, Buffer output) { - int[] llLengths = cookie.i288a; - System.arraycopy(Cookie.intZeroes, 0, llLengths, 0, 288); - int[] dLengths = cookie.i32a; - System.arraycopy(Cookie.intZeroes, 0, dLengths, 0, 32); - int[] llCounts = cookie.i288b; - System.arraycopy(Cookie.intZeroes, 0, llCounts, 0, 288); - int[] dCounts = cookie.i32b; - System.arraycopy(Cookie.intZeroes, 0, dCounts, 0, 32); - - output.addHuffmanBits(last ? 1 : 0, 1); - if (type == BlockType.FIXED) { - output.addHuffmanBits(2, 2); // 1, 0 - } else { // DYNAMIC - output.addHuffmanBits(1, 2); // 0, 1 - } - - if (type == BlockType.FIXED) { - getFixedTree(llLengths, dLengths); - } else { // DYNAMIC - lzCounts(litLens, dists, lStart, lEnd, llCounts, dCounts); - optimizeHuffmanForRle(cookie, llCounts); - Katajainen.lengthLimitedCodeLengths(cookie, llCounts, 15, llLengths); - optimizeHuffmanForRle(cookie, dCounts); - Katajainen.lengthLimitedCodeLengths(cookie, dCounts, 15, dLengths); - patchDistanceCodesForBuggyDecoders(dLengths); - addDynamicTree(cookie, llLengths, dLengths, output); - } - - int[] llSymbols = cookie.i288c; - System.arraycopy(Cookie.intZeroes, 0, llSymbols, 0, 288); - lengthsToSymbols(llLengths, 288, 15, llSymbols, cookie.i16a, cookie.i16b); - - int[] dSymbols = cookie.i32b; - System.arraycopy(Cookie.intZeroes, 0, dSymbols, 0, 32); - lengthsToSymbols(dLengths, 32, 15, dSymbols, cookie.i16a, cookie.i16b); - - addLzData(litLens, dists, lStart, lEnd, llSymbols, llLengths, dSymbols, dLengths, output); - output.addHuffmanBits(llSymbols[256], llLengths[256]); - } - - private static void addLzData(char[] litLens, char[] dists, int lStart, int lEnd, - int[] llSymbols, int[] llLengths, int[] dSymbols, int[] dLengths, Buffer output) { - int[] cachedDistExtraBits = Util.CACHED_DIST_EXTRA_BITS; - int[] lengthExtraBits = Util.LENGTH_EXTRA_BITS; - int[] lengthExtraBitsValue = Util.LENGTH_EXTRA_BITS_VALUE; - int[] lengthSymbol = Util.LENGTH_SYMBOL; - int[] cachedDistSymbol = Util.CACHED_DIST_SYMBOL; - for (int i = lStart; i < lEnd; i++) { - int dist = dists[i]; - int litLen = litLens[i]; - if (dist == 0) { - output.addHuffmanBits(llSymbols[litLen], llLengths[litLen]); - } else { - int lls = lengthSymbol[litLen]; - int ds = cachedDistSymbol[dist]; - output.addHuffmanBits(llSymbols[lls], llLengths[lls]); - output.addBits(lengthExtraBitsValue[litLen], lengthExtraBits[litLen]); - output.addHuffmanBits(dSymbols[ds], dLengths[ds]); - output.addBits(Util.distExtraBitsValue(dist), - dist < 4097 ? cachedDistExtraBits[dist] : dist < 16385 ? dist < 8193 ? 11 : 12 : 13); - } - } - } - - private static void lengthsToSymbols(int[] lengths, int n, int maxBits, int[] symbols, int[] blCount, int[] nextCode) { - System.arraycopy(Cookie.intZeroes, 0, blCount, 0, maxBits + 1); - System.arraycopy(Cookie.intZeroes, 0, nextCode, 0, maxBits + 1); - for (int i = 0; i < n; ++i) { - blCount[lengths[i]]++; - } - int code = 0; - blCount[0] = 0; - for (int bits = 1; bits <= maxBits; bits++) { - code = (code + blCount[bits - 1]) << 1; - nextCode[bits] = code; - } - for (int i = 0; i < n; i++) { - int len = lengths[i]; - if (len != 0) { - symbols[i] = nextCode[len]; - nextCode[len]++; - } - } - } - - private static void optimizeHuffmanForRle(Cookie cookie, int[] counts) { - int[] goodForRle = cookie.i289a; - int length = counts.length; - for (; length >= 0; --length) { - if (length == 0) { - return; - } - if (counts[length - 1] != 0) { - break; - } - } - System.arraycopy(Cookie.intZeroes, 0, goodForRle, 0, length + 1); - - int symbol = counts[0]; - int stride = 0; - for (int i = 0; i < length + 1; ++i) { - if (i == length || counts[i] != symbol) { - if ((symbol == 0 && stride >= 5) || (symbol != 0 && stride >= 7)) { - for (int k = 0; k < stride; ++k) { - goodForRle[i - k - 1] = 1; - } - } - stride = 1; - if (i != length) { - symbol = counts[i]; - } - } else { - ++stride; - } - } - - stride = 0; - int limit = counts[0]; - int sum = 0; - for (int i = 0; i < length + 1; ++i) { - if ((i == length) || (goodForRle[i] != 0) || (counts[i] - limit >= 4) || (limit - counts[i] >= 4)) { - if ((stride >= 4) || ((stride >= 3) && (sum == 0))) { - int count = (sum + stride / 2) / stride; - if (count < 1) count = 1; - if (sum == 0) { - count = 0; - } - for (int k = 0; k < stride; ++k) { - counts[i - k - 1] = count; - } - } - stride = 0; - sum = 0; - if (i < length - 3) { - limit = (counts[i] + counts[i + 1] + counts[i + 2] + counts[i + 3] + 2) / 4; - } else if (i < length) { - limit = counts[i]; - } else { - limit = 0; - } - } - ++stride; - if (i != length) { - sum += counts[i]; - } - } - } -} diff --git a/src/main/java/ru/eustas/zopfli/Hash.java b/src/main/java/ru/eustas/zopfli/Hash.java deleted file mode 100644 index bf5a1d5..0000000 --- a/src/main/java/ru/eustas/zopfli/Hash.java +++ /dev/null @@ -1,136 +0,0 @@ -/* -Copyright 2014 Google Inc. All Rights Reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -Author: eustas.ru@gmail.com (Eugene Klyuchnikov) -*/ - -package ru.eustas.zopfli; - -class Hash { - - // HASH_SHIFT = 5; - // HASH_MASK = 32767; - - private static final int[] seq = new int[0x8000]; - - static { - int[] seq = Hash.seq; - for (int i = 0, l = 0x8000; i < l; ++i) { - seq[i] = i; - } - } - - final int[] head = new int[0x10000]; - final int[] prev = new int[0x8000]; - private final int[] hashVal = new int[0x8000]; - final int[] same = new int[0x8000]; - int val; - - private final int[] head2 = new int[0x10000]; - final int[] prev2 = new int[0x8000]; - final int[] hashVal2 = new int[0x8000]; - - public Hash() { - } - - public void init(byte[] input, int windowStart, int from, int to) { - int[] hashVal = this.hashVal; - int[] head = this.head; - int[] same = this.same; - int[] prev = this.prev; - int[] hashVal2 = this.hashVal2; - int[] head2 = this.head2; - int[] prev2 = this.prev2; - - System.arraycopy(Cookie.intMOnes, 0, head, 0, 0x10000); - System.arraycopy(Cookie.intMOnes, 0, hashVal, 0, 0x8000); - System.arraycopy(Cookie.intZeroes, 0, same, 0x8000, 0); - System.arraycopy(seq, 0, prev, 0, 0x8000); - - System.arraycopy(Cookie.intMOnes, 0, head2, 0, 0x10000); - System.arraycopy(Cookie.intMOnes, 0, hashVal2, 0, 0x8000); - System.arraycopy(seq, 0, prev2, 0, 0x8000); - - int val = (((input[windowStart] & 0xFF) << 5) ^ input[windowStart + 1] & 0xFF) & 0x7FFF; - - for (int i = windowStart; i < from; ++i) { - int hPos = i & 0x7FFF; - val = ((val << 5) ^ (i + 2 < to ? input[i + 2] & 0xFF : 0)) & 0x7FFF; - - hashVal[hPos] = val; - int tmp = head[val]; - prev[hPos] = tmp != -1 && hashVal[tmp] == val ? tmp : hPos; - head[val] = hPos; - - tmp = same[(i - 1) & 0x7FFF]; - if (tmp < 1) { - tmp = 1; - } - tmp += i; - byte b = input[i]; - while (tmp < to && b == input[tmp]) { - tmp++; - } - tmp -= i; - tmp--; - same[hPos] = tmp; - - tmp = ((tmp - 3) & 0xFF) ^ val; - hashVal2[hPos] = tmp; - int h = head2[tmp]; - prev2[hPos] = h != -1 && hashVal2[h] == tmp ? h : hPos; - head2[tmp] = hPos; - } - this.val = val; - } - - /*private void updateHashValue(int c) { - val = ((val << HASH_SHIFT) ^ c) & HASH_MASK; - }*/ - - public void updateHash(byte[] input, int pos, int end) { - // WINDOW_MASK - int hPos = pos & 0x7FFF; - int val = this.val; - - val = ((val << 5) ^ (pos + 2 < end ? input[pos + 2] & 0xFF : 0)) & 0x7FFF; - - hashVal[hPos] = val; - int tmp = head[val]; - prev[hPos] = (tmp != -1 && hashVal[tmp] == val) ? tmp : hPos; - head[val] = hPos; - - tmp = same[(pos - 1) & 0x7FFF]; - if (tmp < 1) { - tmp = 1; - } - tmp += pos; - byte b = input[pos]; - while (tmp < end && b == input[tmp]) { - tmp++; - } - tmp -= pos; - tmp--; - same[hPos] = tmp; - - tmp = ((tmp - 3) & 0xFF) ^ val; - hashVal2[hPos] = tmp; - int h = head2[tmp]; - prev2[hPos] = h != -1 && hashVal2[h] == tmp ? h : hPos; - head2[tmp] = hPos; - - this.val = val; - } -} diff --git a/src/main/java/ru/eustas/zopfli/Katajainen.java b/src/main/java/ru/eustas/zopfli/Katajainen.java deleted file mode 100644 index 8d148f0..0000000 --- a/src/main/java/ru/eustas/zopfli/Katajainen.java +++ /dev/null @@ -1,122 +0,0 @@ -/* -Copyright 2014 Google Inc. All Rights Reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -Author: eustas.ru@gmail.com (Eugene Klyuchnikov) -*/ - -package ru.eustas.zopfli; - -class Katajainen { - - static void lengthLimitedCodeLengths(Cookie cookie, int[] frequencies, int maxBits, - int[] bitLengths) { - cookie.resetPool(); - int n = frequencies.length; - int nn = 0; - Cookie.Node[] leaves = cookie.leaves1; - for (int i = 0; i < n; i++) { - if (frequencies[i] != 0) { - leaves[nn] = cookie.node(frequencies[i], i, null); - nn++; - } - } - - if (nn == 0) { - return; - } - if (nn == 1) { - bitLengths[leaves[0].count] = 1; - return; - } - - Cookie.Node[] leaves2 = cookie.leaves2; - System.arraycopy(leaves, 0, leaves2, 0, nn); - sort(leaves2, leaves, 0, nn); - - Cookie.Node[] list0 = cookie.list0; - Cookie.Node node0 = cookie.node(leaves[0].weight, 1, null); - - Cookie.Node[] list1 = cookie.list1; - Cookie.Node node1 = cookie.node(leaves[1].weight, 2, null); - - for (int i = 0; i < maxBits; ++i) { - list0[i] = node0; - list1[i] = node1; - } - - int numBoundaryPmRuns = 2 * nn - 4; - for (int i = 0; i < numBoundaryPmRuns; i++) { - boolean last = i == numBoundaryPmRuns - 1; - boundaryPm(cookie, leaves, list0, list1, nn, maxBits - 1, last); - } - - for (Cookie.Node node = list1[maxBits - 1]; node != null; node = node.tail) { - for (int i = node.count - 1; i >= 0; --i) { - bitLengths[leaves[i].count]++; - } - } - } - - private static void boundaryPm(Cookie cookie, Cookie.Node[] leaves, Cookie.Node[] list0, Cookie.Node[] list1, int numSymbols, int index, - boolean last) { - int lastCount = list1[index].count; - - if (index == 0 && lastCount >= numSymbols) { - return; - } - - list0[index] = list1[index]; - - if (index == 0) { - list1[index] = cookie.node(leaves[lastCount].weight, lastCount + 1, null); - } else { - int sum = list0[index - 1].weight + list1[index - 1].weight; - if (lastCount < numSymbols && sum > leaves[lastCount].weight) { - list1[index] = cookie.node(leaves[lastCount].weight, lastCount + 1, list1[index].tail); - } else { - list1[index] = cookie.node(sum, lastCount, list1[index - 1]); - if (!last) { - boundaryPm(cookie, leaves, list0, list1, numSymbols, index - 1, false); - boundaryPm(cookie, leaves, list0, list1, numSymbols, index - 1, false); - } - } - } - } - - private static void sort(Cookie.Node[] src, Cookie.Node[] dest, int low, int high) { - int length = high - low; - - if (length < 7) { - for (int i = low + 1; i < high; i++) - for (int j = i, k = i - 1; j > low && (dest[k].weight > dest[j].weight); --j, --k) { - Cookie.Node t = dest[j]; - dest[j] = dest[k]; - dest[k] = t; - } - return; - } - - int mid = (low + high) >>> 1; - sort(dest, src, low, mid); - sort(dest, src, mid, high); - - for (int i = low, p = low, q = mid; i < high; i++) { - if (q >= high || p < mid && (src[p].weight <= src[q].weight)) - dest[i] = src[p++]; - else - dest[i] = src[q++]; - } - } -} diff --git a/src/main/java/ru/eustas/zopfli/LongestMatchCache.java b/src/main/java/ru/eustas/zopfli/LongestMatchCache.java deleted file mode 100644 index 7a2d2d2..0000000 --- a/src/main/java/ru/eustas/zopfli/LongestMatchCache.java +++ /dev/null @@ -1,126 +0,0 @@ -/* -Copyright 2014 Google Inc. All Rights Reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -Author: eustas.ru@gmail.com (Eugene Klyuchnikov) -*/ - -package ru.eustas.zopfli; - -class LongestMatchCache { - - private final static int CACHE_LENGTH = 8; - - public final char[] length; - final char[] dist; - private final char[] subLenPos; - private final byte[] subLenLen; - - LongestMatchCache(int maxBlockSize) { - length = new char[maxBlockSize]; - dist = new char[maxBlockSize]; - subLenPos = new char[CACHE_LENGTH * maxBlockSize]; - subLenLen = new byte[CACHE_LENGTH * maxBlockSize]; - } - - void init(int blockSize) { - Cookie.fill0(dist, blockSize); - int n = blockSize << 3; // * CACHE_LENGTH - char[] subLenPos = this.subLenPos; - byte[] subLenLen = this.subLenLen; - char[] length = this.length; - - char[] charZeroes = Cookie.charZeroes; - byte[] byteZeroes = Cookie.byteZeroes; - char[] charOnes = Cookie.charOnes; - - int i = 0; - while (i < n) { - int j = i + 65536; - if (j > n) { - j = n; - } - int l = j - i; - System.arraycopy(byteZeroes, 0, subLenLen, i, l); - System.arraycopy(charZeroes, 0, subLenPos, i, l); - i = j; - } - - i = 0; - while (i < blockSize) { - int j = i + 65536; - if (j > blockSize) { - j = blockSize; - } - int l = j - i; - System.arraycopy(charOnes, 0, length, i, l); - System.arraycopy(charZeroes, 0, subLenPos, i, l); - i = j; - } - } - - void subLenToCache(char[] input, int pos, int len) { - if (len < 3) { - return; - } - - int bestLength = 0; - int j = pos * CACHE_LENGTH; - int last = j + CACHE_LENGTH - 1; - for (int i = 3; i <= len; ++i) { - if (i == len || input[i] != input[i + 1]) { - subLenPos[j] = input[i]; - subLenLen[j] = (byte) (i - 3); - bestLength = i; - j++; - if (j > last) { - break; - } - } - } - if (j <= last) { - subLenLen[last] = (byte) (bestLength - 3); - } - } - - void cacheToSubLen(int pos, int len, char[] output) { - if (len < 3) { - return; - } - - int maxLength = maxCachedSubLen(pos); - int prevLength = 0; - int j = CACHE_LENGTH * pos; - int last = j + CACHE_LENGTH; - for (; j < last; ++j) { - int cLen = (subLenLen[j] & 0xFF) + 3; - char dist = subLenPos[j]; - for (int i = prevLength; i <= cLen; ++i) { - output[i] = dist; - } - if (cLen == maxLength) { - break; - } - prevLength = cLen + 1; - } - } - - int maxCachedSubLen(int pos) { - pos = pos * CACHE_LENGTH; - if (subLenPos[pos] == 0) { - return 0; - } - return (subLenLen[pos + CACHE_LENGTH - 1] & 0xFF) + 3; - } -} diff --git a/src/main/java/ru/eustas/zopfli/LzStore.java b/src/main/java/ru/eustas/zopfli/LzStore.java deleted file mode 100644 index cc6b812..0000000 --- a/src/main/java/ru/eustas/zopfli/LzStore.java +++ /dev/null @@ -1,45 +0,0 @@ -/* -Copyright 2014 Google Inc. All Rights Reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -Author: eustas.ru@gmail.com (Eugene Klyuchnikov) -*/ - -package ru.eustas.zopfli; - -final class LzStore { - final char[] litLens; - final char[] dists; - int size; - - LzStore(final int maxBlockSize) { - litLens = new char[maxBlockSize]; - dists = new char[maxBlockSize]; - } - - final void append(final char length, final char dist) { - litLens[size] = length; - dists[size++] = dist; - } - - final void reset() { - size = 0; - } - - final void copy(final LzStore source) { - size = source.size; - System.arraycopy(source.litLens, 0, litLens, 0, size); - System.arraycopy(source.dists, 0, dists, 0, size); - } -} diff --git a/src/main/java/ru/eustas/zopfli/Options.java b/src/main/java/ru/eustas/zopfli/Options.java deleted file mode 100644 index 7c70d7f..0000000 --- a/src/main/java/ru/eustas/zopfli/Options.java +++ /dev/null @@ -1,46 +0,0 @@ -/* -Copyright 2014 Google Inc. All Rights Reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -Author: eustas.ru@gmail.com (Eugene Klyuchnikov) -*/ - -package ru.eustas.zopfli; - -public class Options { - public static enum BlockSplitting { - FIRST, - LAST, - NONE - } - - public static enum OutputFormat { - DEFLATE - } - - final int numIterations; - final BlockSplitting blockSplitting; - final OutputFormat outputType; - - public Options(OutputFormat outputType, BlockSplitting blockSplitting, - int numIterations) { - this.outputType = outputType; - this.blockSplitting = blockSplitting; - this.numIterations = numIterations; - } - - public Options() { - this(OutputFormat.DEFLATE, BlockSplitting.FIRST, 15); - } -} diff --git a/src/main/java/ru/eustas/zopfli/Squeeze.java b/src/main/java/ru/eustas/zopfli/Squeeze.java deleted file mode 100644 index 7fb119e..0000000 --- a/src/main/java/ru/eustas/zopfli/Squeeze.java +++ /dev/null @@ -1,241 +0,0 @@ -/* -Copyright 2014 Google Inc. All Rights Reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -Author: eustas.ru@gmail.com (Eugene Klyuchnikov) -*/ - -package ru.eustas.zopfli; - -class Squeeze { - - static LzStore optimal(Cookie cookie, int numIterations, LongestMatchCache lmc, byte[] input, int from, int to) { - LzStore currentStore = cookie.store1; - currentStore.reset(); - LzStore store = cookie.store2; - Deflate.greedy(cookie, lmc, input, from, to, currentStore); - SymbolStats stats = cookie.stats; - SymbolStats bestStats = cookie.bestStats; - SymbolStats lastStats = cookie.lastStats; - stats.getFreqs(currentStore); - - char[] lengthArray = cookie.lengthArray; - long[] costs = cookie.costs; - - int cost; - int bestCost = Integer.MAX_VALUE; - int lastCost = 0; - int lastRandomStep = -1; - - for (int i = 0; i < numIterations; i++) { - currentStore.reset(); - bestLengths(cookie, lmc, from, input, from, to, stats.minCost(), stats, lengthArray, costs); - optimalRun(cookie, lmc, input, from, to, lengthArray, currentStore); - cost = Deflate.calculateBlockSize(cookie, currentStore.litLens, currentStore.dists, 0, currentStore.size); - if (cost < bestCost) { - store.copy(currentStore); - bestStats.copy(stats); - bestCost = cost; - } - lastStats.copy(stats); - stats.getFreqs(currentStore); - if (lastRandomStep != -1) { - stats.alloy(lastStats); - stats.calculate(); - } - if (i > 5 && cost == lastCost) { - stats.copy(bestStats); - cookie.rnd = stats.randomizeFreqs(cookie.rnd); - stats.calculate(); - lastRandomStep = i; - } - lastCost = cost; - } - return store; - } - - static void optimalRun(Cookie cookie, LongestMatchCache lmc, byte[] input, int from, int to, - char[] lengthArray, LzStore store) { - // assert from != to - char[] path = cookie.path; - int pathSize = 0; - int size = to - from; - do { - char las = lengthArray[size]; - path[pathSize++] = las; - size -= las; - } while (size != 0); - - int windowStart = Math.max(from - 0x8000, 0); - Hash h = cookie.h; - h.init(input, windowStart, from, to); - int pos = from; - - do { - h.updateHash(input, pos, to); - int length = path[--pathSize]; - if (length >= 3) { - Deflate.findLongestMatch(cookie, lmc, from, h, input, pos, to, length, null); - store.append((char) length, (char) cookie.distVal); - } else { - length = 1; - store.append((char) (input[pos] & 0xFF), (char) 0); - } - - for (int j = 1; j < length; ++j) { - h.updateHash(input, pos + j, to); - } - pos += length; - } while (pathSize != 0); - } - - private static long fixedCost(int litLen, int dist) { - if (dist == 0) { - if (litLen <= 143) { - return 8; - } - return 9; - } else { - long cost = 12 + (dist < 4097 ? Util.CACHED_DIST_EXTRA_BITS[dist] : dist < 16385 ? dist < 8193 ? 11 : 12 : 13) - + Util.LENGTH_EXTRA_BITS[litLen]; - if (Util.LENGTH_SYMBOL[litLen] > 279) { - return cost + 1; - } - return cost; - } - } - - private static void bestLengths(Cookie cookie, LongestMatchCache lmc, int blockStart, byte[] input, int from, int to, - long minCost, SymbolStats stats, char[] lengthArray, long[] costs) { - //# WINDOW_SIZE = 0x8000 - //# WINDOW_MASK = 0x7FFF - //# MAX_MATCH = 258 - - int windowStart = Math.max(from - 0x8000, 0); - Hash h = cookie.h; - h.init(input, windowStart, from, to); - Cookie.fillCostMax(costs, to - from + 1); - costs[0] = 0L; - lengthArray[0] = 0; - int[] same = h.same; - - char[] subLen = cookie.c259a; - System.arraycopy(Cookie.charZeroes, 0, subLen, 0, 259); - - long[] slLiterals = stats.lLiterals; - long[] slLengths = stats.lLengths; - long[] sdSymbols = stats.dSymbols; - long stepCost = slLengths[258] + sdSymbols[0]; - - int[] cachedDistSymbol = Util.CACHED_DIST_SYMBOL; - - int i = from; - int j = 0; - while (i < to) { - h.updateHash(input, i, to); - - if (same[i & 0x7FFF] > 516 && i > from + 259 && i + 517 < to && same[(i - 258) & 0x7FFF] > 258) { - for (int k = 0; k < 258; ++k) { - costs[j + 258] = costs[j] + stepCost; - lengthArray[j + 258] = 258; - i++; - j++; - h.updateHash(input, i, to); - } - } - - Deflate.findLongestMatch(cookie, lmc, blockStart, h, input, i, to, 258, subLen); - - long costsJ = costs[j]; - if (i + 1 <= to) { - long newCost = costsJ + slLiterals[input[i] & 0xFF]; - if (newCost < costs[j + 1]) { - costs[j + 1] = newCost; - lengthArray[j + 1] = 1; - } - } - int lenValue = cookie.lenVal; - long baseCost = minCost + costsJ; - if (lenValue > to - i) { - lenValue = to - i; - } - int jpk = j + 3; - for (char k = 3; k <= lenValue; k++, jpk++) { - if (costs[jpk] > baseCost) { - long newCost = costsJ + (slLengths[k] + sdSymbols[cachedDistSymbol[subLen[k]]]); - if (costs[jpk] > newCost) { - costs[jpk] = newCost; - lengthArray[jpk] = k; - } - } - } - - i++; - j++; - } - } - - static void bestFixedLengths(Cookie cookie, LongestMatchCache lmc, byte[] input, int from, int to, - char[] lengthArray, long[] costs) { - int windowStart = Math.max(from - 0x8000, 0); - Hash h = cookie.h; - h.init(input, windowStart, from, to); - Cookie.fillCostMax(costs, to - from + 1); - costs[0] = 0L; - lengthArray[0] = 0; - - char[] subLen = cookie.c259a; - for (int i = from; i < to; i++) { - int j = i - from; - h.updateHash(input, i, to); - - if (h.same[i & 0x7FFF] > 258 * 2 - && i > from + 258 + 1 - && i + 258 * 2 + 1 < to - && h.same[(i - 258) & 0x7FFF] - > 258) { - long symbolCost = fixedCost(258, 1); - for (int k = 0; k < 258; k++) { - costs[j + 258] = costs[j] + symbolCost; - lengthArray[j + 258] = 258; - i++; - j++; - h.updateHash(input, i, to); - } - } - - Deflate.findLongestMatch(cookie, lmc, from, h, input, i, to, 258, subLen); - - if (i + 1 <= to) { - long newCost = costs[j] + fixedCost(input[i] & 0xFF, 0); - if (newCost < costs[j + 1]) { - costs[j + 1] = newCost; - lengthArray[j + 1] = 1; - } - } - int lenValue = cookie.lenVal; - for (char k = 3; k <= lenValue && i + k <= to; k++) { - if (costs[j + k] - costs[j] <= 12.0) { - continue; - } - - long newCost = costs[j] + fixedCost(k, subLen[k]); - if (newCost < costs[j + k]) { - costs[j + k] = newCost; - lengthArray[j + k] = k; - } - } - } - } -} diff --git a/src/main/java/ru/eustas/zopfli/SymbolStats.java b/src/main/java/ru/eustas/zopfli/SymbolStats.java deleted file mode 100644 index 7dd9a2d..0000000 --- a/src/main/java/ru/eustas/zopfli/SymbolStats.java +++ /dev/null @@ -1,175 +0,0 @@ -/* -Copyright 2014 Google Inc. All Rights Reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -Author: eustas.ru@gmail.com (Eugene Klyuchnikov) -*/ - -package ru.eustas.zopfli; - -final class SymbolStats { - private final static double INV_LOG_2 = 1.4426950408889 * 0x10000L; /* 1.0 / log(2.0) */ - private final int[] litLens = new int[288]; - private final int[] dists = new int[32]; // Why 32? Expect 30. - final long[] lLiterals = new long[288]; - final long[] lLengths = new long[259]; - final long[] dSymbols = new long[32]; - - void getFreqs(LzStore store) { - int[] sLitLens = this.litLens; - int[] sDists = this.dists; - System.arraycopy(Cookie.intZeroes, 0, sLitLens, 0, 288); - System.arraycopy(Cookie.intZeroes, 0, sDists, 0, 32); - - int size = store.size; - char[] litLens = store.litLens; - char[] dists = store.dists; - int[] lengthSymbol = Util.LENGTH_SYMBOL; - int[] cachedDistSymbol = Util.CACHED_DIST_SYMBOL; - for (int i = 0; i < size; i++) { - int d = dists[i]; - int l = litLens[i]; - if (d == 0) { - sLitLens[l]++; - } else { - sLitLens[lengthSymbol[l]]++; - sDists[cachedDistSymbol[d]]++; - } - } - sLitLens[256] = 1; - calculate(); - } - - final void copy(final SymbolStats source) { - System.arraycopy(source.litLens, 0, litLens, 0, 288); - System.arraycopy(source.dists, 0, dists, 0, 32); - System.arraycopy(source.lLiterals, 0, lLiterals, 0, 288); - System.arraycopy(source.lLengths, 0, lLengths, 0, 259); - System.arraycopy(source.dSymbols, 0, dSymbols, 0, 32); - } - - final void calculate() { - calculateLens(); - calculateDists(); - } - - final void calculateLens() { - int sum = 0; - int[] litLens = this.litLens; - for (int i = 0; i < 288; ++i) { - sum += litLens[i]; - } - double log2sum = (sum == 0 ? Math.log(288) : Math.log(sum)) * INV_LOG_2; - long[] lLiterals = this.lLiterals; - for (int i = 0; i < 288; ++i) { - if (litLens[i] == 0) { - lLiterals[i] = (long) log2sum; - } else { - lLiterals[i] = (long) (log2sum - Math.log(litLens[i]) * INV_LOG_2); - } - if (lLiterals[i] < 0) { - lLiterals[i] = 0; - } - } - long[] lLengths = this.lLengths; - int[] lengthSymbol = Util.LENGTH_SYMBOL; - int[] lengthExtraBits = Util.LENGTH_EXTRA_BITS; - for (int i = 0; i < 259; ++i) { - lLengths[i] = lLiterals[lengthSymbol[i]] + (lengthExtraBits[i] * 0x10000L); - } - } - - final void calculateDists() { - int sum = 0; - int[] dists = this.dists; - for (int i = 0; i < 32; ++i) { - sum += dists[i]; - } - double log2sum = (sum == 0 ? Math.log(32) : Math.log(sum)) * INV_LOG_2; - long[] dSymbols = this.dSymbols; - for (int i = 0; i < 32; ++i) { - if (dists[i] == 0) { - dSymbols[i] = (long) log2sum; - } else { - dSymbols[i] = (long) (log2sum - Math.log(dists[i]) * INV_LOG_2); - } - if (dSymbols[i] < 0) { - dSymbols[i] = 0; - } - } - for (int i = 4; i < 30; ++i) { - dSymbols[i] += 0x10000L * ((i / 2) - 1); - } - } - - final void alloy(final SymbolStats ligand) { - for (int i = 0; i < 288; i++) { - litLens[i] += ligand.litLens[i] / 2; - } - litLens[256] = 1; - - for (int i = 0; i < 32; i++) { - dists[i] += ligand.dists[i] / 2; - } - } - - final int randomizeFreqs(int z) { - int[] data = litLens; - int n = data.length; - z = getZ(z, data, n); - data[256] = 1; - - data = dists; - n = data.length; - z = getZ(z, data, n); - - return z; - } - - private int getZ(int z, int[] data, int n) { - for (int i = 0; i < n; i++) { - z = 0x7FFFFFFF & (1103515245 * z + 12345); - if ((z >>> 4) % 3 == 0) { - z = 0x7FFFFFFF & (1103515245 * z + 12345); - int p = z % n; - if (data[i] < data[p]) { - data[i] = data[p]; - } - } - } - return z; - } - - final long minCost() { - long[] lLengths = this.lLengths; - long minLengthCost = lLengths[3]; - for (int i = 4; i < 259; i++) { - long c = lLengths[i]; - if (c < minLengthCost) { - minLengthCost = c; - } - } - - long[] dSymbols = this.dSymbols; - long minDistCost = dSymbols[0]; - for (int i = 1; i < 30; i++) { - long c = dSymbols[i]; - if (c < minDistCost) { - minDistCost = c; - } - } - - return minDistCost + minLengthCost; - } -} diff --git a/src/main/java/ru/eustas/zopfli/Util.java b/src/main/java/ru/eustas/zopfli/Util.java deleted file mode 100644 index fbba76d..0000000 --- a/src/main/java/ru/eustas/zopfli/Util.java +++ /dev/null @@ -1,230 +0,0 @@ -/* -Copyright 2014 Google Inc. All Rights Reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -Author: eustas.ru@gmail.com (Eugene Klyuchnikov) -*/ - -package ru.eustas.zopfli; - -class Util { - - static final int[] LENGTH_SYMBOL = new int[]{ - 0, 0, 0, - 257, 258, 259, 260, 261, 262, 263, 264, - 265, 265, - 266, 266, - 267, 267, - 268, 268, - 269, 269, 269, 269, - 270, 270, 270, 270, - 271, 271, 271, 271, - 272, 272, 272, 272, - 273, 273, 273, 273, 273, 273, 273, 273, - 274, 274, 274, 274, 274, 274, 274, 274, - 275, 275, 275, 275, 275, 275, 275, 275, - 276, 276, 276, 276, 276, 276, 276, 276, - 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, - 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, - 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, - 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, - 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, - 282, 282, 282, - 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, - 283, 283, 283, - 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, - 284, 284, - 285 - }; - - static final int[] LENGTH_EXTRA_BITS = new int[]{ - 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, - 1, 1, - 1, 1, - 1, 1, - 2, 2, 2, 2, - 2, 2, 2, 2, - 2, 2, 2, 2, - 2, 2, 2, 2, - 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 0 - }; - - static final int[] LENGTH_EXTRA_BITS_VALUE = new int[]{ - 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, - 0, 1, - 0, 1, - 0, 1, - 0, 1, 2, 3, - 0, 1, 2, 3, - 0, 1, 2, 3, - 0, 1, 2, 3, - 0, 1, 2, 3, 4, 5, 6, 7, - 0, 1, 2, 3, 4, 5, 6, 7, - 0, 1, 2, 3, 4, 5, 6, 7, - 0, 1, 2, 3, 4, 5, 6, 7, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, - 0 - }; - - static final int[] ORDER = new int[]{ - 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 - }; - - static final int[] CACHED_DIST_SYMBOL = cacheDistSymbol(); - - private static int[] cacheDistSymbol() { - int[] r = new int[32768]; - for (int i = 0; i < 32768; ++i) { - r[i] = distSymbol(i); - } - return r; - } - - private static int distSymbol(int dist) { - if (dist < 193) { - if (dist < 13) { /* dist 0..13. */ - if (dist < 5) return dist - 1; - else if (dist < 7) return 4; - else if (dist < 9) return 5; - else return 6; - } else { /* dist 13..193. */ - if (dist < 17) return 7; - else if (dist < 25) return 8; - else if (dist < 33) return 9; - else if (dist < 49) return 10; - else if (dist < 65) return 11; - else if (dist < 97) return 12; - else if (dist < 129) return 13; - else return 14; - } - } else { - if (dist < 2049) { /* dist 193..2049. */ - if (dist < 257) return 15; - else if (dist < 385) return 16; - else if (dist < 513) return 17; - else if (dist < 769) return 18; - else if (dist < 1025) return 19; - else if (dist < 1537) return 20; - else return 21; - } else { /* dist 2049..32768. */ - if (dist < 3073) return 22; - else if (dist < 4097) return 23; - else if (dist < 6145) return 24; - else if (dist < 8193) return 25; - else if (dist < 12289) return 26; - else if (dist < 16385) return 27; - else if (dist < 24577) return 28; - else return 29; - } - } - } - - static final int[] CACHED_DIST_EXTRA_BITS = precacheDistExtraBits(); - - private static int[] precacheDistExtraBits() { - int[] r = new int[4097]; - for (int i = 0; i < 4097; ++i) { - r[i] = distExtraBits(i); - } - return r; - } - - private static int distExtraBits(int dist) { - if (dist < 5) { - return 0; - } else if (dist < 9) { - return 1; - } else if (dist < 17) { - return 2; - } else if (dist < 33) { - return 3; - } else if (dist < 65) { - return 4; - } else if (dist < 129) { - return 5; - } else if (dist < 257) { - return 6; - } else if (dist < 513) { - return 7; - } else if (dist < 1025) { - return 8; - } else if (dist < 2049) { - return 9; - } else if (dist < 4097) { - return 10; - } else if (dist < 8193) { - return 11; - } else if (dist < 16385) { - return 12; - } - return 13; - } - - static int distExtraBitsValue(int dist) { - if (dist < 5) { - return 0; - } else if (dist < 9) { - return (dist - 5) & 1; - } else if (dist < 17) { - return (dist - 9) & 3; - } else if (dist < 33) { - return (dist - 17) & 7; - } else if (dist < 65) { - return (dist - 33) & 15; - } else if (dist < 129) { - return (dist - 65) & 31; - } else if (dist < 257) { - return (dist - 129) & 63; - } else if (dist < 513) { - return (dist - 257) & 127; - } else if (dist < 1025) { - return (dist - 513) & 255; - } else if (dist < 2049) { - return (dist - 1025) & 511; - } else if (dist < 4097) { - return (dist - 2049) & 1023; - } else if (dist < 8193) { - return (dist - 4097) & 2047; - } else if (dist < 16385) { - return (dist - 8193) & 4095; - } - return (dist - 16385) & 8191; - } - -} diff --git a/src/main/java/ru/eustas/zopfli/Zopfli.java b/src/main/java/ru/eustas/zopfli/Zopfli.java deleted file mode 100644 index 73e49b9..0000000 --- a/src/main/java/ru/eustas/zopfli/Zopfli.java +++ /dev/null @@ -1,41 +0,0 @@ -/* -Copyright 2014 Google Inc. All Rights Reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -Author: eustas.ru@gmail.com (Eugene Klyuchnikov) -*/ - -package ru.eustas.zopfli; - -public class Zopfli { - - private final Cookie cookie; - - public synchronized Buffer compress(Options options, byte[] input) { - Buffer output = new Buffer(); - switch (options.outputType) { - case DEFLATE: - Deflate.compress(cookie, options, input, output); - break; - default: - throw new IllegalArgumentException( - "Unexpected output format: " + options.outputType); - } - return output; - } - - public Zopfli(int masterBlockSize) { - cookie = new Cookie(masterBlockSize); - } -} diff --git a/src/main/java/systems/crigges/jmpq3/JMpqEditor.java b/src/main/java/systems/crigges/jmpq3/JMpqEditor.java index 2fa2831..5d67f9d 100644 --- a/src/main/java/systems/crigges/jmpq3/JMpqEditor.java +++ b/src/main/java/systems/crigges/jmpq3/JMpqEditor.java @@ -3,6 +3,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import systems.crigges.jmpq3.BlockTable.Block; +import systems.crigges.jmpq3.compression.RecompressOptions; import systems.crigges.jmpq3.security.MPQEncryption; import systems.crigges.jmpq3.security.MPQHashGenerator; @@ -674,14 +675,18 @@ public void close() throws IOException { close(true, true, false); } + public void close(boolean buildListfile, boolean buildAttributes, boolean recompress) throws IOException { + close(buildListfile, buildAttributes, new RecompressOptions(recompress)); + } + /** * @param buildListfile whether or not to add a (listfile) to this mpq * @param buildAttributes whether or not to add a (attributes) file to this mpq * @throws IOException */ - public void close(boolean buildListfile, boolean buildAttributes, boolean recompress) throws IOException { + public void close(boolean buildListfile, boolean buildAttributes, RecompressOptions options) throws IOException { // only rebuild if allowed - if (!canWrite || ! fc.isOpen()) { + if (!canWrite || !fc.isOpen()) { fc.close(); log.debug("closed readonly mpq."); return; @@ -716,8 +721,8 @@ public void close(boolean buildListfile, boolean buildAttributes, boolean recomp newHeaderSize = 208; break; } - newSectorSizeShift = sectorSizeShift; - newDiscBlockSize = discBlockSize; + newSectorSizeShift = options.recompress ? Math.min(options.newSectorSizeShift, 15) : sectorSizeShift; + newDiscBlockSize = options.recompress ? 512 * (1 << newSectorSizeShift) : discBlockSize; calcNewTableSize(); ArrayList newBlocks = new ArrayList<>(); @@ -737,7 +742,7 @@ public void close(boolean buildListfile, boolean buildAttributes, boolean recomp } for (String existingName : existingFiles) { - if (recompress && !existingName.endsWith("wav")) { + if (options.recompress && !existingName.endsWith("wav")) { ByteBuffer extracted = ByteBuffer.wrap(extractFileAsBytes(existingName)); internalFilename.put(extracted, existingName); filesToAdd.add(extracted); @@ -765,7 +770,7 @@ public void close(boolean buildListfile, boolean buildAttributes, boolean recomp MappedByteBuffer fileWriter = writeChannel.map(MapMode.READ_WRITE, currentPos, newFile.limit() * 2); Block newBlock = new Block(currentPos - (keepHeaderOffset ? headerOffset : 0), 0, 0, 0); newBlocks.add(newBlock); - MpqFile.writeFileAndBlock(newFile.array(), newBlock, fileWriter, newDiscBlockSize, recompress); + MpqFile.writeFileAndBlock(newFile.array(), newBlock, fileWriter, newDiscBlockSize, options); currentPos += newBlock.getCompressedSize(); log.debug("Added file " + internalFilename.get(newFile)); } @@ -777,7 +782,7 @@ public void close(boolean buildListfile, boolean buildAttributes, boolean recomp MappedByteBuffer fileWriter = writeChannel.map(MapMode.READ_WRITE, currentPos, listfileArr.length * 2); Block newBlock = new Block(currentPos - (keepHeaderOffset ? headerOffset : 0), 0, 0, EXISTS | COMPRESSED | ENCRYPTED | ADJUSTED_ENCRYPTED); newBlocks.add(newBlock); - MpqFile.writeFileAndBlock(listfileArr, newBlock, fileWriter, newDiscBlockSize, "(listfile)", recompress); + MpqFile.writeFileAndBlock(listfileArr, newBlock, fileWriter, newDiscBlockSize, "(listfile)", options); currentPos += newBlock.getCompressedSize(); log.debug("Added listfile"); } diff --git a/src/main/java/systems/crigges/jmpq3/MpqFile.java b/src/main/java/systems/crigges/jmpq3/MpqFile.java index 6569432..bbb404c 100644 --- a/src/main/java/systems/crigges/jmpq3/MpqFile.java +++ b/src/main/java/systems/crigges/jmpq3/MpqFile.java @@ -3,6 +3,7 @@ import systems.crigges.jmpq3.BlockTable.Block; import systems.crigges.jmpq3.compression.CompressionUtil; +import systems.crigges.jmpq3.compression.RecompressOptions; import systems.crigges.jmpq3.security.MPQEncryption; import systems.crigges.jmpq3.security.MPQHashGenerator; @@ -276,20 +277,19 @@ public void writeFileAndBlock(Block newBlock, MappedByteBuffer writeBuffer) thro * @param sectorSize the sector size * @param recompress */ - public static void writeFileAndBlock(byte[] file, Block b, MappedByteBuffer buf, int sectorSize, boolean recompress) { + public static void writeFileAndBlock(byte[] file, Block b, MappedByteBuffer buf, int sectorSize, RecompressOptions recompress) { writeFileAndBlock(file, b, buf, sectorSize, "", recompress); } /** * Write file and block. - * - * @param fileArr the file arr + * @param fileArr the file arr * @param b the b * @param buf the buf * @param sectorSize the sector size * @param recompress */ - public static void writeFileAndBlock(byte[] fileArr, Block b, MappedByteBuffer buf, int sectorSize, String pathlessName, boolean recompress) { + public static void writeFileAndBlock(byte[] fileArr, Block b, MappedByteBuffer buf, int sectorSize, String pathlessName, RecompressOptions recompress) { ByteBuffer fileBuf = ByteBuffer.wrap(fileArr); fileBuf.position(0); b.setNormalSize(fileArr.length); diff --git a/src/main/java/systems/crigges/jmpq3/compression/CompressionUtil.java b/src/main/java/systems/crigges/jmpq3/compression/CompressionUtil.java index ee845a1..adcc150 100644 --- a/src/main/java/systems/crigges/jmpq3/compression/CompressionUtil.java +++ b/src/main/java/systems/crigges/jmpq3/compression/CompressionUtil.java @@ -22,11 +22,11 @@ public class CompressionUtil { private static final byte FLAG_ADPCM2C = -0x80; private static final byte FLAG_LMZA = 0x12; - public static byte[] compress(byte[] temp, boolean recompress) { - if (recompress && zopfli == null) { + public static byte[] compress(byte[] temp, RecompressOptions recompress) { + if (recompress.recompress && recompress.useZopfli && zopfli == null) { zopfli = new ZopfliHelper(); } - return recompress ? zopfli.deflate(temp) : JzLibHelper.deflate(temp); + return recompress.useZopfli ? zopfli.deflate(temp, recompress.iterations) : JzLibHelper.deflate(temp); } public static byte[] decompress(byte[] sector, int compressedSize, int uncompressedSize) throws JMpqException { diff --git a/src/main/java/systems/crigges/jmpq3/compression/RecompressOptions.java b/src/main/java/systems/crigges/jmpq3/compression/RecompressOptions.java new file mode 100644 index 0000000..a4243de --- /dev/null +++ b/src/main/java/systems/crigges/jmpq3/compression/RecompressOptions.java @@ -0,0 +1,12 @@ +package systems.crigges.jmpq3.compression; + +public class RecompressOptions { + public boolean recompress; + public boolean useZopfli = false; + public int iterations = 15; + public int newSectorSizeShift = 3; + + public RecompressOptions(boolean recompress) { + this.recompress = recompress; + } +} diff --git a/src/main/java/systems/crigges/jmpq3/compression/ZopfliHelper.java b/src/main/java/systems/crigges/jmpq3/compression/ZopfliHelper.java index 7408102..378ed15 100644 --- a/src/main/java/systems/crigges/jmpq3/compression/ZopfliHelper.java +++ b/src/main/java/systems/crigges/jmpq3/compression/ZopfliHelper.java @@ -1,32 +1,24 @@ package systems.crigges.jmpq3.compression; -import ru.eustas.zopfli.Buffer; -import ru.eustas.zopfli.Options; -import ru.eustas.zopfli.Zopfli; + +import com.googlecode.pngtastic.core.processing.zopfli.Buffer; +import com.googlecode.pngtastic.core.processing.zopfli.Options; +import com.googlecode.pngtastic.core.processing.zopfli.Zopfli; /** * Created by Frotty on 09.05.2017. */ public class ZopfliHelper { private final Zopfli compressor; - private final Options options; - private static final byte[] ZLIB_MAGIC = {120, -38}; public ZopfliHelper() { - Options.OutputFormat outputType = Options.OutputFormat.DEFLATE; - Options.BlockSplitting blockSplitting = Options.BlockSplitting.FIRST; - int numIterations = 15; - compressor = new Zopfli(8 * 1024 * 1024); - options = new Options(outputType, blockSplitting, numIterations); } - public byte[] deflate(byte[] bytes) { - Buffer output = compressor.compress(options, bytes); - byte[] outputBytes = new byte[output.getSize() + 2]; - System.arraycopy(output.getData(), 0, outputBytes, 2, output.getSize()); - outputBytes[0] = ZLIB_MAGIC[0]; - outputBytes[1] = ZLIB_MAGIC[1]; + public byte[] deflate(byte[] bytes, int iterations) { + Buffer output = compressor.compress(new Options(Options.BlockSplitting.FIRST, iterations), bytes); + byte[] outputBytes = new byte[output.getSize()]; + System.arraycopy(output.getData(), 0, outputBytes, 0, output.getSize()); return outputBytes; } } diff --git a/src/test/java/systems/crigges/jmpq3test/MpqTests.java b/src/test/java/systems/crigges/jmpq3test/MpqTests.java index 065bcee..cfa8426 100644 --- a/src/test/java/systems/crigges/jmpq3test/MpqTests.java +++ b/src/test/java/systems/crigges/jmpq3test/MpqTests.java @@ -6,6 +6,7 @@ import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; import systems.crigges.jmpq3.*; +import systems.crigges.jmpq3.compression.RecompressOptions; import systems.crigges.jmpq3.security.MPQEncryption; import java.io.*; @@ -136,11 +137,14 @@ public void testRebuild() throws IOException { @Test public void testRecompressBuild() throws IOException { File[] mpqs = getMpqs(); + RecompressOptions options = new RecompressOptions(true); + options.newSectorSizeShift = 15; for (File mpq : mpqs) { log.info(mpq.getName()); JMpqEditor mpqEditor = new JMpqEditor(mpq, MPQOpenOption.FORCE_V0); long length = mpq.length(); - mpqEditor.close(true, true, true); + options.useZopfli = !options.useZopfli; + mpqEditor.close(true, true, options); long newlength = mpq.length(); System.out.println("Size win: " + (length - newlength)); } diff --git a/src/test/resources/mpqs/implodedTest.w3x b/src/test/resources/mpqs/implodedTest.w3x new file mode 100644 index 0000000..19cf83a Binary files /dev/null and b/src/test/resources/mpqs/implodedTest.w3x differ