diff --git a/app/build.gradle b/app/build.gradle index e9ee818c2..3516a9b42 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -71,12 +71,12 @@ dependencies { implementation "com.android.support:design:$supportLibrary" implementation 'com.google.firebase:firebase-core:16.0.6' implementation 'com.crashlytics.sdk.android:crashlytics:2.9.7' + implementation 'com.github.seven332:a7zip:4a484317b0' implementation 'com.github.amlcurran.showcaseview:library:5.4.3' implementation 'com.github.seven332:android-recaptcha:2bbb3459a8' implementation 'com.github.seven332:android-resource:0.1.0' implementation 'com.github.seven332:animator:0.1.0' implementation 'com.github.seven332:beerbelly:0.1.4' - implementation 'com.github.seven332.commons-compress:android-lite:464e96d3b4' implementation 'com.github.seven332:conaco:0.1.5-eh' implementation 'com.github.seven332:drawerlayout:0.2.1' implementation 'com.github.seven332:easyrecyclerview:0.1.1' @@ -85,7 +85,6 @@ dependencies { implementation 'com.github.seven332:glview-image:0.1.0' implementation 'com.github.seven332:hotspot:0.1.0' implementation 'com.github.seven332:image:0.1.12' - implementation 'com.github.seven332.junrar:android-lite:46ecc9630a' implementation 'com.github.seven332:okhttp:3.5.0' implementation 'com.github.seven332:refreshlayout:0.1.0' implementation 'com.github.seven332:ripple:0.1.2' diff --git a/app/src/main/java/com/hippo/ehviewer/EhApplication.java b/app/src/main/java/com/hippo/ehviewer/EhApplication.java index a1a2255fa..b54af5e2b 100644 --- a/app/src/main/java/com/hippo/ehviewer/EhApplication.java +++ b/app/src/main/java/com/hippo/ehviewer/EhApplication.java @@ -31,6 +31,7 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.util.LruCache; import android.util.Log; +import com.hippo.a7zip.A7Zip; import com.hippo.beerbelly.SimpleDiskCache; import com.hippo.conaco.Conaco; import com.hippo.content.RecordingApplication; @@ -104,6 +105,7 @@ public class EhApplication extends RecordingApplication { EhEngine.initialize(); BitmapUtils.initialize(this); Image.initialize(this); + A7Zip.initialize(this); if (EhDB.needMerge()) { EhDB.mergeOldDB(this); diff --git a/app/src/main/java/com/hippo/ehviewer/gallery/A7ZipArchive.java b/app/src/main/java/com/hippo/ehviewer/gallery/A7ZipArchive.java new file mode 100644 index 000000000..85e838b9c --- /dev/null +++ b/app/src/main/java/com/hippo/ehviewer/gallery/A7ZipArchive.java @@ -0,0 +1,102 @@ +/* + * Copyright 2019 Hippo Seven + * + * 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. + */ + +package com.hippo.ehviewer.gallery; + +import com.hippo.a7zip.ArchiveException; +import com.hippo.a7zip.InArchive; +import com.hippo.a7zip.PropID; +import com.hippo.a7zip.PropType; +import com.hippo.unifile.UniRandomAccessFile; +import java.io.Closeable; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; +import okio.Store; +import okio.UniRandomAccessFileStore; + +class A7ZipArchive implements Closeable { + + private InArchive archive; + + private A7ZipArchive(InArchive archive) { + this.archive = archive; + } + + @Override + public void close() { + archive.close(); + } + + private static boolean isSupportedFilename(String name) { + for (String extension : GalleryProvider2.SUPPORT_IMAGE_EXTENSIONS) { + if (name.endsWith(extension)) { + return true; + } + } + return false; + } + + List getArchiveEntries() { + List entries = new ArrayList<>(); + + for (int i = 0, n = archive.getNumberOfEntries(); i < n; i++) { + if (!archive.getEntryBooleanProperty(i, PropID.ENCRYPTED) + && !archive.getEntryBooleanProperty(i, PropID.IS_DIR) + && !archive.getEntryBooleanProperty(i, PropID.IS_VOLUME) + && !archive.getEntryBooleanProperty(i, PropID.SOLID)) { + String path = archive.getEntryPath(i); + if (isSupportedFilename(path.toLowerCase())) { + entries.add(new A7ZipArchiveEntry(archive, i, path)); + } + } + } + + return entries; + } + + static A7ZipArchive create(UniRandomAccessFile file) throws ArchiveException { + Store store = new UniRandomAccessFileStore(file); + InArchive archive = InArchive.create(store); + if ((archive.getArchivePropertyType(PropID.ENCRYPTED) == PropType.BOOL && archive.getArchiveBooleanProperty(PropID.ENCRYPTED)) + || (archive.getArchivePropertyType(PropID.SOLID) == PropType.BOOL && archive.getArchiveBooleanProperty(PropID.SOLID)) + || (archive.getArchivePropertyType(PropID.IS_VOLUME) == PropType.BOOL && archive.getArchiveBooleanProperty(PropID.IS_VOLUME))) { + throw new ArchiveException("Unsupported archive"); + } + return new A7ZipArchive(archive); + } + + static class A7ZipArchiveEntry { + + private InArchive archive; + private int index; + private String path; + + private A7ZipArchiveEntry(InArchive archive, int index, String path) { + this.archive = archive; + this.index = index; + this.path = path; + } + + String getPath() { + return path; + } + + void extract(OutputStream os) throws ArchiveException { + archive.extractEntry(index, os); + } + } +} diff --git a/app/src/main/java/com/hippo/ehviewer/gallery/ArchiveGalleryProvider.java b/app/src/main/java/com/hippo/ehviewer/gallery/ArchiveGalleryProvider.java index 5954a0964..f5654187b 100644 --- a/app/src/main/java/com/hippo/ehviewer/gallery/ArchiveGalleryProvider.java +++ b/app/src/main/java/com/hippo/ehviewer/gallery/ArchiveGalleryProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 Hippo Seven + * Copyright 2019 Hippo Seven * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,38 +21,43 @@ import android.net.Uri; import android.os.Process; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import com.hippo.a7zip.ArchiveException; import com.hippo.ehviewer.GetText; import com.hippo.ehviewer.R; -import com.hippo.ehviewer.gallery.archive.Archive; -import com.hippo.ehviewer.gallery.archive.ArchiveEntry; import com.hippo.glgallery.GalleryPageView; import com.hippo.image.Image; import com.hippo.unifile.UniFile; import com.hippo.unifile.UniRandomAccessFile; import com.hippo.util.NaturalComparator; -import com.hippo.yorozuya.IOUtils; import com.hippo.yorozuya.thread.PriorityThread; import java.io.IOException; import java.io.InputStream; import java.util.Collections; import java.util.Comparator; +import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.Stack; import java.util.concurrent.atomic.AtomicInteger; -public class ArchiveGalleryProvider extends GalleryProvider2 implements Runnable { +public class ArchiveGalleryProvider extends GalleryProvider2 { - private static final String TAG = ArchiveGalleryProvider.class.getSimpleName(); private static final AtomicInteger sIdGenerator = new AtomicInteger(); private final UniFile file; - private final Stack requests = new Stack<>(); - private final AtomicInteger decodingIndex = new AtomicInteger(GalleryPageView.INVALID_INDEX); - @Nullable - private Thread bgThread; + + private Thread archiveThread; + private Thread decodeThread; + private volatile int size = STATE_WAIT; private String error; + private final Stack requests = new Stack<>(); + private final AtomicInteger extractingIndex = new AtomicInteger(GalleryPageView.INVALID_INDEX); + private final LinkedHashMap streams = new LinkedHashMap<>(); + private final AtomicInteger decodingIndex = new AtomicInteger(GalleryPageView.INVALID_INDEX); + public ArchiveGalleryProvider(Context context, Uri uri) { file = UniFile.fromUri(context, uri); } @@ -61,18 +66,28 @@ public class ArchiveGalleryProvider extends GalleryProvider2 implements Runnable public void start() { super.start(); - bgThread = new PriorityThread(this, TAG + '-' + sIdGenerator.incrementAndGet(), - Process.THREAD_PRIORITY_BACKGROUND); - bgThread.start(); + int id = sIdGenerator.incrementAndGet(); + + archiveThread = new PriorityThread( + new ArchiveTask(), "ArchiveTask" + '-' + id, Process.THREAD_PRIORITY_BACKGROUND); + archiveThread.start(); + + decodeThread = new PriorityThread( + new DecodeTask(), "DecodeTask" + '-' + id, Process.THREAD_PRIORITY_BACKGROUND); + decodeThread.start(); } @Override public void stop() { super.stop(); - if (bgThread != null) { - bgThread.interrupt(); - bgThread = null; + if (archiveThread != null) { + archiveThread.interrupt(); + archiveThread = null; + } + if (decodeThread != null) { + decodeThread.interrupt(); + decodeThread = null; } } @@ -83,8 +98,14 @@ public class ArchiveGalleryProvider extends GalleryProvider2 implements Runnable @Override protected void onRequest(int index) { + boolean inDecodeTask; + synchronized (streams) { + inDecodeTask = streams.keySet().contains(index) || index == decodingIndex.get(); + } + synchronized (requests) { - if (!requests.contains(index) && index != decodingIndex.get()) { + boolean inArchiveTask = requests.contains(index) || index == extractingIndex.get(); + if (!inArchiveTask && !inDecodeTask) { requests.add(index); requests.notify(); } @@ -129,90 +150,132 @@ public class ArchiveGalleryProvider extends GalleryProvider2 implements Runnable return null; } - @Override - public void run() { - UniRandomAccessFile uraf = null; - if (file != null) { + private class ArchiveTask implements Runnable { + @Override + public void run() { + UniRandomAccessFile uraf = null; + if (file != null) { + try { + uraf = file.createRandomAccessFile("r"); + } catch (IOException e) { + e.printStackTrace(); + } + } + if (uraf == null) { + size = STATE_ERROR; + error = GetText.getString(R.string.error_reading_failed); + notifyDataChanged(); + return; + } + + A7ZipArchive archive = null; try { - uraf = file.createRandomAccessFile("r"); - } catch (IOException e) { + archive = A7ZipArchive.create(uraf); + } catch (ArchiveException e) { e.printStackTrace(); } - } - if (uraf == null) { - size = STATE_ERROR; - error = GetText.getString(R.string.error_reading_failed); + if (archive == null) { + size = STATE_ERROR; + error = GetText.getString(R.string.error_invalid_archive); + notifyDataChanged(); + return; + } + + List entries = archive.getArchiveEntries(); + Collections.sort(entries, naturalComparator); + + // Update size and notify changed + size = entries.size(); notifyDataChanged(); - return; - } - Archive archive = null; - try { - archive = Archive.create(uraf); - } catch (IOException e) { - e.printStackTrace(); - } - if (archive == null) { - size = STATE_ERROR; - error = GetText.getString(R.string.error_invalid_archive); - notifyDataChanged(); - return; - } - - List entries = archive.getArchiveEntries(); - Collections.sort(entries, naturalComparator); - - // Update size and notify changed - size = entries.size(); - notifyDataChanged(); - - while (!Thread.currentThread().isInterrupted()) { - int index; - synchronized (requests) { - if (requests.isEmpty()) { - try { - requests.wait(); - } catch (InterruptedException e) { - // Interrupted - break; + while (!Thread.currentThread().isInterrupted()) { + int index; + synchronized (requests) { + if (requests.isEmpty()) { + try { + requests.wait(); + } catch (InterruptedException e) { + // Interrupted + break; + } + continue; } + index = requests.pop(); + extractingIndex.lazySet(index); + } + + // Check index valid + if (index < 0 || index >= entries.size()) { + extractingIndex.lazySet(GalleryPageView.INVALID_INDEX); + notifyPageFailed(index, GetText.getString(R.string.error_out_of_range)); continue; } - index = requests.pop(); - decodingIndex.lazySet(index); - } - // Check index valid - if (index < 0 || index >= entries.size()) { - decodingIndex.lazySet(GalleryPageView.INVALID_INDEX); - notifyPageFailed(index, GetText.getString(R.string.error_out_of_range)); - continue; - } + Pipe pipe = new Pipe(4 * 1024); - try { - InputStream is = entries.get(index).getInputStream(); - Image image = Image.decode(is, true); - decodingIndex.lazySet(GalleryPageView.INVALID_INDEX); - if (image != null) { - notifyPageSucceed(index, image); - } else { - notifyPageFailed(index, GetText.getString(R.string.error_decoding_failed)); + synchronized (streams) { + if (streams.get(index) != null) { + continue; + } + streams.put(index, pipe.getInputStream()); + streams.notify(); + } + + try { + entries.get(index).extract(pipe.getOutputStream()); + } catch (ArchiveException e) { + e.printStackTrace(); + } finally { + extractingIndex.lazySet(GalleryPageView.INVALID_INDEX); } - } catch (IOException e) { - decodingIndex.lazySet(GalleryPageView.INVALID_INDEX); - notifyPageFailed(index, GetText.getString(R.string.error_reading_failed)); } } - - // Clear - IOUtils.closeQuietly(archive); } - private static Comparator naturalComparator = new Comparator() { + private class DecodeTask implements Runnable { + @Override + public void run() { + while (!Thread.currentThread().isInterrupted()) { + int index; + InputStream stream; + synchronized (streams) { + if (streams.isEmpty()) { + try { + streams.wait(); + } catch (InterruptedException e) { + // Interrupted + break; + } + continue; + } + + Iterator> iterator = streams.entrySet().iterator(); + Map.Entry entry = iterator.next(); + iterator.remove(); + index = entry.getKey(); + stream = entry.getValue(); + decodingIndex.lazySet(index); + } + + try { + Image image = Image.decode(stream, true); + if (image != null) { + notifyPageSucceed(index, image); + } else { + notifyPageFailed(index, GetText.getString(R.string.error_decoding_failed)); + } + } finally { + decodingIndex.lazySet(GalleryPageView.INVALID_INDEX); + } + } + } + } + + private static Comparator naturalComparator = new Comparator() { private NaturalComparator comparator = new NaturalComparator(); @Override - public int compare(ArchiveEntry o1, ArchiveEntry o2) { - return comparator.compare(o1.getName(), o2.getName()); + public int compare(A7ZipArchive.A7ZipArchiveEntry o1, A7ZipArchive.A7ZipArchiveEntry o2) { + return comparator.compare(o1.getPath(), o2.getPath()); } }; } diff --git a/app/src/main/java/com/hippo/ehviewer/gallery/Pipe.java b/app/src/main/java/com/hippo/ehviewer/gallery/Pipe.java new file mode 100644 index 000000000..ad20bbc47 --- /dev/null +++ b/app/src/main/java/com/hippo/ehviewer/gallery/Pipe.java @@ -0,0 +1,160 @@ +/* + * Copyright 2019 Hippo Seven + * + * 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. + */ + +package com.hippo.ehviewer.gallery; + +import android.support.annotation.NonNull; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +class Pipe { + + private final int capacity; + private final byte[] buffer; + + private int head = 0; + private int tail = 0; + private boolean full = false; + + private boolean inClosed = false; + private boolean outClosed = false; + + private InputStream inputStream = new InputStream() { + @Override + public int read() throws IOException { + synchronized (Pipe.this) { + byte[] bytes = new byte[1]; + if (read(bytes, 0, 1) != -1) { + return bytes[0]; + } else { + return -1; + } + } + } + + @Override + public int read(@NonNull byte[] b, int off, int len) throws IOException { + synchronized (Pipe.this) { + for (;;) { + if (inClosed) { + throw new IOException("The InputStream is closed"); + } + if (len == 0) { + return 0; + } + + if (head == tail && !full) { + if (outClosed) { + // No bytes available and the OutputStream is closed. So it's the end. + return -1; + } else { + // Wait for OutputStream write bytes + try { + Pipe.this.wait(); + } catch (InterruptedException e) { + throw new IOException("The thread interrupted", e); + } + } + } else { + int read = Math.min(len, (head < tail ? tail : capacity) - head); + System.arraycopy(buffer, head, b, off, read); + head += read; + if (head == capacity) { + head = 0; + } + full = false; + Pipe.this.notifyAll(); + return read; + } + } + } + } + + @Override + public void close() { + synchronized (Pipe.this) { + inClosed = true; + } + } + }; + + private OutputStream outputStream = new OutputStream() { + @Override + public void write(int b) throws IOException { + synchronized (Pipe.this) { + byte[] bytes = new byte[] { (byte) b}; + write(bytes, 0, 1); + } + } + + @Override + public void write(@NonNull byte[] b, int off, int len) throws IOException { + synchronized (Pipe.this) { + while (len != 0) { + if (outClosed) { + throw new IOException("The OutputStream is closed"); + } + if (inClosed) { + throw new IOException("The InputStream is closed"); + } + + if (head == tail && full) { + // The buffer is full, wait for InputStream read bytes + try { + Pipe.this.wait(); + } catch (InterruptedException e) { + throw new IOException("The thread interrupted", e); + } + } else { + int write = Math.min(len, (head <= tail ? capacity : head) - tail); + System.arraycopy(b, off, buffer, tail, write); + off += write; + len -= write; + tail += write; + if (tail == capacity) { + tail = 0; + } + if (head == tail) { + full = true; + } + Pipe.this.notifyAll(); + } + } + } + } + + @Override + public void close() { + synchronized (Pipe.this) { + outClosed = true; + } + } + }; + + Pipe(int capacity) { + this.capacity = capacity; + this.buffer = new byte[capacity]; + } + + InputStream getInputStream() { + return inputStream; + } + + OutputStream getOutputStream() { + return outputStream; + } +} diff --git a/app/src/main/java/com/hippo/ehviewer/gallery/archive/Archive.java b/app/src/main/java/com/hippo/ehviewer/gallery/archive/Archive.java deleted file mode 100644 index 03dcaaeab..000000000 --- a/app/src/main/java/com/hippo/ehviewer/gallery/archive/Archive.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2018 Hippo Seven - * - * 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. - */ - -package com.hippo.ehviewer.gallery.archive; - -import com.hippo.ehviewer.gallery.GalleryProvider2; -import com.hippo.unifile.UniRandomAccessFile; -import java.io.Closeable; -import java.io.IOException; -import java.util.List; - -public abstract class Archive implements Closeable { - - private static final byte[] FILE_SIGNATURE_SEVEN_Z = { 0x37, 0x7A, (byte) 0xBC, (byte) 0xAF, 0x27, 0x1C }; - private static final byte[] FILE_SIGNATURE_ZIP = { 0x50, 0x4B }; - private static final byte[] FILE_SIGNATURE_RAR = { 0x52, 0x61, 0x72, 0x21, 0x1A, 0x07, 0x00 }; - - public abstract List getArchiveEntries(); - - public static Archive create(UniRandomAccessFile file) throws IOException { - byte[] bytes = new byte[8]; - readFully(file, bytes, 8); - file.seek(0); - - if (equals(bytes, FILE_SIGNATURE_SEVEN_Z, FILE_SIGNATURE_SEVEN_Z.length)) { - return SevenZArchive.create(file); - } else if (equals(bytes, FILE_SIGNATURE_ZIP, FILE_SIGNATURE_ZIP.length)) { - return ZipArchive.create(file); - } else if (equals(bytes, FILE_SIGNATURE_RAR, FILE_SIGNATURE_RAR.length)) { - return RarArchive.create(file); - } - - return null; - } - - static boolean isSupportedFilename(String name) { - for (String extension : GalleryProvider2.SUPPORT_IMAGE_EXTENSIONS) { - if (name.endsWith(extension)) { - return true; - } - } - return false; - } - - private static int readFully(UniRandomAccessFile file, byte[] bytes, int count) throws IOException { - if (count <= 0) { - return 0; - } - - int read = 0; - do { - int l = file.read(bytes, read, count - read); - if (l < 0) - break; - read += l; - } while (read < count); - - if (read == 0) { - return -1; - } else { - return read; - } - } - - private static boolean equals(byte[] b1, byte[] b2, int size) { - if (b1.length < size || b2.length < size) { - return false; - } - - for (int i = 0; i < size; i++) { - if (b1[i] != b2[i]) { - return false; - } - } - - return true; - } -} diff --git a/app/src/main/java/com/hippo/ehviewer/gallery/archive/ArchiveEntry.java b/app/src/main/java/com/hippo/ehviewer/gallery/archive/ArchiveEntry.java deleted file mode 100644 index 250faa39e..000000000 --- a/app/src/main/java/com/hippo/ehviewer/gallery/archive/ArchiveEntry.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2018 Hippo Seven - * - * 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. - */ - -package com.hippo.ehviewer.gallery.archive; - -import java.io.IOException; -import java.io.InputStream; - -public abstract class ArchiveEntry { - - public abstract String getName(); - - public abstract InputStream getInputStream() throws IOException; -} diff --git a/app/src/main/java/com/hippo/ehviewer/gallery/archive/RarArchive.java b/app/src/main/java/com/hippo/ehviewer/gallery/archive/RarArchive.java deleted file mode 100644 index f7ada25b1..000000000 --- a/app/src/main/java/com/hippo/ehviewer/gallery/archive/RarArchive.java +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Copyright 2018 Hippo Seven - * - * 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. - */ - -package com.hippo.ehviewer.gallery.archive; - -import com.github.junrar.Volume; -import com.github.junrar.VolumeManager; -import com.github.junrar.exception.RarException; -import com.github.junrar.io.IReadOnlyAccess; -import com.github.junrar.rarfile.FileHeader; -import com.hippo.unifile.UniRandomAccessFile; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.List; - -public class RarArchive extends Archive { - - private com.github.junrar.Archive file; - - private RarArchive(com.github.junrar.Archive file) { - this.file = file; - } - - @Override - public List getArchiveEntries() { - List result = new ArrayList<>(); - - for (FileHeader fileHeader : file.getFileHeaders()) { - if (isSupportedFilename(fileHeader.getFileNameString().toLowerCase())) { - result.add(new RarArchiveEntry(file, fileHeader)); - } - } - - return result; - } - - @Override - public void close() throws IOException { - file.close(); - } - - public static Archive create(UniRandomAccessFile file) throws IOException { - VolumeManager volumeManager = new UniRandomAccessFileVolumeManager(file); - com.github.junrar.Archive archive; - try { - archive = new com.github.junrar.Archive(volumeManager); - if (archive.getMainHeader().isSolid()) { - throw new IOException("Solid archive is not supported"); - } - } catch (RarException e) { - throw new IOException("Invalid rar archive", e); - } - return new RarArchive(archive); - } - - public static class RarArchiveEntry extends ArchiveEntry { - - private com.github.junrar.Archive file; - private FileHeader header; - - private RarArchiveEntry(com.github.junrar.Archive file, FileHeader header) { - this.file = file; - this.header = header; - } - - @Override - public String getName() { - return header.getFileNameString(); - } - - @Override - public InputStream getInputStream() throws IOException { - try { - return file.getInputStream(header); - } catch (RarException e) { - throw new IOException("Can't getInputStream from rar archive", e); - } - } - } - - private static class UniRandomAccessFileVolumeManager implements VolumeManager { - - private UniRandomAccessFile file; - - private UniRandomAccessFileVolumeManager(UniRandomAccessFile file) { - this.file = file; - } - - @Override - public Volume nextArchive(com.github.junrar.Archive archive, Volume lastVolume) { - if (lastVolume == null) { - return new UniRandomAccessFileVolume(archive, file); - } else { - return null; - } - } - } - - private static class UniRandomAccessFileVolume implements Volume { - - private com.github.junrar.Archive archive; - private UniRandomAccessFile file; - - private UniRandomAccessFileVolume(com.github.junrar.Archive archive, UniRandomAccessFile file) { - this.archive = archive; - this.file = file; - } - - @Override - public IReadOnlyAccess getReadOnlyAccess() { - return new UniReadOnlyAccessFile(file); - } - - @Override - public long getLength() throws IOException { - return file.length(); - } - - @Override - public com.github.junrar.Archive getArchive() { - return archive; - } - } - - private static class UniReadOnlyAccessFile implements IReadOnlyAccess { - - private UniRandomAccessFile file; - - private UniReadOnlyAccessFile(UniRandomAccessFile file) { - this.file = file; - } - - @Override - public long getPosition() throws IOException { - return file.getFilePointer(); - } - - @Override - public void setPosition(long pos) throws IOException { - file.seek(pos); - } - - @Override - public int read() throws IOException { - byte[] b = new byte[1]; - if (file.read(b) == -1) { - return -1; - } else { - return b[0]; - } - } - - @Override - public int read(byte[] buffer, int off, int count) throws IOException { - return file.read(buffer, off, count); - } - - @Override - public int readFully(byte[] buffer, int count) throws IOException { - if (count <= 0) { - return 0; - } - - int read = 0; - do { - int l = read(buffer, read, count - read); - if (l < 0) - break; - read += l; - } while (read < count); - - if (read == 0) { - return -1; - } else { - return read; - } - } - - @Override - public void close() throws IOException { - file.close(); - } - } -} diff --git a/app/src/main/java/com/hippo/ehviewer/gallery/archive/SevenZArchive.java b/app/src/main/java/com/hippo/ehviewer/gallery/archive/SevenZArchive.java deleted file mode 100644 index 161a9085b..000000000 --- a/app/src/main/java/com/hippo/ehviewer/gallery/archive/SevenZArchive.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2018 Hippo Seven - * - * 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. - */ - -package com.hippo.ehviewer.gallery.archive; - -import com.hippo.unifile.UniRandomAccessFile; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.List; -import okio.Store; -import okio.UniRandomAccessFileStore; -import org.apache.commons.compress.archivers.sevenz.SevenZFile; - -public class SevenZArchive extends Archive { - - private SevenZFile file; - - public SevenZArchive(SevenZFile file) { - this.file = file; - } - - @Override - public List getArchiveEntries() { - List result = new ArrayList<>(); - - for (org.apache.commons.compress.archivers.sevenz.SevenZArchiveEntry archive : file.getEntries()) { - if (archive.getSize() != 0 - && !archive.isDirectory() - && isSupportedFilename(archive.getName().toLowerCase())) { - result.add(new SevenZArchiveEntry(file, archive)); - } - } - - return result; - } - - @Override - public void close() throws IOException { - file.close(); - } - - public static Archive create(UniRandomAccessFile file) throws IOException { - Store store = new UniRandomAccessFileStore(file); - SevenZFile sevenZFile = new SevenZFile(store); - - // Check whether the archive is solid - for (org.apache.commons.compress.archivers.sevenz.SevenZArchiveEntry entry : sevenZFile.getEntries()) { - if (entry.getSize() != 0 - && !entry.isDirectory() - && !entry.isFirstInFolder()) { - throw new IOException("Solid archive is not supported"); - } - } - - return new SevenZArchive(sevenZFile); - } - - public static class SevenZArchiveEntry extends ArchiveEntry { - - private SevenZFile file; - private org.apache.commons.compress.archivers.sevenz.SevenZArchiveEntry entry; - - private SevenZArchiveEntry( - SevenZFile file, - org.apache.commons.compress.archivers.sevenz.SevenZArchiveEntry entry - ) { - this.file = file; - this.entry = entry; - } - - @Override - public String getName() { - return entry.getName(); - } - - @Override - public InputStream getInputStream() throws IOException { - file.setEntry(entry); - return file.getInputStreamForCurrentEntry(); - } - } -} diff --git a/app/src/main/java/com/hippo/ehviewer/gallery/archive/ZipArchive.java b/app/src/main/java/com/hippo/ehviewer/gallery/archive/ZipArchive.java deleted file mode 100644 index 23891bde4..000000000 --- a/app/src/main/java/com/hippo/ehviewer/gallery/archive/ZipArchive.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2018 Hippo Seven - * - * 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. - */ - -package com.hippo.ehviewer.gallery.archive; - -import com.hippo.unifile.UniRandomAccessFile; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.List; -import okio.Store; -import okio.UniRandomAccessFileStore; -import org.apache.commons.compress.archivers.zip.ZipFile; - -public class ZipArchive extends Archive { - - private ZipFile file; - - private ZipArchive(ZipFile file) { - this.file = file; - } - - @Override - public List getArchiveEntries() { - List result = new ArrayList<>(); - - Enumeration entries = file.getEntries(); - while (entries.hasMoreElements()) { - org.apache.commons.compress.archivers.zip.ZipArchiveEntry entry = entries.nextElement(); - if (isSupportedFilename(entry.getName().toLowerCase())) { - result.add(new ZipArchiveEntry(file, entry)); - } - } - - return result; - } - - @Override - public void close() throws IOException { - file.close(); - } - - public static Archive create(UniRandomAccessFile file) throws IOException { - Store store = new UniRandomAccessFileStore(file); - ZipFile zipFile = new ZipFile(store); - return new ZipArchive(zipFile); - } - - public static class ZipArchiveEntry extends ArchiveEntry { - - private ZipFile file; - private org.apache.commons.compress.archivers.zip.ZipArchiveEntry entry; - - private ZipArchiveEntry( - ZipFile file, - org.apache.commons.compress.archivers.zip.ZipArchiveEntry entry - ) { - this.file = file; - this.entry = entry; - } - - @Override - public String getName() { - return entry.getName(); - } - - @Override - public InputStream getInputStream() throws IOException { - return file.getInputStream(entry); - } - } -}