diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 3ba29b4c6..3e159bff2 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -210,6 +210,7 @@ dependencies { // Disk implementation(libs.disklrucache) implementation(libs.unifile) + implementation(libs.compress) implementation(libs.junrar) // SY --> implementation(libs.zip4j) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ChapterLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ChapterLoader.kt index 59173243e..6eeffbeaa 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ChapterLoader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ChapterLoader.kt @@ -101,13 +101,7 @@ class ChapterLoader( source is LocalSource -> source.getFormat(chapter.chapter).let { format -> when (format) { is Format.Directory -> DirectoryPageLoader(format.file) - // SY --> - is Format.Zip -> try { - ZipPageLoader(format.file, context) - } catch (e: Throwable) { - error(context.getString(R.string.wrong_cbz_archive_password)) - } - // SY <-- + is Format.Zip -> ZipPageLoader(format.file) is Format.Rar -> try { RarPageLoader(format.file) } catch (e: UnsupportedRarV5Exception) { @@ -124,13 +118,7 @@ class ChapterLoader( source is LocalSource -> source.getFormat(chapter.chapter).let { format -> when (format) { is Format.Directory -> DirectoryPageLoader(format.file) - // SY --> - is Format.Zip -> try { - ZipPageLoader(format.file, context) - } catch (e: Throwable) { - error(context.getString(R.string.wrong_cbz_archive_password)) - } - // SY <-- + is Format.Zip -> ZipPageLoader(format.file) is Format.Rar -> try { RarPageLoader(format.file) } catch (e: UnsupportedRarV5Exception) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/DownloadPageLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/DownloadPageLoader.kt index 7518158e8..b20c81e24 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/DownloadPageLoader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/DownloadPageLoader.kt @@ -47,9 +47,7 @@ internal class DownloadPageLoader( } private suspend fun getPagesFromArchive(chapterPath: UniFile): List { - // SY --> - val loader = ZipPageLoader(File(chapterPath.filePath!!), context).also { zipPageLoader = it } - // SY <-- + val loader = ZipPageLoader(File(chapterPath.filePath!!)).also { zipPageLoader = it } return loader.getPages() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ZipPageLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ZipPageLoader.kt index 0bccbd695..03dd9fe08 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ZipPageLoader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ZipPageLoader.kt @@ -1,27 +1,23 @@ package eu.kanade.tachiyomi.ui.reader.loader import android.app.Application -import android.content.Context import android.os.Build import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.ui.reader.model.ReaderPage import eu.kanade.tachiyomi.util.storage.CbzCrypto -import net.lingala.zip4j.ZipFile +import org.apache.commons.compress.archivers.zip.ZipFile +import org.apache.commons.compress.utils.SeekableInMemoryByteChannel import uy.kohesive.injekt.injectLazy +import java.io.ByteArrayOutputStream import java.io.File import java.io.FileInputStream import java.nio.charset.StandardCharsets -import java.util.zip.ZipInputStream +import net.lingala.zip4j.ZipFile as Zip4JFile /** * Loader used to load a chapter from a .zip or .cbz file. */ -internal class ZipPageLoader( - file: File, - // SY --> - context: Context, - // SY <-- -) : PageLoader() { +internal class ZipPageLoader(file: File) : PageLoader() { private val context: Application by injectLazy() private val tmpDir = File(context.externalCacheDir, "reader_${file.hashCode()}").also { @@ -31,11 +27,11 @@ internal class ZipPageLoader( // SY --> init { - val zip = ZipFile(file) + val zip = Zip4JFile(file) if (zip.isEncrypted) { if (!CbzCrypto.checkCbzPassword(zip, CbzCrypto.getDecryptedPasswordCbz())) { this.recycle() - throw Exception(context.getString(R.string.wrong_cbz_archive_password)) + throw IllegalStateException(context.getString(R.string.wrong_cbz_archive_password)) } unzipEncrypted(zip) } else { @@ -44,34 +40,26 @@ internal class ZipPageLoader( } private fun unzip(file: File) { // SY <-- - ZipInputStream(FileInputStream(file)).use { zipInputStream -> - generateSequence { zipInputStream.nextEntry } - .filterNot { it.isDirectory } - .forEach { entry -> - File(tmpDir, entry.name.substringAfterLast("/")) - .also { it.createNewFile() } - .outputStream().use { pageOutputStream -> - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - pageOutputStream.write(zipInputStream.readNBytes(entry.size.toInt())) - } else { - val buffer = ByteArray(2048) - var len: Int - while ( - zipInputStream.read(buffer, 0, buffer.size) - .also { len = it } >= 0 - ) { - pageOutputStream.write(buffer, 0, len) - } + ByteArrayOutputStream().use { byteArrayOutputStream -> + FileInputStream(file).use { it.copyTo(byteArrayOutputStream) } + + ZipFile(SeekableInMemoryByteChannel(byteArrayOutputStream.toByteArray())).use { zip -> + zip.entries.asSequence() + .filterNot { it.isDirectory } + .forEach { entry -> + File(tmpDir, entry.name.substringAfterLast("/")) + .also { it.createNewFile() } + .outputStream().use { pageOutputStream -> + zip.getInputStream(entry).copyTo(pageOutputStream) + pageOutputStream.flush() } - pageOutputStream.flush() - } - zipInputStream.closeEntry() - } + } + } } } // SY --> - private fun unzipEncrypted(zip: ZipFile) { + private fun unzipEncrypted(zip: Zip4JFile) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { zip.charset = StandardCharsets.ISO_8859_1 } diff --git a/core/src/main/java/eu/kanade/tachiyomi/util/storage/CbzCrypto.kt b/core/src/main/java/eu/kanade/tachiyomi/util/storage/CbzCrypto.kt index 6b35fa172..242b19a0a 100644 --- a/core/src/main/java/eu/kanade/tachiyomi/util/storage/CbzCrypto.kt +++ b/core/src/main/java/eu/kanade/tachiyomi/util/storage/CbzCrypto.kt @@ -146,10 +146,12 @@ object CbzCrypto { return decrypt(securityPreferences.sqlPassword().get(), ALIAS_SQL).toByteArray() } - /** Function that returns true when the supplied password - * can Successfully decrypt the supplied zip archive */ - // not very elegant but this is the solution recommended by the maintainer for checking passwords - // a real password check will likely be implemented in the future though + /** + * Function that returns true when the supplied password + * can Successfully decrypt the supplied zip archive + * not very elegant but this is the solution recommended by the maintainer for checking passwords + * a real password check will likely be implemented in the future though + */ fun checkCbzPassword(zip4j: ZipFile, password: CharArray): Boolean { try { zip4j.setPassword(password) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e085544a5..9f0461683 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -32,6 +32,7 @@ jsoup = "org.jsoup:jsoup:1.15.4" disklrucache = "com.jakewharton:disklrucache:2.0.2" unifile = "com.github.tachiyomiorg:unifile:17bec43" +compress = "org.apache.commons:commons-compress:1.23.0" junrar = "com.github.junrar:junrar:7.5.4" zip4j = "net.lingala.zip4j:zip4j:2.11.5"