mirror of
https://github.com/HighCapable/YukiHookAPI.git
synced 2025-09-04 09:45:19 +08:00
Remove parse folder because is no longer need it
This commit is contained in:
@@ -1,115 +0,0 @@
|
||||
/*
|
||||
* YukiHookAPI - An efficient Kotlin version of the Xposed Hook API.
|
||||
* Copyright (C) 2019-2022 HighCapable
|
||||
* https://github.com/fankes/YukiHookAPI
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
* This file is Created by fankes on 2022/5/24.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
|
||||
package com.highcapable.yukihookapi.hook.core.reflex.parse
|
||||
|
||||
import android.content.pm.ApplicationInfo
|
||||
import com.highcapable.yukihookapi.hook.utils.parallelForEach
|
||||
import java.io.Closeable
|
||||
import java.io.File
|
||||
import java.nio.ByteBuffer
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipFile
|
||||
|
||||
/**
|
||||
* 封装对 APK 文件的解析操作
|
||||
*
|
||||
* 参考了 dongliu 的 [apk-parser](https://github.com/hsiafan/apk-parser) 项目
|
||||
*
|
||||
* Contributed from [conan](https://github.com/meowool-catnip/conan)
|
||||
* @param apkFile APK 文件
|
||||
*/
|
||||
internal class ApkFile private constructor(apkFile: File) : Closeable {
|
||||
|
||||
internal companion object {
|
||||
|
||||
private const val DEX_FILE = "classes.dex"
|
||||
private const val DEX_ADDITIONAL = "classes%d.dex"
|
||||
|
||||
/**
|
||||
* 获取 [ApkFile] 实例
|
||||
* @param appInfo 当前 APP 的 [ApplicationInfo]
|
||||
* @return [ApkFile] or null
|
||||
*/
|
||||
internal fun from(appInfo: ApplicationInfo?) = runCatching { ApkFile(File(appInfo!!.sourceDir)) }.getOrNull()
|
||||
}
|
||||
|
||||
override fun close() = zipFile.close()
|
||||
|
||||
/**
|
||||
* 当前 APK 文件
|
||||
* @return [ZipFile]
|
||||
*/
|
||||
private val zipFile = ZipFile(apkFile)
|
||||
|
||||
/**
|
||||
* 读取 Entry
|
||||
* @param entry 压缩文件 Entry
|
||||
* @return [ByteArray]
|
||||
*/
|
||||
private fun readEntry(entry: ZipEntry) = zipFile.getInputStream(entry).use { it.readBytes() }
|
||||
|
||||
/**
|
||||
* 读取 DEX 文件路径
|
||||
* @param idx 字节序号
|
||||
* @return [String]
|
||||
*/
|
||||
private fun readDexFilePath(idx: Int) = if (idx == 1) DEX_FILE else String.format(DEX_ADDITIONAL, idx)
|
||||
|
||||
/**
|
||||
* DEX 文件是否存在
|
||||
* @param idx 字节序号
|
||||
* @return [Boolean]
|
||||
*/
|
||||
private fun isDexFileExist(idx: Int) = zipFile.getEntry(readDexFilePath(idx)) != null
|
||||
|
||||
/**
|
||||
* 读取 DEX 文件字节流
|
||||
* @param idx 字节序号
|
||||
* @return [ByteArray]
|
||||
*/
|
||||
private fun readDexFile(idx: Int) = readEntry(zipFile.getEntry(readDexFilePath(idx)))
|
||||
|
||||
/**
|
||||
* 获取当前 DEX 的 package 结构实例
|
||||
* @return [ClassTrie]
|
||||
*/
|
||||
internal val classTypes by lazy {
|
||||
var end = 2
|
||||
while (isDexFileExist(end)) end++
|
||||
val ret = ClassTrie()
|
||||
(1 until end).parallelForEach { idx ->
|
||||
val data = readDexFile(idx)
|
||||
val buffer = ByteBuffer.wrap(data)
|
||||
val parser = DexParser(buffer)
|
||||
ret += parser.parseClassTypes()
|
||||
}
|
||||
return@lazy ret.apply { mutable = false }
|
||||
}
|
||||
}
|
@@ -1,169 +0,0 @@
|
||||
/*
|
||||
* YukiHookAPI - An efficient Kotlin version of the Xposed Hook API.
|
||||
* Copyright (C) 2019-2022 HighCapable
|
||||
* https://github.com/fankes/YukiHookAPI
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
* This file is Created by fankes on 2022/5/24.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
|
||||
package com.highcapable.yukihookapi.hook.core.reflex.parse
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
/**
|
||||
* 用来储存一个 APK 的 package 结构
|
||||
*
|
||||
* 出于性能考虑 - 这个类不支持读线程和写线程同时操作 - 但支持同类型的线程同时操作
|
||||
*
|
||||
* Contributed from [conan](https://github.com/meowool-catnip/conan)
|
||||
*/
|
||||
internal class ClassTrie internal constructor() {
|
||||
|
||||
private companion object {
|
||||
|
||||
/**
|
||||
* 用来将 JVM 格式的类型标识符转换为类名
|
||||
*
|
||||
* Example: String 的类型标识符为 "Ljava/lang/String;"
|
||||
*
|
||||
* [Refer](https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.3)
|
||||
* @return [String]
|
||||
*/
|
||||
private fun convertJVMTypeToClassName(type: String) = type.substring(1, type.length - 1).replace(oldChar = '/', newChar = '.')
|
||||
}
|
||||
|
||||
/**
|
||||
* 读写开关 - 用于增强线程间的安全性
|
||||
*
|
||||
* 只有开关设为 true 的时候 - 写操作才会被执行
|
||||
*
|
||||
* 只有开关设为 false 的时候 - 读操作才会返回有效数据
|
||||
*/
|
||||
@Volatile
|
||||
internal var mutable = true
|
||||
|
||||
/**
|
||||
* package 结构的根结点
|
||||
* @return [TrieNode]
|
||||
*/
|
||||
private val root = TrieNode()
|
||||
|
||||
/**
|
||||
* 插入一个单独的 JVM 格式的类型标识符
|
||||
* @param type 类型
|
||||
*/
|
||||
internal operator fun plusAssign(type: String) {
|
||||
if (mutable) root.add(convertJVMTypeToClassName(type))
|
||||
}
|
||||
|
||||
/**
|
||||
* 插入一组 JVM 格式的类型标识符
|
||||
* @param types 类型数组
|
||||
*/
|
||||
internal operator fun plusAssign(types: Array<String>) = types.forEach { this += it }
|
||||
|
||||
/**
|
||||
* 查找指定包里指定深度的所有类
|
||||
*
|
||||
* 出于性能方面的考虑 - 只有深度相等的类才会被返回 - 比如查找深度为 0 的时候 - 就只返回这个包自己拥有的类 - 不包括它里面其他包拥有的类
|
||||
* @param packageName 包名 - 默认为根包名
|
||||
* @param depth 深度
|
||||
* @return [List] 查找到的类名数组
|
||||
*/
|
||||
internal fun search(packageName: String = "root", depth: Int): List<String> {
|
||||
if (mutable) return emptyList()
|
||||
if (packageName == "root") return root.classes
|
||||
return root.search(packageName, depth)
|
||||
}
|
||||
|
||||
/**
|
||||
* 私有的节点结构
|
||||
*/
|
||||
inner class TrieNode {
|
||||
|
||||
/** 当前的 Class 实例数组 */
|
||||
internal val classes: MutableList<String> = ArrayList(50)
|
||||
|
||||
/** 当前的 Class 子实例数组 */
|
||||
private val children: MutableMap<String, TrieNode> = ConcurrentHashMap()
|
||||
|
||||
/**
|
||||
* 添加节点
|
||||
* @param className 类名
|
||||
*/
|
||||
internal fun add(className: String) = add(className, pos = 0)
|
||||
|
||||
/**
|
||||
* 获取节点下的类名数组
|
||||
* @param depth 深度
|
||||
* @return [List]
|
||||
*/
|
||||
internal fun get(depth: Int = 0): List<String> {
|
||||
if (depth == 0) return classes
|
||||
return children.flatMap { it.value.get(depth - 1) }
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找当前包里指定深度的所有类
|
||||
* @param packageName 包名 - 默认为根包名
|
||||
* @param depth 深度
|
||||
* @return [List] 查找到的类名数组
|
||||
*/
|
||||
internal fun search(packageName: String, depth: Int) = search(packageName, depth, pos = 0)
|
||||
|
||||
/**
|
||||
* 添加节点
|
||||
* @param className 类名
|
||||
* @param pos 下标
|
||||
*/
|
||||
private fun add(className: String, pos: Int) {
|
||||
val delimiterAt = className.indexOf(char = '.', pos)
|
||||
if (delimiterAt == -1) {
|
||||
synchronized(this) { classes.add(className) }
|
||||
return
|
||||
}
|
||||
val pkg = className.substring(pos, delimiterAt)
|
||||
if (pkg !in children) children[pkg] = TrieNode()
|
||||
children[pkg]?.add(className, pos = delimiterAt + 1)
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找当前包里指定深度的所有类
|
||||
* @param packageName 包名 - 默认为根包名
|
||||
* @param depth 深度
|
||||
* @param pos 下标
|
||||
* @return [List] 查找到的类名数组
|
||||
*/
|
||||
private fun search(packageName: String, depth: Int, pos: Int): List<String> {
|
||||
val delimiterAt = packageName.indexOf(char = '.', pos)
|
||||
if (delimiterAt == -1) {
|
||||
val pkg = packageName.substring(pos)
|
||||
return children[pkg]?.get(depth) ?: emptyList()
|
||||
}
|
||||
val pkg = packageName.substring(pos, delimiterAt)
|
||||
val next = children[pkg] ?: return emptyList()
|
||||
return next.search(packageName, depth, pos = delimiterAt + 1)
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,70 +0,0 @@
|
||||
/*
|
||||
* YukiHookAPI - An efficient Kotlin version of the Xposed Hook API.
|
||||
* Copyright (C) 2019-2022 HighCapable
|
||||
* https://github.com/fankes/YukiHookAPI
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
* This file is Created by fankes on 2022/5/24.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
|
||||
package com.highcapable.yukihookapi.hook.core.reflex.parse
|
||||
|
||||
/**
|
||||
* DEX 格式的文件头
|
||||
*
|
||||
* 参考来源 [Dex Format](https://source.android.com/devices/tech/dalvik/dex-format)
|
||||
*
|
||||
* Contributed from [conan](https://github.com/meowool-catnip/conan)
|
||||
*/
|
||||
internal class DexHeader internal constructor() {
|
||||
|
||||
internal var version = 0
|
||||
internal var checksum = 0u
|
||||
internal var signature = ByteArray(kSHA1DigestLen)
|
||||
internal var fileSize = 0u
|
||||
internal var headerSize = 0u
|
||||
internal var endianTag = 0u
|
||||
internal var linkSize = 0u
|
||||
internal var linkOff = 0u
|
||||
internal var mapOff = 0u
|
||||
internal var stringIdsSize = 0
|
||||
internal var stringIdsOff = 0u
|
||||
internal var typeIdsSize = 0
|
||||
internal var typeIdsOff = 0u
|
||||
internal var protoIdsSize = 0
|
||||
internal var protoIdsOff = 0u
|
||||
internal var fieldIdsSize = 0
|
||||
internal var fieldIdsOff = 0u
|
||||
internal var methodIdsSize = 0
|
||||
internal var methodIdsOff = 0u
|
||||
internal var classDefsSize = 0
|
||||
internal var classDefsOff = 0u
|
||||
internal var dataSize = 0
|
||||
internal var dataOff = 0u
|
||||
|
||||
internal companion object {
|
||||
|
||||
internal const val kSHA1DigestLen = 20
|
||||
internal const val kSHA1DigestOutputLen = kSHA1DigestLen * 2 + 1
|
||||
}
|
||||
}
|
@@ -1,226 +0,0 @@
|
||||
/*
|
||||
* YukiHookAPI - An efficient Kotlin version of the Xposed Hook API.
|
||||
* Copyright (C) 2019-2022 HighCapable
|
||||
* https://github.com/fankes/YukiHookAPI
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
* This file is Created by fankes on 2022/5/24.
|
||||
*/
|
||||
@file:Suppress("unused")
|
||||
|
||||
package com.highcapable.yukihookapi.hook.core.reflex.parse
|
||||
|
||||
import java.io.UTFDataFormatException
|
||||
import java.nio.Buffer
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.ByteOrder
|
||||
|
||||
/**
|
||||
* 封装对 DEX 格式数据的解析操作
|
||||
*
|
||||
* 参考了 dongliu 的 [apk-parser](https://github.com/hsiafan/apk-parser) 项目
|
||||
*
|
||||
* Contributed from [conan](https://github.com/meowool-catnip/conan)
|
||||
* @param buffer DEX 字节流
|
||||
*/
|
||||
internal class DexParser internal constructor(buffer: ByteBuffer) {
|
||||
|
||||
/**
|
||||
* 当前字节流
|
||||
* @return [ByteBuffer]
|
||||
*/
|
||||
private val buffer = buffer.duplicate().apply { order(ByteOrder.LITTLE_ENDIAN) }
|
||||
|
||||
/**
|
||||
* 读取 Bytes
|
||||
* @param size 大小
|
||||
* @return [ByteArray]
|
||||
*/
|
||||
private fun ByteBuffer.readBytes(size: Int) = ByteArray(size).also { get(it) }
|
||||
|
||||
/**
|
||||
* 获取当前 package 下全部的类名
|
||||
* @return [Array]
|
||||
*/
|
||||
internal fun parseClassTypes(): Array<String> {
|
||||
// read magic
|
||||
val magic = String(buffer.readBytes(8))
|
||||
if (magic.startsWith(prefix = "dex\n").not()) return arrayOf()
|
||||
val version = magic.substring(4, 7).toInt()
|
||||
// now the version is 035
|
||||
if (version < 35) {
|
||||
// version 009 was used for the M3 releases of the Android platform (November–December 2007),
|
||||
// and version 013 was used for the M5 releases of the Android platform (February–March 2008)
|
||||
error("Dex file version: $version is not supported")
|
||||
}
|
||||
// read header
|
||||
val header = readDexHeader()
|
||||
header.version = version
|
||||
// read string offsets
|
||||
val stringOffsets = readStringOffsets(header.stringIdsOff, header.stringIdsSize)
|
||||
// read type ids
|
||||
val typeIds = readTypeIds(header.typeIdsOff, header.typeIdsSize)
|
||||
// read class ids
|
||||
val classIds = readClassIds(header.classDefsOff, header.classDefsSize)
|
||||
// read class types
|
||||
return Array(classIds.size) { i ->
|
||||
val classId = classIds[i]
|
||||
val typeId = typeIds[classId]
|
||||
val offset = stringOffsets[typeId]
|
||||
readStringAtOffset(offset)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 DEX 文件头
|
||||
* @return [DexHeader]
|
||||
*/
|
||||
private fun readDexHeader() = DexHeader().apply {
|
||||
checksum = buffer.int.toUInt()
|
||||
buffer.get(signature)
|
||||
fileSize = buffer.int.toUInt()
|
||||
headerSize = buffer.int.toUInt()
|
||||
endianTag = buffer.int.toUInt()
|
||||
linkSize = buffer.int.toUInt()
|
||||
linkOff = buffer.int.toUInt()
|
||||
mapOff = buffer.int.toUInt()
|
||||
stringIdsSize = buffer.int
|
||||
stringIdsOff = buffer.int.toUInt()
|
||||
typeIdsSize = buffer.int
|
||||
typeIdsOff = buffer.int.toUInt()
|
||||
protoIdsSize = buffer.int
|
||||
protoIdsOff = buffer.int.toUInt()
|
||||
fieldIdsSize = buffer.int
|
||||
fieldIdsOff = buffer.int.toUInt()
|
||||
methodIdsSize = buffer.int
|
||||
methodIdsOff = buffer.int.toUInt()
|
||||
classDefsSize = buffer.int
|
||||
classDefsOff = buffer.int.toUInt()
|
||||
dataSize = buffer.int
|
||||
dataOff = buffer.int.toUInt()
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取字符串偏移量
|
||||
* @param stringIdsOff 偏移量
|
||||
* @param stringIdsSize 偏移大小
|
||||
* @return [IntArray]
|
||||
*/
|
||||
private fun readStringOffsets(stringIdsOff: UInt, stringIdsSize: Int): IntArray {
|
||||
(buffer as Buffer).position(stringIdsOff.toInt())
|
||||
return IntArray(stringIdsSize) { buffer.int }
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取 Ids 类型偏移量
|
||||
* @param typeIdsOff 偏移量
|
||||
* @param typeIdsSize 偏移大小
|
||||
* @return [IntArray]
|
||||
*/
|
||||
private fun readTypeIds(typeIdsOff: UInt, typeIdsSize: Int): IntArray {
|
||||
(buffer as Buffer).position(typeIdsOff.toInt())
|
||||
return IntArray(typeIdsSize) { buffer.int }
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取 Class Ids 偏移量
|
||||
* @param classDefsOff 偏移量
|
||||
* @param classDefsSize 偏移大小
|
||||
* @return [Array]
|
||||
*/
|
||||
private fun readClassIds(classDefsOff: UInt, classDefsSize: Int): Array<Int> {
|
||||
(buffer as Buffer).position(classDefsOff.toInt())
|
||||
return Array(classDefsSize) {
|
||||
val classIdx = buffer.int
|
||||
// access_flags, skip
|
||||
buffer.int
|
||||
// superclass_idx, skip
|
||||
buffer.int
|
||||
// interfaces_off, skip
|
||||
buffer.int
|
||||
// source_file_idx, skip
|
||||
buffer.int
|
||||
// annotations_off, skip
|
||||
buffer.int
|
||||
// class_data_off, skip
|
||||
buffer.int
|
||||
// static_values_off, skip
|
||||
buffer.int
|
||||
classIdx
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取偏移量的字符串
|
||||
* @param offset 偏移量
|
||||
* @return [String]
|
||||
*/
|
||||
private fun readStringAtOffset(offset: Int): String {
|
||||
(buffer as Buffer).position(offset)
|
||||
return readString(readULEB128Int())
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取 ULEB128 整型
|
||||
* @return [Int]
|
||||
*/
|
||||
private fun readULEB128Int(): Int {
|
||||
var ret = 0
|
||||
var count = 0
|
||||
var byte: Int
|
||||
do {
|
||||
if (count > 4) error("read varints error.")
|
||||
byte = buffer.get().toInt()
|
||||
ret = ret or (byte and 0x7f shl count * 7)
|
||||
count++
|
||||
} while (byte and 0x80 != 0)
|
||||
return ret
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取字符串
|
||||
* @param len 长度
|
||||
* @return [String]
|
||||
*/
|
||||
private fun readString(len: Int): String {
|
||||
val chars = CharArray(len)
|
||||
for (i in 0 until len) {
|
||||
val byte = buffer.get().toInt()
|
||||
when {
|
||||
// ascii char
|
||||
byte and 0x80 == 0 -> chars[i] = byte.toChar()
|
||||
// read one more
|
||||
byte and 0xe0 == 0xc0 -> {
|
||||
val b = buffer.get().toInt()
|
||||
chars[i] = (byte and 0x1F shl 6 or (b and 0x3F)).toChar()
|
||||
}
|
||||
byte and 0xf0 == 0xe0 -> {
|
||||
val b = buffer.get().toInt()
|
||||
val c = buffer.get().toInt()
|
||||
chars[i] = (byte and 0x0F shl 12 or (b and 0x3F shl 6) or (c and 0x3F)).toChar()
|
||||
}
|
||||
else -> throw UTFDataFormatException()
|
||||
}
|
||||
}
|
||||
return String(chars)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user