Add support for non-UTF-8 encoded archives (#682)
This commit is contained in:
1 parent
e9f615819f
commit
ce1dc55d3c
7 files changed
+59
-92
No files matched your search
+25
-17
@@ -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
|
||||
|
||||
Reference in new issue
Block a user