diff --git a/lib/unpacker/build.gradle.kts b/lib/unpacker/build.gradle.kts new file mode 100644 index 000000000..aefbbe7dd --- /dev/null +++ b/lib/unpacker/build.gradle.kts @@ -0,0 +1,8 @@ +plugins { + `java-library` + kotlin("jvm") +} + +dependencies { + compileOnly(libs.kotlin.stdlib) +} diff --git a/lib/unpacker/src/main/java/eu/kanade/tachiyomi/lib/unpacker/SubstringExtractor.kt b/lib/unpacker/src/main/java/eu/kanade/tachiyomi/lib/unpacker/SubstringExtractor.kt new file mode 100644 index 000000000..0b3f23930 --- /dev/null +++ b/lib/unpacker/src/main/java/eu/kanade/tachiyomi/lib/unpacker/SubstringExtractor.kt @@ -0,0 +1,42 @@ +package eu.kanade.tachiyomi.lib.unpacker + +/* + * Copyright (C) The Tachiyomi Open Source Project + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +/** + * A helper class to extract substrings efficiently. + * + * Note that all methods move [startIndex] over the ending delimiter. + */ +class SubstringExtractor(private val text: String) { + private var startIndex = 0 + + fun skipOver(str: String) { + val index = text.indexOf(str, startIndex) + if (index == -1) return + startIndex = index + str.length + } + + fun substringBefore(str: String): String { + val index = text.indexOf(str, startIndex) + if (index == -1) return "" + val result = text.substring(startIndex, index) + startIndex = index + str.length + return result + } + + fun substringBetween(left: String, right: String): String { + val index = text.indexOf(left, startIndex) + if (index == -1) return "" + val leftIndex = index + left.length + val rightIndex = text.indexOf(right, leftIndex) + if (rightIndex == -1) return "" + startIndex = rightIndex + right.length + return text.substring(leftIndex, rightIndex) + } +} diff --git a/lib/unpacker/src/main/java/eu/kanade/tachiyomi/lib/unpacker/Unpacker.kt b/lib/unpacker/src/main/java/eu/kanade/tachiyomi/lib/unpacker/Unpacker.kt new file mode 100644 index 000000000..e765c0cc3 --- /dev/null +++ b/lib/unpacker/src/main/java/eu/kanade/tachiyomi/lib/unpacker/Unpacker.kt @@ -0,0 +1,84 @@ +package eu.kanade.tachiyomi.lib.unpacker + +/* + * Copyright (C) The Tachiyomi Open Source Project + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +/** + * Helper class to unpack JavaScript code compressed by [packer](http://dean.edwards.name/packer/). + * + * Source code of packer can be found [here](https://github.com/evanw/packer/blob/master/packer.js). + */ +object Unpacker { + + /** + * Unpacks JavaScript code compressed by packer. + * + * Specify [left] and [right] to unpack only the data between them. + * + * Note: single quotes `\'` in the data will be replaced with double quotes `"`. + */ + fun unpack(script: String, left: String? = null, right: String? = null): String = + unpack(SubstringExtractor(script), left, right) + + /** + * Unpacks JavaScript code compressed by packer. + * + * Specify [left] and [right] to unpack only the data between them. + * + * Note: single quotes `\'` in the data will be replaced with double quotes `"`. + */ + fun unpack(script: SubstringExtractor, left: String? = null, right: String? = null): String { + val packed = script + .substringBetween("}('", ".split('|'),0,{}))") + .replace("\\'", "\"") + + val parser = SubstringExtractor(packed) + val data: String + if (left != null && right != null) { + data = parser.substringBetween(left, right) + parser.skipOver("',") + } else { + data = parser.substringBefore("',") + } + if (data.isEmpty()) return "" + + val dictionary = parser.substringBetween("'", "'").split("|") + val size = dictionary.size + + return wordRegex.replace(data) { + val key = it.value + val index = parseRadix62(key) + if (index >= size) return@replace key + dictionary[index].ifEmpty { key } + } + } + + private val wordRegex by lazy { Regex("""\w+""") } + + private fun parseRadix62(str: String): Int { + var result = 0 + for (ch in str.toCharArray()) { + result = result * 62 + when { + ch.code <= '9'.code -> { // 0-9 + ch.code - '0'.code + } + + ch.code >= 'a'.code -> { // a-z + // ch - 'a' + 10 + ch.code - ('a'.code - 10) + } + + else -> { // A-Z + // ch - 'A' + 36 + ch.code - ('A'.code - 36) + } + } + } + return result + } +}