Anchira: Add Entry Bundling (#1643)
* Anchira: Add Entry Bundling Prefixing queries with `bundle:` will create a single SManga entry with results as chapters. * Switch default bundle title to first entry's Fix url used to check for bundles Default page count of 1 * Enable filters on bundles * Strip chapter number suffix from bundle title * Convert RegEx to variable * Convert RegEx constructor to top-level val
This commit is contained in:
parent
58b5aa2f3d
commit
2bb5ef9059
@ -1,7 +1,7 @@
|
|||||||
ext {
|
ext {
|
||||||
extName = 'Anchira'
|
extName = 'Anchira'
|
||||||
extClass = '.Anchira'
|
extClass = '.Anchira'
|
||||||
extVersionCode = 9
|
extVersionCode = 10
|
||||||
isNsfw = true
|
isNsfw = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ import android.content.SharedPreferences
|
|||||||
import androidx.preference.ListPreference
|
import androidx.preference.ListPreference
|
||||||
import androidx.preference.PreferenceScreen
|
import androidx.preference.PreferenceScreen
|
||||||
import androidx.preference.SwitchPreferenceCompat
|
import androidx.preference.SwitchPreferenceCompat
|
||||||
|
import eu.kanade.tachiyomi.extension.en.anchira.AnchiraHelper.createChapter
|
||||||
import eu.kanade.tachiyomi.extension.en.anchira.AnchiraHelper.getPathFromUrl
|
import eu.kanade.tachiyomi.extension.en.anchira.AnchiraHelper.getPathFromUrl
|
||||||
import eu.kanade.tachiyomi.extension.en.anchira.AnchiraHelper.prepareTags
|
import eu.kanade.tachiyomi.extension.en.anchira.AnchiraHelper.prepareTags
|
||||||
import eu.kanade.tachiyomi.network.GET
|
import eu.kanade.tachiyomi.network.GET
|
||||||
@ -23,6 +24,7 @@ import eu.kanade.tachiyomi.source.online.HttpSource
|
|||||||
import kotlinx.serialization.decodeFromString
|
import kotlinx.serialization.decodeFromString
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import kotlinx.serialization.json.decodeFromStream
|
import kotlinx.serialization.json.decodeFromStream
|
||||||
|
import okhttp3.HttpUrl
|
||||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||||
import okhttp3.Interceptor
|
import okhttp3.Interceptor
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
@ -33,6 +35,8 @@ import uy.kohesive.injekt.Injekt
|
|||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
import kotlin.math.ceil
|
||||||
|
import kotlin.math.min
|
||||||
|
|
||||||
class Anchira : HttpSource(), ConfigurableSource {
|
class Anchira : HttpSource(), ConfigurableSource {
|
||||||
override val name = "Anchira"
|
override val name = "Anchira"
|
||||||
@ -109,6 +113,23 @@ class Anchira : HttpSource(), ConfigurableSource {
|
|||||||
fetchMangaDetails(manga).map {
|
fetchMangaDetails(manga).map {
|
||||||
MangasPage(listOf(it), false)
|
MangasPage(listOf(it), false)
|
||||||
}
|
}
|
||||||
|
} else if (query.startsWith(SLUG_BUNDLE_PREFIX)) {
|
||||||
|
// bundle entries as chapters
|
||||||
|
val url = applyFilters(
|
||||||
|
page,
|
||||||
|
query.substringAfter(SLUG_BUNDLE_PREFIX),
|
||||||
|
filters,
|
||||||
|
).removeAllQueryParameters("page")
|
||||||
|
if (
|
||||||
|
url.build().queryParameter("sort") == "4"
|
||||||
|
) {
|
||||||
|
url.removeAllQueryParameters("sort")
|
||||||
|
}
|
||||||
|
val manga = SManga.create()
|
||||||
|
.apply { this.url = "?${url.build().query}" }
|
||||||
|
fetchMangaDetails(manga).map {
|
||||||
|
MangasPage(listOf(it), false)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// regular filtering without text search
|
// regular filtering without text search
|
||||||
client.newCall(searchMangaRequest(page, query, filters))
|
client.newCall(searchMangaRequest(page, query, filters))
|
||||||
@ -116,29 +137,29 @@ class Anchira : HttpSource(), ConfigurableSource {
|
|||||||
.map(::searchMangaParse)
|
.map(::searchMangaParse)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
|
||||||
|
override fun searchMangaRequest(page: Int, query: String, filters: FilterList) =
|
||||||
|
GET(applyFilters(page, query, filters).build(), headers)
|
||||||
|
|
||||||
|
private fun applyFilters(page: Int, query: String, filters: FilterList): HttpUrl.Builder {
|
||||||
val filterList = if (filters.isEmpty()) getFilterList() else filters
|
val filterList = if (filters.isEmpty()) getFilterList() else filters
|
||||||
val trendingFilter = filterList.findInstance<TrendingFilter>()
|
val trendingFilter = filterList.findInstance<TrendingFilter>()
|
||||||
val sortTrendingFilter = filters.findInstance<SortTrendingFilter>()
|
val sortTrendingFilter = filters.findInstance<SortTrendingFilter>()
|
||||||
var url = libraryUrl.toHttpUrl().newBuilder()
|
var url = libraryUrl.toHttpUrl().newBuilder()
|
||||||
|
|
||||||
url.addQueryParameter("page", page.toString())
|
|
||||||
|
|
||||||
if (trendingFilter?.state == true) {
|
if (trendingFilter?.state == true) {
|
||||||
val interval = when (sortTrendingFilter?.state) {
|
val interval = when (sortTrendingFilter?.state) {
|
||||||
1 -> "3"
|
1 -> "3"
|
||||||
else -> ""
|
else -> ""
|
||||||
}
|
}
|
||||||
|
|
||||||
if (interval.isNotBlank()) url.addQueryParameter("interval", interval)
|
if (interval.isNotBlank()) url.setQueryParameter("interval", interval)
|
||||||
|
|
||||||
url = url.toString().replace("library", "trending").toHttpUrl()
|
url = url.toString().replace("library", "trending").toHttpUrl()
|
||||||
.newBuilder()
|
.newBuilder()
|
||||||
|
|
||||||
return GET(url.build(), headers)
|
|
||||||
} else {
|
} else {
|
||||||
if (query.isNotBlank()) {
|
if (query.isNotBlank()) {
|
||||||
url.addQueryParameter("s", query)
|
url.setQueryParameter("s", query)
|
||||||
}
|
}
|
||||||
|
|
||||||
filters.forEach { filter ->
|
filters.forEach { filter ->
|
||||||
@ -154,7 +175,7 @@ class Anchira : HttpSource(), ConfigurableSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sum > 0) url.addQueryParameter("cat", sum.toString())
|
if (sum > 0) url.setQueryParameter("cat", sum.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
is SortFilter -> {
|
is SortFilter -> {
|
||||||
@ -166,8 +187,8 @@ class Anchira : HttpSource(), ConfigurableSource {
|
|||||||
else -> ""
|
else -> ""
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sort.isNotEmpty()) url.addQueryParameter("sort", sort)
|
if (sort.isNotEmpty()) url.setQueryParameter("sort", sort)
|
||||||
if (filter.state?.ascending == true) url.addQueryParameter("order", "1")
|
if (filter.state?.ascending == true) url.setQueryParameter("order", "1")
|
||||||
}
|
}
|
||||||
|
|
||||||
is FavoritesFilter -> {
|
is FavoritesFilter -> {
|
||||||
@ -184,57 +205,103 @@ class Anchira : HttpSource(), ConfigurableSource {
|
|||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return GET(url.build(), headers)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (page > 1) {
|
||||||
|
url.setQueryParameter("page", page.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
return url
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun searchMangaParse(response: Response) = latestUpdatesParse(response)
|
override fun searchMangaParse(response: Response) = latestUpdatesParse(response)
|
||||||
|
|
||||||
// Details
|
// Details
|
||||||
|
|
||||||
override fun mangaDetailsRequest(manga: SManga) =
|
override fun mangaDetailsRequest(manga: SManga): Request {
|
||||||
GET("$libraryUrl/${getPathFromUrl(manga.url)}", headers)
|
return if (manga.url.startsWith("?")) {
|
||||||
|
GET(libraryUrl + manga.url, headers)
|
||||||
override fun mangaDetailsParse(response: Response): SManga {
|
} else {
|
||||||
val data = json.decodeFromString<Entry>(response.body.string())
|
GET("$libraryUrl/${getPathFromUrl(manga.url)}", headers)
|
||||||
|
|
||||||
return SManga.create().apply {
|
|
||||||
url = "/g/${data.id}/${data.key}"
|
|
||||||
title = data.title
|
|
||||||
thumbnail_url =
|
|
||||||
"$cdnUrl/${data.id}/${data.key}/b/${data.thumbnailIndex + 1}"
|
|
||||||
artist = data.tags.filter { it.namespace == 1 }.joinToString(", ") { it.name }
|
|
||||||
author = data.tags.filter { it.namespace == 2 }.joinToString(", ") { it.name }
|
|
||||||
genre = prepareTags(data.tags, preferences.useTagGrouping)
|
|
||||||
update_strategy = UpdateStrategy.ONLY_FETCH_ONCE
|
|
||||||
status = SManga.COMPLETED
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getMangaUrl(manga: SManga) = if (preferences.openSource) {
|
override fun mangaDetailsParse(response: Response): SManga {
|
||||||
val id = manga.url.split("/").reversed()[1].toInt()
|
return if (response.request.url.pathSegments.count() == libraryUrl.toHttpUrl().pathSegments.count()) {
|
||||||
anchiraData.find { it.id == id }?.url ?: "$baseUrl${manga.url}"
|
val manga = latestUpdatesParse(response).mangas.first()
|
||||||
} else {
|
val query = response.request.url.queryParameter("s")
|
||||||
"$baseUrl${manga.url}"
|
val cleanTitle = CHAPTER_SUFFIX_RE.replace(manga.title, "").trim()
|
||||||
|
manga.apply {
|
||||||
|
url = "?${response.request.url.query}"
|
||||||
|
description = "Bundled from $query"
|
||||||
|
title = "[Bundle] $cleanTitle"
|
||||||
|
update_strategy = UpdateStrategy.ALWAYS_UPDATE
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val data = json.decodeFromString<Entry>(response.body.string())
|
||||||
|
|
||||||
|
SManga.create().apply {
|
||||||
|
url = "/g/${data.id}/${data.key}"
|
||||||
|
title = data.title
|
||||||
|
thumbnail_url =
|
||||||
|
"$cdnUrl/${data.id}/${data.key}/b/${data.thumbnailIndex + 1}"
|
||||||
|
artist = data.tags.filter { it.namespace == 1 }.joinToString(", ") { it.name }
|
||||||
|
author = data.tags.filter { it.namespace == 2 }.joinToString(", ") { it.name }
|
||||||
|
genre = prepareTags(data.tags, preferences.useTagGrouping)
|
||||||
|
update_strategy = UpdateStrategy.ONLY_FETCH_ONCE
|
||||||
|
status = SManga.COMPLETED
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getMangaUrl(manga: SManga) =
|
||||||
|
if (preferences.openSource && !manga.url.startsWith("?")) {
|
||||||
|
val id = manga.url.split("/").reversed()[1].toInt()
|
||||||
|
anchiraData.find { it.id == id }?.url ?: "$baseUrl${manga.url}"
|
||||||
|
} else {
|
||||||
|
"$baseUrl${manga.url}"
|
||||||
|
}
|
||||||
|
|
||||||
// Chapter
|
// Chapter
|
||||||
|
|
||||||
override fun chapterListRequest(manga: SManga) =
|
override fun chapterListRequest(manga: SManga): Request {
|
||||||
GET("$libraryUrl/${getPathFromUrl(manga.url)}", headers)
|
return if (manga.url.startsWith("?")) {
|
||||||
|
GET(libraryUrl + manga.url, headers)
|
||||||
|
} else {
|
||||||
|
GET("$libraryUrl/${getPathFromUrl(manga.url)}", headers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun chapterListParse(response: Response): List<SChapter> {
|
override fun chapterListParse(response: Response): List<SChapter> {
|
||||||
val data = json.decodeFromString<Entry>(response.body.string())
|
val chapterList = mutableListOf<SChapter>()
|
||||||
|
if (response.request.url.pathSegments.count() == libraryUrl.toHttpUrl().pathSegments.count()) {
|
||||||
return listOf(
|
var results = json.decodeFromString<LibraryResponse>(response.body.string())
|
||||||
SChapter.create().apply {
|
val pages = min(5, ceil((results.total.toFloat() / results.limit)).toInt())
|
||||||
url = "/g/${data.id}/${data.key}"
|
for (page in 1..pages) {
|
||||||
name = "Chapter"
|
results.entries.forEach { data ->
|
||||||
date_upload = data.publishedAt * 1000
|
chapterList.add(
|
||||||
chapter_number = 1f
|
createChapter(data, response, anchiraData),
|
||||||
},
|
)
|
||||||
)
|
}
|
||||||
|
if (page < pages) {
|
||||||
|
results = json.decodeFromString<LibraryResponse>(
|
||||||
|
client.newCall(
|
||||||
|
GET(
|
||||||
|
response.request.url.newBuilder()
|
||||||
|
.setQueryParameter("page", (page + 1).toString()).build(),
|
||||||
|
headers,
|
||||||
|
),
|
||||||
|
).execute().body.string(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val data = json.decodeFromString<Entry>(response.body.string())
|
||||||
|
chapterList.add(
|
||||||
|
createChapter(data, response, anchiraData),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return chapterList
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getChapterUrl(chapter: SChapter) = "$baseUrl/g/${getPathFromUrl(chapter.url)}"
|
override fun getChapterUrl(chapter: SChapter) = "$baseUrl/g/${getPathFromUrl(chapter.url)}"
|
||||||
@ -295,14 +362,16 @@ class Anchira : HttpSource(), ConfigurableSource {
|
|||||||
val openSourcePref = SwitchPreferenceCompat(screen.context).apply {
|
val openSourcePref = SwitchPreferenceCompat(screen.context).apply {
|
||||||
key = OPEN_SOURCE_PREF
|
key = OPEN_SOURCE_PREF
|
||||||
title = "Open source website in WebView"
|
title = "Open source website in WebView"
|
||||||
summary = "Enable to open the original source website of the gallery (if available) instead of Anchira."
|
summary =
|
||||||
|
"Enable to open the original source website of the gallery (if available) instead of Anchira."
|
||||||
setDefaultValue(false)
|
setDefaultValue(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
val useTagGrouping = SwitchPreferenceCompat(screen.context).apply {
|
val useTagGrouping = SwitchPreferenceCompat(screen.context).apply {
|
||||||
key = USE_TAG_GROUPING
|
key = USE_TAG_GROUPING
|
||||||
title = "Group tags"
|
title = "Group tags"
|
||||||
summary = "Enable to group tags together by artist, circle, parody, magazine and general tags"
|
summary =
|
||||||
|
"Enable to group tags together by artist, circle, parody, magazine and general tags"
|
||||||
setDefaultValue(false)
|
setDefaultValue(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -399,6 +468,7 @@ class Anchira : HttpSource(), ConfigurableSource {
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val SLUG_SEARCH_PREFIX = "id:"
|
const val SLUG_SEARCH_PREFIX = "id:"
|
||||||
|
const val SLUG_BUNDLE_PREFIX = "bundle:"
|
||||||
private const val IMAGE_QUALITY_PREF = "image_quality"
|
private const val IMAGE_QUALITY_PREF = "image_quality"
|
||||||
private const val OPEN_SOURCE_PREF = "use_manga_source"
|
private const val OPEN_SOURCE_PREF = "use_manga_source"
|
||||||
private const val USE_TAG_GROUPING = "use_tag_grouping"
|
private const val USE_TAG_GROUPING = "use_tag_grouping"
|
||||||
@ -406,3 +476,5 @@ class Anchira : HttpSource(), ConfigurableSource {
|
|||||||
"https://gist.githubusercontent.com/LetrixZ/2b559cc5829d1c221c701e02ecd81411/raw/data-v5.json"
|
"https://gist.githubusercontent.com/LetrixZ/2b559cc5829d1c221c701e02ecd81411/raw/data-v5.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val CHAPTER_SUFFIX_RE = Regex("(?<!20\\d\\d-)\\b[\\d.]{1,4}$")
|
||||||
|
@ -3,15 +3,6 @@ package eu.kanade.tachiyomi.extension.en.anchira
|
|||||||
import kotlinx.serialization.SerialName
|
import kotlinx.serialization.SerialName
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class ListEntry(
|
|
||||||
val id: Int,
|
|
||||||
val key: String,
|
|
||||||
val title: String,
|
|
||||||
@SerialName("thumb_index") val thumbnailIndex: Int,
|
|
||||||
val tags: List<Tag> = emptyList(),
|
|
||||||
)
|
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class Tag(
|
data class Tag(
|
||||||
var name: String,
|
var name: String,
|
||||||
@ -20,7 +11,7 @@ data class Tag(
|
|||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class LibraryResponse(
|
data class LibraryResponse(
|
||||||
val entries: List<ListEntry> = emptyList(),
|
val entries: List<Entry> = emptyList(),
|
||||||
val total: Int,
|
val total: Int,
|
||||||
val page: Int,
|
val page: Int,
|
||||||
val limit: Int,
|
val limit: Int,
|
||||||
@ -30,11 +21,12 @@ data class LibraryResponse(
|
|||||||
data class Entry(
|
data class Entry(
|
||||||
val id: Int,
|
val id: Int,
|
||||||
val key: String,
|
val key: String,
|
||||||
@SerialName("published_at") val publishedAt: Long,
|
@SerialName("published_at") val publishedAt: Long = 0L,
|
||||||
val title: String,
|
val title: String,
|
||||||
@SerialName("thumb_index") val thumbnailIndex: Int,
|
@SerialName("thumb_index") val thumbnailIndex: Int = 1,
|
||||||
val tags: List<Tag> = emptyList(),
|
val tags: List<Tag> = emptyList(),
|
||||||
val url: String? = null,
|
val url: String? = null,
|
||||||
|
val pages: Int = 1,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
package eu.kanade.tachiyomi.extension.en.anchira
|
package eu.kanade.tachiyomi.extension.en.anchira
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.source.model.SChapter
|
||||||
|
import okhttp3.Response
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
object AnchiraHelper {
|
object AnchiraHelper {
|
||||||
fun getPathFromUrl(url: String) = "${url.split("/").reversed()[1]}/${url.split("/").last()}"
|
fun getPathFromUrl(url: String) = "${url.split("/").reversed()[1]}/${url.split("/").last()}"
|
||||||
|
|
||||||
@ -25,4 +29,31 @@ object AnchiraHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.joinToString(", ") { it }
|
.joinToString(", ") { it }
|
||||||
|
|
||||||
|
fun createChapter(entry: Entry, response: Response, anchiraData: List<EntryKey>) =
|
||||||
|
SChapter.create().apply {
|
||||||
|
val ch =
|
||||||
|
CHAPTER_SUFFIX_RE.find(entry.title)?.value?.trim('.') ?: "1"
|
||||||
|
val source = anchiraData.find { it.id == entry.id }?.url
|
||||||
|
?: response.request.url.toString()
|
||||||
|
url = "/g/${entry.id}/${entry.key}"
|
||||||
|
name = "$ch. ${entry.title.removeSuffix(" $ch")}"
|
||||||
|
date_upload = entry.publishedAt * 1000
|
||||||
|
chapter_number = ch.toFloat()
|
||||||
|
scanlator = buildString {
|
||||||
|
append(
|
||||||
|
Regex("fakku|irodori|anchira").find(source)?.value.orEmpty()
|
||||||
|
.replaceFirstChar {
|
||||||
|
if (it.isLowerCase()) {
|
||||||
|
it.titlecase(
|
||||||
|
Locale.getDefault(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
it.toString()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
append(" - ${entry.pages} pages")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,107 +0,0 @@
|
|||||||
package eu.kanade.tachiyomi.extension.en.anchira
|
|
||||||
|
|
||||||
object XXTEA {
|
|
||||||
|
|
||||||
private const val DELTA = -0x61c88647
|
|
||||||
|
|
||||||
@Suppress("NOTHING_TO_INLINE", "FunctionName")
|
|
||||||
private inline fun MX(sum: Int, y: Int, z: Int, p: Int, e: Int, k: IntArray): Int {
|
|
||||||
return (z.ushr(5) xor (y shl 2)) + (y.ushr(3) xor (z shl 4)) xor (sum xor y) + (k[p and 3 xor e] xor z)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun decrypt(data: ByteArray, key: ByteArray): ByteArray =
|
|
||||||
data.takeIf { it.isNotEmpty() }
|
|
||||||
?.let {
|
|
||||||
decrypt(data.toIntArray(false), key.fixKey().toIntArray(false))
|
|
||||||
.toByteArray(true)
|
|
||||||
} ?: data
|
|
||||||
|
|
||||||
fun decrypt(data: ByteArray, key: String): ByteArray? =
|
|
||||||
kotlin.runCatching { decrypt(data, key.toByteArray(Charsets.UTF_8)) }.getOrNull()
|
|
||||||
|
|
||||||
fun decryptToString(data: ByteArray, key: String): String? =
|
|
||||||
kotlin.runCatching { decrypt(data, key)?.toString(Charsets.UTF_8) }.getOrNull()
|
|
||||||
|
|
||||||
private fun decrypt(v: IntArray, k: IntArray): IntArray {
|
|
||||||
val n = v.size - 1
|
|
||||||
|
|
||||||
if (n < 1) {
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
var p: Int
|
|
||||||
val q = 6 + 52 / (n + 1)
|
|
||||||
var z: Int
|
|
||||||
var y = v[0]
|
|
||||||
var sum = q * DELTA
|
|
||||||
var e: Int
|
|
||||||
|
|
||||||
while (sum != 0) {
|
|
||||||
e = sum.ushr(2) and 3
|
|
||||||
p = n
|
|
||||||
while (p > 0) {
|
|
||||||
z = v[p - 1]
|
|
||||||
v[p] -= MX(sum, y, z, p, e, k)
|
|
||||||
y = v[p]
|
|
||||||
p--
|
|
||||||
}
|
|
||||||
z = v[n]
|
|
||||||
v[0] -= MX(sum, y, z, p, e, k)
|
|
||||||
y = v[0]
|
|
||||||
sum -= DELTA
|
|
||||||
}
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun ByteArray.fixKey(): ByteArray {
|
|
||||||
if (size == 16) return this
|
|
||||||
val fixedKey = ByteArray(16)
|
|
||||||
|
|
||||||
if (size < 16) {
|
|
||||||
copyInto(fixedKey)
|
|
||||||
} else {
|
|
||||||
copyInto(fixedKey, endIndex = 16)
|
|
||||||
}
|
|
||||||
return fixedKey
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun ByteArray.toIntArray(includeLength: Boolean): IntArray {
|
|
||||||
var n = if (size and 3 == 0) {
|
|
||||||
size.ushr(2)
|
|
||||||
} else {
|
|
||||||
size.ushr(2) + 1
|
|
||||||
}
|
|
||||||
val result: IntArray
|
|
||||||
|
|
||||||
if (includeLength) {
|
|
||||||
result = IntArray(n + 1)
|
|
||||||
result[n] = size
|
|
||||||
} else {
|
|
||||||
result = IntArray(n)
|
|
||||||
}
|
|
||||||
n = size
|
|
||||||
for (i in 0 until n) {
|
|
||||||
result[i.ushr(2)] =
|
|
||||||
result[i.ushr(2)] or (0x000000ff and this[i].toInt() shl (i and 3 shl 3))
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun IntArray.toByteArray(includeLength: Boolean): ByteArray? {
|
|
||||||
var n = size shl 2
|
|
||||||
|
|
||||||
if (includeLength) {
|
|
||||||
val m = this[size - 1]
|
|
||||||
n -= 4
|
|
||||||
if (m < n - 3 || m > n) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
n = m
|
|
||||||
}
|
|
||||||
val result = ByteArray(n)
|
|
||||||
|
|
||||||
for (i in 0 until n) {
|
|
||||||
result[i] = this[i.ushr(2)].ushr(i and 3 shl 3).toByte()
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user