Add support for non-UTF-8 encoded archives (#682)

This commit is contained in:
FooIbar authored and GitHub committed 2024-02-04 22:24:32 +08:00
1 parent e9f615819f
commit ce1dc55d3c
7 files changed
+59 -92

No files matched your search

+25 -17
View File
@@ -98,6 +98,20 @@ static inline int filename_is_playable_file(const char *name) {
return false;
}
static inline const char *archive_entry_pathname2(struct archive_entry *entry) {
const char *name = archive_entry_pathname_utf8(entry);
return name ? name : archive_entry_pathname(entry);
}
static inline bool archive_entry_is_file(struct archive_entry *entry) {
return archive_entry_filetype(entry) == AE_IFREG;
}
static inline bool archive_entry_is_playable(struct archive_entry *entry) {
return archive_entry_is_file(entry) &&
filename_is_playable_file(archive_entry_pathname2(entry));
}
static inline int compare_entries(const void *a, const void *b) {
const char *fa = ((entry *) a)->filename;
const char *fb = ((entry *) b)->filename;
@@ -107,8 +121,8 @@ static inline int compare_entries(const void *a, const void *b) {
static long archive_map_entries_index(archive_ctx *ctx) {
long count = 0;
while (archive_read_next_header(ctx->arc, &ctx->entry) == ARCHIVE_OK) {
const char *name = archive_entry_pathname_utf8(ctx->entry);
if (filename_is_playable_file(name)) {
const char *name = archive_entry_pathname2(ctx->entry);
if (archive_entry_is_file(ctx->entry) && filename_is_playable_file(name)) {
entries[count].filename = strdup(name);
entries[count].index = count;
entries[count].size = archive_entry_size(ctx->entry);
@@ -166,7 +180,7 @@ static void mempool_release_pages(void *addr, size_t size) {
static long archive_list_all_entries(archive_ctx *ctx) {
long count = 0;
while (archive_read_next_header(ctx->arc, &ctx->entry) == ARCHIVE_OK)
if (filename_is_playable_file(archive_entry_pathname_utf8(ctx->entry)))
if (archive_entry_is_playable(ctx->entry))
count++;
if (!count)
LOGE("%s", archive_error_string(ctx->arc));
@@ -214,7 +228,7 @@ static int archive_alloc_ctx(archive_ctx **ctxptr) {
static int archive_skip_to_index(archive_ctx *ctx, int index) {
while (archive_read_next_header(ctx->arc, &ctx->entry) == ARCHIVE_OK) {
if (!filename_is_playable_file(archive_entry_pathname_utf8(ctx->entry)))
if (!archive_entry_is_playable(ctx->entry))
continue;
if (ctx->next_index++ == index) {
return ctx->next_index - 1;
@@ -433,7 +447,7 @@ Java_com_hippo_ehviewer_jni_ArchiveKt_providePassword(JNIEnv *env, jclass thiz,
archive_alloc_ctx(&ctx);
void *tmpBuf = alloca(4096);
while (archive_read_next_header(ctx->arc, &entry) == ARCHIVE_OK) {
if (!filename_is_playable_file(archive_entry_pathname_utf8(entry)))
if (!archive_entry_is_playable(entry))
continue;
if (!archive_entry_is_encrypted(entry))
continue;
@@ -448,22 +462,15 @@ Java_com_hippo_ehviewer_jni_ArchiveKt_providePassword(JNIEnv *env, jclass thiz,
}
JNIEXPORT jstring JNICALL
Java_com_hippo_ehviewer_jni_ArchiveKt_getFilename(JNIEnv *env, jclass thiz,
Java_com_hippo_ehviewer_jni_ArchiveKt_getExtension(JNIEnv *env, jclass thiz,
jint index) {
EH_UNUSED(env);
EH_UNUSED(thiz);
index = entries[index].index;
archive_ctx *ctx = NULL;
int ret;
ret = archive_get_ctx(&ctx, index);
if (ret)
return NULL;
jstring str = (*env)->NewStringUTF(env, archive_entry_pathname_utf8(ctx->entry));
ctx->using = 0;
return str;
const char *ext = strrchr(entries[index].filename, '.') + 1;
return (*env)->NewStringUTF(env, ext);
}
JNIEXPORT void JNICALL
JNIEXPORT jboolean JNICALL
Java_com_hippo_ehviewer_jni_ArchiveKt_extractToFd(JNIEnv *env, jclass thiz,
jint index, jint fd) {
EH_UNUSED(env);
@@ -473,9 +480,10 @@ Java_com_hippo_ehviewer_jni_ArchiveKt_extractToFd(JNIEnv *env, jclass thiz,
int ret;
ret = archive_get_ctx(&ctx, index);
if (!ret) {
archive_read_data_into_fd(ctx->arc, fd);
ret = archive_read_data_into_fd(ctx->arc, fd);
ctx->using = 0;
}
return ret == ARCHIVE_OK;
}
JNIEXPORT void JNICALL
@@ -23,7 +23,7 @@ import com.hippo.ehviewer.image.Image
import com.hippo.ehviewer.jni.closeArchive
import com.hippo.ehviewer.jni.extractToByteBuffer
import com.hippo.ehviewer.jni.extractToFd
import com.hippo.ehviewer.jni.getFilename
import com.hippo.ehviewer.jni.getExtension
import com.hippo.ehviewer.jni.needPassword
import com.hippo.ehviewer.jni.openArchive
import com.hippo.ehviewer.jni.providePassword
@@ -144,28 +144,25 @@ class ArchivePageLoader(private val file: UniFile, passwdProvider: PasswdProvide
mJobMap[index]?.cancel()
}
override fun getImageFilename(index: Int): String {
return FileUtils.getNameFromFilename(getImageFilenameWithExtension(index))!!
override val title by lazy {
FileUtils.getNameFromFilename(file.name)!!
}
override fun getImageFilenameWithExtension(index: Int): String {
return FileUtils.sanitizeFilename(getFilename(index))
override fun getImageExtension(index: Int): String {
return getExtension(index)
}
override fun save(index: Int, file: UniFile): Boolean {
runCatching {
return runCatching {
file.openFileDescriptor("w").use {
extractToFd(index, it.fd)
}
}.onFailure {
}.getOrElse {
it.printStackTrace()
return false
false
}
return true
}
override fun save(index: Int, dir: UniFile, filename: String) = (dir / filename).apply { save(index, this) }
override fun preloadPages(pages: List<Int>, pair: Pair<Int, Int>) {}
}
@@ -15,6 +15,7 @@
*/
package com.hippo.ehviewer.gallery
import com.hippo.ehviewer.client.EhUtils
import com.hippo.ehviewer.client.data.GalleryInfo
import com.hippo.ehviewer.image.Image
import com.hippo.ehviewer.spider.SpiderQueen
@@ -23,7 +24,6 @@ import com.hippo.ehviewer.spider.SpiderQueen.Companion.releaseSpiderQueen
import com.hippo.ehviewer.spider.SpiderQueen.OnSpiderListener
import com.hippo.ehviewer.util.SimpleHandler
import com.hippo.unifile.UniFile
import java.util.Locale
class EhPageLoader(private val mGalleryInfo: GalleryInfo) : PageLoader2(), OnSpiderListener {
private lateinit var mSpiderQueen: SpiderQueen
@@ -44,29 +44,18 @@ class EhPageLoader(private val mGalleryInfo: GalleryInfo) : PageLoader2(), OnSpi
override val startPage
get() = mSpiderQueen.startPage
override fun getImageFilename(index: Int): String {
return String.format(
Locale.US,
"%d-%s-%08d",
mGalleryInfo.gid,
mGalleryInfo.token,
index + 1,
)
override val title by lazy {
EhUtils.getSuitableTitle(mGalleryInfo)
}
override fun getImageFilenameWithExtension(index: Int): String {
val filename = getImageFilename(index)
return mSpiderQueen.getExtension(index)?.let { "$filename.$it" } ?: filename
override fun getImageExtension(index: Int): String {
return mSpiderQueen.getExtension(index)!!
}
override fun save(index: Int, file: UniFile): Boolean {
return mSpiderQueen.save(index, file)
}
override fun save(index: Int, dir: UniFile, filename: String): UniFile? {
return mSpiderQueen.save(index, dir, filename)
}
override fun putStartPage(page: Int) {
mSpiderQueen.putStartPage(page)
}
@@ -24,19 +24,18 @@ abstract class PageLoader2 : PageLoader() {
open fun putStartPage(page: Int) {}
/**
* @return without extension
*/
abstract fun getImageFilename(index: Int): String
protected abstract val title: String
protected abstract fun getImageExtension(index: Int): String
fun getImageFilename(index: Int): String {
return "$title - ${index + 1}.${getImageExtension(index)}"
}
/**
* @return with extension
*/
abstract fun getImageFilenameWithExtension(index: Int): String
abstract fun save(index: Int, file: UniFile): Boolean
/**
* @param filename with extension
*/
abstract fun save(index: Int, dir: UniFile, filename: String): UniFile?
fun saveToDir(index: Int, dir: UniFile): UniFile? {
val filename = getImageFilename(index)
return (dir / filename).takeIf { save(index, it) }
}
}
@@ -5,8 +5,8 @@ import java.nio.ByteBuffer
external fun releaseByteBuffer(buffer: ByteBuffer)
external fun openArchive(fd: Int, size: Long): Int
external fun extractToByteBuffer(index: Int): ByteBuffer?
external fun extractToFd(index: Int, fd: Int)
external fun getFilename(index: Int): String
external fun extractToFd(index: Int, fd: Int): Boolean
external fun getExtension(index: Int): String
external fun needPassword(): Boolean
external fun providePassword(str: String): Boolean
external fun closeArchive()
@@ -309,12 +309,6 @@ class SpiderQueen private constructor(val galleryInfo: GalleryInfo) : CoroutineS
}
}
fun save(index: Int, dir: UniFile, filename: String): UniFile? {
val state = getPageState(index)
if (STATE_FINISHED != state) return null
return (dir / filename).takeIf { mSpiderDen.saveToUniFile(index, it) }
}
fun getExtension(index: Int): String? {
val state = getPageState(index)
return if (STATE_FINISHED != state) {
@@ -384,11 +384,7 @@ class ReaderActivity : EhActivity() {
Toast.makeText(this, R.string.error_cant_create_temp_file, Toast.LENGTH_SHORT).show()
return
}
val file = mGalleryProvider!!.save(
page,
dir.asUniFile(),
mGalleryProvider!!.getImageFilenameWithExtension(page),
)
val file = mGalleryProvider!!.saveToDir(page, dir.asUniFile())
if (file == null) {
Toast.makeText(this, R.string.error_cant_save_image, Toast.LENGTH_SHORT).show()
return
@@ -399,12 +395,8 @@ class ReaderActivity : EhActivity() {
return
}
var mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
MimeTypeMap.getFileExtensionFromUrl(filename),
)
if (mimeType.isNullOrEmpty()) {
mimeType = "image/jpeg"
}
val extension = FileUtils.getExtensionFromFilename(filename)
val mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension) ?: "image/jpeg"
val uri = FileProvider.getUriForFile(
this,
@@ -442,11 +434,7 @@ class ReaderActivity : EhActivity() {
Toast.makeText(this, R.string.error_cant_create_temp_file, Toast.LENGTH_SHORT).show()
return
}
val file = mGalleryProvider!!.save(
page,
dir.asUniFile(),
mGalleryProvider!!.getImageFilenameWithExtension(page),
)
val file = mGalleryProvider!!.saveToDir(page, dir.asUniFile())
if (file == null) {
Toast.makeText(this, R.string.error_cant_save_image, Toast.LENGTH_SHORT).show()
return
@@ -474,13 +462,9 @@ class ReaderActivity : EhActivity() {
lifecycleScope.launchUI {
val granted = isAtLeastQ || requestPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
if (granted) {
val filename = mGalleryProvider!!.getImageFilenameWithExtension(page)
var mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
MimeTypeMap.getFileExtensionFromUrl(filename),
)
if (mimeType.isNullOrEmpty()) {
mimeType = "image/jpeg"
}
val filename = mGalleryProvider!!.getImageFilename(page)
val extension = FileUtils.getExtensionFromFilename(filename)
val mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension) ?: "image/jpeg"
val realPath: String
val resolver = contentResolver
@@ -547,11 +531,7 @@ class ReaderActivity : EhActivity() {
Toast.makeText(this, R.string.error_cant_create_temp_file, Toast.LENGTH_SHORT).show()
return
}
val file = mGalleryProvider!!.save(
page,
dir.asUniFile(),
mGalleryProvider!!.getImageFilenameWithExtension(page),
)
val file = mGalleryProvider!!.saveToDir(page, dir.asUniFile())
if (file == null) {
Toast.makeText(this, R.string.error_cant_save_image, Toast.LENGTH_SHORT).show()
return