mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-10 02:20:14 +08:00
Rearrange tests
This commit is contained in:
parent
3e6c7bc691
commit
d5c2c19c53
@ -1,317 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.qqandroid.io.serialization
|
||||
|
||||
/*
|
||||
|
||||
import kotlinx.io.core.readBytes
|
||||
import kotlinx.serialization.SerialId
|
||||
import kotlinx.serialization.Serializable
|
||||
import net.mamoe.mirai.qqandroid.io.JceOutput
|
||||
import net.mamoe.mirai.qqandroid.io.JceStruct
|
||||
import net.mamoe.mirai.qqandroid.io.buildJcePacket
|
||||
import net.mamoe.mirai.utils.contentToString
|
||||
import net.mamoe.mirai.utils.io.toUHexString
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class JceDecoderTest {
|
||||
@Serializable
|
||||
data class TestSimpleJceStruct(
|
||||
@SerialId(0) val string: String = "123",
|
||||
@SerialId(1) val byte: Byte = 123,
|
||||
@SerialId(2) val short: Short = 123,
|
||||
@SerialId(3) val int: Int = 123,
|
||||
@SerialId(4) val long: Long = 123,
|
||||
@SerialId(5) val float: Float = 123f,
|
||||
@SerialId(6) val double: Double = 123.0,
|
||||
@SerialId(7) val byteArray: ByteArray = byteArrayOf(1, 2, 3),
|
||||
@SerialId(8) val byteArray2: ByteArray = byteArrayOf(1, 2, 3)
|
||||
) : JceStruct {
|
||||
override fun writeTo(output: JceOutput) = output.run {
|
||||
writeString(string, 0)
|
||||
writeByte(byte, 1)
|
||||
writeShort(short, 2)
|
||||
writeInt(int, 3)
|
||||
writeLong(long, 4)
|
||||
writeFloat(float, 5)
|
||||
writeDouble(double, 6)
|
||||
writeFully(byteArray, 7)
|
||||
writeFully(byteArray2, 8)
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as TestSimpleJceStruct
|
||||
|
||||
if (string != other.string) return false
|
||||
if (byte != other.byte) return false
|
||||
if (short != other.short) return false
|
||||
if (int != other.int) return false
|
||||
if (long != other.long) return false
|
||||
if (float != other.float) return false
|
||||
if (double != other.double) return false
|
||||
if (!byteArray.contentEquals(other.byteArray)) return false
|
||||
if (!byteArray2.contentEquals(other.byteArray2)) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = string.hashCode()
|
||||
result = 31 * result + byte
|
||||
result = 31 * result + short
|
||||
result = 31 * result + int
|
||||
result = 31 * result + long.hashCode()
|
||||
result = 31 * result + float.hashCode()
|
||||
result = 31 * result + double.hashCode()
|
||||
result = 31 * result + byteArray.contentHashCode()
|
||||
result = 31 * result + byteArray2.contentHashCode()
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testByteArray() {
|
||||
|
||||
@Serializable
|
||||
data class TestByteArray(
|
||||
@SerialId(0) val byteArray: ByteArray = byteArrayOf(1, 2, 3)
|
||||
) : JceStruct {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as TestByteArray
|
||||
|
||||
if (!byteArray.contentEquals(other.byteArray)) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return byteArray.contentHashCode()
|
||||
}
|
||||
}
|
||||
assertEquals(
|
||||
TestByteArray(),
|
||||
TestByteArray().toByteArray(TestByteArray.serializer()).loadAs(TestByteArray.serializer())
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSimpleStruct() {
|
||||
assertEquals(
|
||||
TestSimpleJceStruct(),
|
||||
TestSimpleJceStruct().toByteArray(TestSimpleJceStruct.serializer()).loadAs(TestSimpleJceStruct.serializer())
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@Serializable
|
||||
class TestComplexJceStruct(
|
||||
@SerialId(6) val string: String = "haha",
|
||||
@SerialId(7) val byteArray: ByteArray = ByteArray(500),
|
||||
@SerialId(8) val byteList: List<Long> = listOf(1, 2, 3), // error here
|
||||
@SerialId(9) val map: Map<String, Map<String, ByteArray>> = mapOf("哈哈" to mapOf("哈哈" to byteArrayOf(1, 2, 3))),
|
||||
// @SerialId(10) val nestedJceStruct: TestSimpleJceStruct = TestSimpleJceStruct(),
|
||||
@SerialId(11) val byteList2: List<List<Int>> = listOf(listOf(1, 2, 3), listOf(1, 2, 3))
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
class TestComplexNullableJceStruct(
|
||||
@SerialId(6) val string: String = "haha",
|
||||
@SerialId(7) val byteArray: ByteArray = ByteArray(2000),
|
||||
@SerialId(8) val byteList: List<Long>? = listOf(1, 2, 3), // error here
|
||||
@SerialId(9) val map: Map<String, Map<String, ByteArray>>? = mapOf("哈哈" to mapOf("哈哈" to byteArrayOf(1, 2, 3))),
|
||||
@SerialId(10) val nestedJceStruct: TestComplexJceStruct? = TestComplexJceStruct(),
|
||||
@SerialId(11) val byteList2: List<List<Int>>? = listOf(listOf(1, 2, 3), listOf(1, 2, 3))
|
||||
) : JceStruct
|
||||
|
||||
@Test
|
||||
fun testEncoder() {
|
||||
println(
|
||||
TestComplexJceStruct().toByteArray(TestComplexJceStruct.serializer()).loadAs(
|
||||
TestComplexNullableJceStruct.serializer(),
|
||||
JceCharset.GBK
|
||||
).contentToString()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testEncoder3() {
|
||||
println(
|
||||
TestComplexNullableJceStruct().toByteArray(TestComplexNullableJceStruct.serializer()).loadAs(
|
||||
TestComplexNullableJceStruct.serializer(),
|
||||
JceCharset.GBK
|
||||
).contentToString()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNestedList() {
|
||||
@Serializable
|
||||
data class TestNestedList(
|
||||
@SerialId(7) val array: List<List<Int>> = listOf(listOf(1, 2, 3), listOf(1, 2, 3), listOf(1, 2, 3))
|
||||
) : JceStruct
|
||||
|
||||
println(buildJcePacket {
|
||||
writeCollection(listOf(listOf(1, 2, 3), listOf(1, 2, 3), listOf(1, 2, 3)), 7)
|
||||
}.readBytes().loadAs(TestNestedList.serializer()).contentToString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNestedArray() {
|
||||
@Serializable
|
||||
class TestNestedArray(
|
||||
@SerialId(7) val array: Array<Array<Int>> = arrayOf(arrayOf(1, 2, 3), arrayOf(1, 2, 3), arrayOf(1, 2, 3))
|
||||
) : JceStruct
|
||||
|
||||
println(buildJcePacket {
|
||||
writeFully(arrayOf(arrayOf(1, 2, 3), arrayOf(1, 2, 3), arrayOf(1, 2, 3)), 7)
|
||||
}.readBytes().loadAs(TestNestedArray.serializer()).contentToString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSimpleMap() {
|
||||
|
||||
@Serializable
|
||||
data class TestSimpleMap(
|
||||
@SerialId(7) val map: Map<String, Long> = mapOf("byteArrayOf(1)" to 2222L)
|
||||
) : JceStruct
|
||||
assertEquals(buildJcePacket {
|
||||
writeMap(mapOf("byteArrayOf(1)" to 2222), 7)
|
||||
}.readBytes().loadAs(TestSimpleMap.serializer()).toString(), TestSimpleMap().toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSimpleList() {
|
||||
|
||||
@Serializable
|
||||
data class TestSimpleList(
|
||||
@SerialId(7) val list: List<String> = listOf("asd", "asdasdasd")
|
||||
) : JceStruct
|
||||
assertEquals(buildJcePacket {
|
||||
writeCollection(listOf("asd", "asdasdasd"), 7)
|
||||
}.readBytes().loadAs(TestSimpleList.serializer()).toString(), TestSimpleList().toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNestedMap() {
|
||||
@Serializable
|
||||
class TestNestedMap(
|
||||
@SerialId(7) val map: Map<ByteArray, Map<ByteArray, ShortArray>> = mapOf(byteArrayOf(1) to mapOf(byteArrayOf(1) to shortArrayOf(2)))
|
||||
) : JceStruct
|
||||
assertEquals(buildJcePacket {
|
||||
writeMap(mapOf(byteArrayOf(1) to mapOf(byteArrayOf(1) to shortArrayOf(2))), 7)
|
||||
}.readBytes().loadAs(TestNestedMap.serializer()).map.entries.first().value.contentToString(), "{01=[0x0002(2)]}")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMap3() {
|
||||
@Serializable
|
||||
class TestNestedMap(
|
||||
@SerialId(7) val map: Map<Byte, ShortArray> = mapOf(1.toByte() to shortArrayOf(2))
|
||||
) : JceStruct
|
||||
assertEquals("{0x01(1)=[0x0002(2)]}", buildJcePacket {
|
||||
writeMap(mapOf(1.toByte() to shortArrayOf(2)), 7)
|
||||
}.readBytes().loadAs(TestNestedMap.serializer()).map.contentToString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNestedMap2() {
|
||||
@Serializable
|
||||
class TestNestedMap(
|
||||
@SerialId(7) val map: Map<Int, Map<Byte, ShortArray>> = mapOf(1 to mapOf(1.toByte() to shortArrayOf(2)))
|
||||
) : JceStruct
|
||||
assertEquals(buildJcePacket {
|
||||
writeMap(mapOf(1 to mapOf(1.toByte() to shortArrayOf(2))), 7)
|
||||
}.readBytes().loadAs(TestNestedMap.serializer()).map.entries.first().value.contentToString(), "{0x01(1)=[0x0002(2)]}")
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testNullableEncode() {
|
||||
@Serializable
|
||||
data class AllNullJce(
|
||||
@SerialId(6) val string: String? = null,
|
||||
@SerialId(7) val byteArray: ByteArray? = null,
|
||||
@SerialId(8) val byteList: List<Long>? = null,
|
||||
@SerialId(9) val map: Map<String, Map<String, ByteArray>>? = null,
|
||||
@SerialId(10) val nestedJceStruct: TestComplexJceStruct? = null,
|
||||
@SerialId(11) val byteList2: List<List<Int>>? = null
|
||||
) : JceStruct {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as AllNullJce
|
||||
|
||||
if (string != other.string) return false
|
||||
if (byteArray != null) {
|
||||
if (other.byteArray == null) return false
|
||||
if (!byteArray.contentEquals(other.byteArray)) return false
|
||||
} else if (other.byteArray != null) return false
|
||||
if (byteList != other.byteList) return false
|
||||
if (map != other.map) return false
|
||||
if (nestedJceStruct != other.nestedJceStruct) return false
|
||||
if (byteList2 != other.byteList2) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = string?.hashCode() ?: 0
|
||||
result = 31 * result + (byteArray?.contentHashCode() ?: 0)
|
||||
result = 31 * result + (byteList?.hashCode() ?: 0)
|
||||
result = 31 * result + (map?.hashCode() ?: 0)
|
||||
result = 31 * result + (nestedJceStruct?.hashCode() ?: 0)
|
||||
result = 31 * result + (byteList2?.hashCode() ?: 0)
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
assert(AllNullJce().toByteArray(AllNullJce.serializer()).isEmpty())
|
||||
assertEquals(ByteArray(0).loadAs(AllNullJce.serializer()), AllNullJce())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNestedStruct() {
|
||||
@Serializable
|
||||
data class OuterStruct(
|
||||
@SerialId(0) val innerStructList: List<TestSimpleJceStruct>
|
||||
) : JceStruct
|
||||
|
||||
println(buildJcePacket {
|
||||
writeCollection(listOf(TestSimpleJceStruct(), TestSimpleJceStruct()), 0)
|
||||
}.readBytes().loadAs(OuterStruct.serializer()).innerStructList.toString())
|
||||
assertEquals(
|
||||
buildJcePacket {
|
||||
writeCollection(listOf(TestSimpleJceStruct(), TestSimpleJceStruct()), 0)
|
||||
}.readBytes().toUHexString(),
|
||||
OuterStruct(listOf(TestSimpleJceStruct(), TestSimpleJceStruct())).toByteArray(OuterStruct.serializer()).toUHexString()
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
OuterStruct(
|
||||
listOf(
|
||||
TestSimpleJceStruct(),
|
||||
TestSimpleJceStruct()
|
||||
)
|
||||
).toByteArray(OuterStruct.serializer()).loadAs(OuterStruct.serializer()).contentToString(),
|
||||
OuterStruct(listOf(TestSimpleJceStruct(), TestSimpleJceStruct())).contentToString()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
@ -1,26 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.qqandroid.io.serialization
|
||||
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.data.jce.RequestPacket
|
||||
import net.mamoe.mirai.utils.io.hexToBytes
|
||||
|
||||
class TestRequesetPacket {
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun main(args: Array<String>) {
|
||||
|
||||
val data =
|
||||
"10 03 2C 3C 4C 56 0B 50 75 73 68 53 65 72 76 69 63 65 66 0E 53 76 63 52 65 71 52 65 67 69 73 74 65 72 7D 00 01 D6 00 08 00 01 06 0E 53 76 63 52 65 71 52 65 67 69 73 74 65 72 1D 00 01 BE 00 0A 02 DD B8 E4 76 10 07 2C 36 00 40 0B 5C 6C 7C 8C 9C AC B0 1D C0 01 D6 00 EC FD 10 00 00 10 07 EA 76 78 FD EB 06 C3 33 B2 18 80 32 15 09 BA F1 11 04 08 FC 12 F6 13 19 41 6E 64 72 6F 69 64 20 53 44 4B 20 62 75 69 6C 74 20 66 6F 72 20 78 38 36 F6 14 19 41 6E 64 72 6F 69 64 20 53 44 4B 20 62 75 69 6C 74 20 66 6F 72 20 78 38 36 F6 15 02 31 30 F0 16 01 F1 17 0F 06 FC 18 FC 1A F3 1B A9 00 FE 00 66 00 82 00 FC 1D F6 1E 04 4D 49 55 49 F6 1F 14 3F 4F 4E 45 50 4C 55 53 20 41 35 30 30 30 5F 32 33 5F 31 37 FD 21 00 00 0D 0A 04 08 2E 10 00 0A 05 08 9B 02 10 00 FC 22 FC 24 0B 8C 98 0C A8 0C".hexToBytes()
|
||||
|
||||
println(data.loadAs(RequestPacket.serializer(), JceCharset.UTF8))
|
||||
}
|
||||
}
|
||||
}
|
@ -1,89 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package test
|
||||
|
||||
import java.util.*
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
|
||||
fun main() {
|
||||
val inputs = LinkedList<String>()
|
||||
thread {
|
||||
while (true){
|
||||
val x = readLine()
|
||||
if(x!=null) inputs.offer(x)
|
||||
}
|
||||
}
|
||||
|
||||
tailrec fun getNext():String{
|
||||
val x = inputs.poll()
|
||||
if(x == null){
|
||||
Thread.sleep(100)
|
||||
return getNext()
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
fun getAll():String{
|
||||
val b = StringBuilder(getNext())
|
||||
Thread.sleep(500)
|
||||
while(true){
|
||||
val c = inputs.poll();
|
||||
if(c===null)break;
|
||||
b.append("\n").append(c)
|
||||
}
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
while (true){
|
||||
println("-proto || -jce")
|
||||
val x = getNext()
|
||||
|
||||
if(x.contains("proto",true)){
|
||||
//proto
|
||||
println("..Copy file content below, after each file is submited, click enter, after all file are in, input \'end'\'")
|
||||
val y = mutableListOf<String>()
|
||||
while (true){
|
||||
val z = getAll()
|
||||
if(z.toLowerCase() == "end" || z.toLowerCase() == "end\n"){
|
||||
println("received file content: " + y.size + ", start generating ProtoBuf" )
|
||||
break;
|
||||
}
|
||||
y.add(z)
|
||||
println("received, ")
|
||||
}
|
||||
println("======================>protoBuf output<===========================")
|
||||
println()
|
||||
println()
|
||||
println(y.map { it.generateProtoBufDataClass() }.toMutableList().arrangeClasses().joinToString("\n\n"));
|
||||
println()
|
||||
println()
|
||||
println("======================>protoBuf output<===========================")
|
||||
}
|
||||
|
||||
if(x.contains("jce",true)){
|
||||
println("..Copy the WHOLE file below")
|
||||
while (true){
|
||||
val z = getAll()
|
||||
println("======================>JCE output<===========================")
|
||||
println()
|
||||
println()
|
||||
println(toJCEInfo(z).toString())
|
||||
println()
|
||||
println()
|
||||
println("======================>JCE output<===========================")
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,190 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package test;
|
||||
|
||||
import java.io.File
|
||||
|
||||
fun main() {
|
||||
println(
|
||||
"import kotlinx.serialization.SerialId\n" +
|
||||
"import kotlinx.serialization.Serializable\n" +
|
||||
"import net.mamoe.mirai.qqandroid.io.JceStruct\n"
|
||||
)
|
||||
File(
|
||||
"""
|
||||
E:\Projects\QQAndroidFF\app\src\main\java\ConfigPush
|
||||
""".trimIndent()
|
||||
).listFiles()!!.forEach {
|
||||
try {
|
||||
println(toJCEInfo(it.readText()).toString())
|
||||
} catch (e: Exception) {
|
||||
println("when processing ${it.path}")
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 不支持叠加中
|
||||
*/
|
||||
class JCEInfo(
|
||||
){
|
||||
lateinit var className: String
|
||||
var parents: List<String>? = null//seems useless
|
||||
lateinit var properties: List<Property>
|
||||
|
||||
override fun toString(): String {
|
||||
properties = properties.sortedBy { it->it.jceID }
|
||||
val max = (properties.size - 1).toString().length
|
||||
val builder:StringBuilder = StringBuilder("@Serializable")
|
||||
builder.append("\n").append("internal class ").append(className).append("(")
|
||||
properties.forEach {
|
||||
builder.append(",").append("\n").append(it.toStringWithSpacing(max))
|
||||
}
|
||||
builder.append("\n").append("): JceStruct")
|
||||
return builder.toString().replace("(,","(")
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
class Property(
|
||||
var name:String,
|
||||
var type:String,
|
||||
var defaultValue:String? = null
|
||||
){
|
||||
var isRequired: Boolean = true
|
||||
var jceID:Int = -1
|
||||
|
||||
//convert type/default value to kotlin format
|
||||
init {
|
||||
type = type
|
||||
.replace("byte[]", "ByteArray")
|
||||
.replace("ArrayList", "List")
|
||||
.replace("byte", "Byte")
|
||||
.replace("int", "Int")
|
||||
.replace("short", "Short")
|
||||
.replace("long", "Long")
|
||||
|
||||
if(name.length >1 && name.get(1).isUpperCase()){
|
||||
if(name.get(0) == 'l' || name.get(0) =='c' || name.get(0) == 'b'){
|
||||
name = name.substring(1)
|
||||
}
|
||||
}
|
||||
|
||||
if(name.startsWith("str") || name.startsWith("bytes")){
|
||||
name = name.replace("str","").replace("bytes","")
|
||||
}
|
||||
|
||||
if(name.contains("_")){
|
||||
val x = name.split("_")
|
||||
name = x.get(0);
|
||||
var z = 1;
|
||||
repeat(x.size-1){
|
||||
name+= "" + x.get(z).get(0).toUpperCase() + x.get(z).substring(1).toLowerCase()
|
||||
++z;
|
||||
}
|
||||
}
|
||||
|
||||
name = "" + name.get(0).toLowerCase() + "" + name.substring(1)
|
||||
}
|
||||
|
||||
//@SerialId(1) val iVersion: Short = 3,
|
||||
override fun toString(): String {
|
||||
if (defaultValue != null) {
|
||||
return "@SerialId(" + jceID + ") val " + name + ":" + type + " = " + defaultValue
|
||||
}
|
||||
return "@SerialId(" + jceID + ") val " + name + ":" + type+"? = null"
|
||||
}
|
||||
|
||||
fun toStringWithSpacing(maxIDLength:Int): String {
|
||||
val space = " ".repeat((maxIDLength - (jceID.toString().length)).coerceAtLeast(0))
|
||||
var base = " @SerialId(" + jceID + ") " + space + "val " + name + ":" + type + ""
|
||||
if(!isRequired){
|
||||
if(defaultValue == null) {
|
||||
base += "? = null"
|
||||
}else{
|
||||
base += "? = $defaultValue"
|
||||
}
|
||||
}else{
|
||||
if(defaultValue != null) {
|
||||
base+=" = " + defaultValue
|
||||
}
|
||||
}
|
||||
return base
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
fun toJCEInfo(source:String):JCEInfo{
|
||||
val info = JCEInfo()
|
||||
val allProperties = mutableMapOf<String,Property>()
|
||||
var inputStreamVariableRegix:String? = null
|
||||
// println(source)
|
||||
source.split("\n").forEach{
|
||||
when{
|
||||
it.contains("class") -> {
|
||||
var var0 = it.substringBetween("class","{").trim()
|
||||
if(var0.contains("extends")){
|
||||
info.parents = var0.substringAfter("extends").split(",").map { it.trim() }.toList()
|
||||
var0 = var0.substringBefore(" extends")
|
||||
}
|
||||
//println("class name: $var0" )
|
||||
info.className = var0
|
||||
}
|
||||
|
||||
(it.contains("public") && it.contains(";") && (!it.contains("static"))) -> {
|
||||
val var1 = it.replace(", ",",").trim().split(" ")
|
||||
if(var1.size == 5){
|
||||
allProperties.put(var1[2],
|
||||
Property(
|
||||
var1[2],
|
||||
var1[1].replace(",", ", "),
|
||||
var1[4].replace(";","")
|
||||
)
|
||||
)
|
||||
}else{
|
||||
allProperties.put(
|
||||
var1[2].replace(";",""),
|
||||
Property(
|
||||
var1[2].replace(";",""),
|
||||
var1[1].replace(",", ", ")
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
(inputStreamVariableRegix==null && it.contains("public void readFrom")) -> {
|
||||
// public void readFrom(JceInputStream var1) {
|
||||
inputStreamVariableRegix = it.trim().substringBetween("(JceInputStream ",")") + ".read"
|
||||
//println("inputStreamVariableRegix: " + inputStreamVariableRegix )
|
||||
}
|
||||
|
||||
(inputStreamVariableRegix!=null && it.contains(inputStreamVariableRegix!!)) -> {
|
||||
val key = it.substringBetween("this.", " = ")
|
||||
if(!allProperties.containsKey(key)){
|
||||
println(key + " is found in readFrom but not in properties")
|
||||
}
|
||||
val src = it
|
||||
.replace(".readString(",".read(\" \",")
|
||||
.substringBetween("(",");")
|
||||
.split(",")
|
||||
with(allProperties.get(key)!!){
|
||||
this.jceID = src[1].trim().toInt()
|
||||
this.isRequired = src[2].trim().toBoolean()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
info.properties = allProperties.values.toList();
|
||||
return info;
|
||||
}
|
@ -1,406 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
@file:Suppress("EXPERIMENTAL_API_USAGE")
|
||||
|
||||
package test
|
||||
|
||||
import net.mamoe.mirai.utils.MiraiDebugAPI
|
||||
import net.mamoe.mirai.utils.cryptor.ProtoType
|
||||
import net.mamoe.mirai.utils.cryptor.protoFieldNumber
|
||||
import java.io.File
|
||||
|
||||
fun main() {
|
||||
println(
|
||||
"""
|
||||
import kotlinx.serialization.SerialId
|
||||
import kotlinx.serialization.Serializable
|
||||
import net.mamoe.mirai.qqandroid.io.ProtoBuf
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
|
||||
""".trimIndent()
|
||||
)
|
||||
println(
|
||||
File(
|
||||
"""
|
||||
E:\Projects\QQAndroidFF\app\src\main\java\tencent\im\msgrevoke
|
||||
""".trimIndent()
|
||||
)
|
||||
.generateUnarrangedClasses().toMutableList().arrangeClasses().joinToString("\n\n")
|
||||
)
|
||||
}
|
||||
|
||||
class ArrangedClass(
|
||||
val name: String,
|
||||
val source: String,
|
||||
val innerClasses: MutableList<ArrangedClass>
|
||||
) {
|
||||
fun toKotlinProtoBufClass(): String {
|
||||
return if (innerClasses.isNotEmpty()) {
|
||||
"""
|
||||
$source {
|
||||
${innerClasses.joinToString("\n\n") { " ${it.toKotlinProtoBufClass()}" }}
|
||||
}
|
||||
""".trimIndent()
|
||||
} else source
|
||||
}
|
||||
|
||||
override fun toString(): String = toKotlinProtoBufClass()
|
||||
}
|
||||
|
||||
fun MutableList<GeneratedClass>.arrangeClasses(): List<ArrangedClass> {
|
||||
val tree: MutableMap<String, ArrangedClass> = mutableMapOf()
|
||||
|
||||
// 先处理所有没有父类的
|
||||
this.removeAll(this.filter { it.superclasses.isEmpty() }.onEach {
|
||||
tree[it.className] = it.toArrangedClass()
|
||||
})
|
||||
|
||||
// 一层继承的处理
|
||||
tree.forEach { (name, clazz) ->
|
||||
this.removeAll(this.filter { it.superclasses.size == 1 && it.superclasses[0] == name }.onEach {
|
||||
clazz.innerClasses.add(it.toArrangedClass())
|
||||
})
|
||||
}
|
||||
|
||||
// 两层继承的处理
|
||||
tree.filter { it.value.innerClasses.isNotEmpty() }.forEach { (_, clazz) ->
|
||||
clazz.innerClasses.forEach { innerClass ->
|
||||
this.removeAll(this.filter { it.superclasses[1] == innerClass.name }.onEach {
|
||||
innerClass.innerClasses.add(it.toArrangedClass())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return tree.values.toList()
|
||||
|
||||
// // 循环处理每个 class 的第一个父类
|
||||
// while (this.any { it.superclasses.isNotEmpty() }) {
|
||||
// this.forEach { generatedClass: GeneratedClass ->
|
||||
// generatedClass.superclasses.lastOrNull()?.let { superClassName ->
|
||||
// if (!tree.containsKey(superClassName)) {
|
||||
// tree[superClassName] = this@arrangeClasses.firstOrNull {
|
||||
// it.className == superClassName
|
||||
// } ?: inline {
|
||||
// println("${generatedClass.className} 继承了 $superClassName, 但找不到生成的这个 class, 将使用一个空的 class 替代")
|
||||
// GeneratedClass(mutableListOf(), superClassName, "class $superClassName")
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// generatedClass.superclasses.remove(superClassName)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
fun File.generateUnarrangedClasses(): List<GeneratedClass> {
|
||||
return this.listFiles()?.filter { it.isFile }?.map {
|
||||
it.readText().generateProtoBufDataClass()
|
||||
} ?: error("Not a folder")
|
||||
}
|
||||
|
||||
fun String.substringBetween(left: String, right: String): String {
|
||||
return this.substringAfter(left, "").substringBefore(right, "")
|
||||
}
|
||||
|
||||
data class GeneratedClass(
|
||||
/**
|
||||
* 带先后顺序
|
||||
*/
|
||||
val superclasses: MutableList<String>,
|
||||
val className: String,
|
||||
val source: String
|
||||
) {
|
||||
fun toArrangedClass(): ArrangedClass {
|
||||
return ArrangedClass(className, source, mutableListOf())
|
||||
}
|
||||
}
|
||||
|
||||
sealed class InferredType(val adjustKotlinAnnotationDeclaration: (String) -> String = { it }, val kotlinType: String) {
|
||||
object FIXED64 : InferredType({ "@ProtoType(ProtoNumberType.FIXED) $it" }, "Long")
|
||||
object FIXED32 : InferredType({ "@ProtoType(ProtoNumberType.FIXED) $it" }, "Int")
|
||||
object FIXED16 : InferredType({ "@ProtoType(ProtoNumberType.FIXED) $it" }, "Short")
|
||||
object FIXED8 : InferredType({ "@ProtoType(ProtoNumberType.FIXED) $it" }, "Byte")
|
||||
|
||||
object SIGNED64 : InferredType({ "@ProtoType(ProtoNumberType.SIGNED) $it" }, "Long")
|
||||
object SIGNED32 : InferredType({ "@ProtoType(ProtoNumberType.SIGNED) $it" }, "Int")
|
||||
object SIGNED16 : InferredType({ "@ProtoType(ProtoNumberType.SIGNED) $it" }, "Short")
|
||||
object SIGNED8 : InferredType({ "@ProtoType(ProtoNumberType.SIGNED) $it" }, "Byte")
|
||||
|
||||
object UNSIGNED64 : InferredType(kotlinType = "Long")
|
||||
object UNSIGNED32 : InferredType(kotlinType = "Int")
|
||||
object UNSIGNED16 : InferredType(kotlinType = "Short")
|
||||
object UNSIGNED8 : InferredType(kotlinType = "Byte")
|
||||
|
||||
object BYTES : InferredType(kotlinType = "ByteArray")
|
||||
object STRING : InferredType(kotlinType = "String")
|
||||
|
||||
object FLOAT : InferredType(kotlinType = "Float")
|
||||
object DOUBLE : InferredType(kotlinType = "Double")
|
||||
object BOOLEAN : InferredType(kotlinType = "Boolean")
|
||||
|
||||
object ENUM : InferredType(kotlinType = "Int /* enum */")
|
||||
|
||||
class REPEATED(kotlinType: String) : InferredType(kotlinType = "List<$kotlinType>")
|
||||
class CUSTOM(kotlinType: String) : InferredType(kotlinType = kotlinType)
|
||||
}
|
||||
|
||||
data class PBFieldInfo(
|
||||
val protoTag: Int,
|
||||
val name: String,
|
||||
val inferredType: InferredType,
|
||||
val defaultValue: String
|
||||
) {
|
||||
fun toKotlinProtoBufClassParam(): String {
|
||||
return "${inferredType.adjustKotlinAnnotationDeclaration("@SerialId($protoTag)")} val $name: ${inferredType.kotlinType}${if (defaultValue == "null") "?" else ""} = $defaultValue"
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalUnsignedTypes::class)
|
||||
fun String.generateProtoBufDataClass(): GeneratedClass {
|
||||
if (this.indexOf("extends") == -1) {
|
||||
val javaClassname = substringBetween("class", "{")
|
||||
val superclasses = javaClassname.split("$").map { it.trim().adjustClassName() }.toMutableList().apply { removeAt(this.lastIndex) }
|
||||
val className = substringBetween("class", "{").substringAfterLast("$").trim().adjustClassName()
|
||||
return GeneratedClass(superclasses, className, "@Serializable\nclass $className : ProtoBuf")
|
||||
}
|
||||
|
||||
val superclasses = substringBetween("class", "extends").split("$").map { it.trim().adjustClassName() }.toMutableList()
|
||||
superclasses.removeAt(superclasses.lastIndex)
|
||||
val className = substringBetween("class", "extends").substringAfterLast("$").trim().adjustClassName()
|
||||
|
||||
|
||||
val ids = substringBetween("new int[]{", "}").split(",").map { it.trim() }
|
||||
|
||||
if (ids.all { it.isBlank() }) {
|
||||
return GeneratedClass(superclasses, className, "@Serializable\nclass $className : ProtoBuf")
|
||||
}
|
||||
|
||||
val names = substringBetween("new String[]{", "}").split(",").map { it.trim() }
|
||||
val defaultValues = substringBetween("new Object[]{", "}").split(",").map { it.trim() }
|
||||
|
||||
|
||||
// name to original declaration
|
||||
val pbTypedFields = lines()
|
||||
.asSequence()
|
||||
.map { it.trim() }
|
||||
.filter { it.startsWith("public final PB") }
|
||||
.filterNot { it.startsWith("public final class") }
|
||||
.map { it.substringAfter("public final PB") }
|
||||
.associateBy { it.substringBetween(" ", " ").takeIf { it.isNotBlank() } ?: it.substringBetween(" ", ";") }
|
||||
.mapKeys { it.key.trim() }
|
||||
|
||||
val customTypedFields = lines()
|
||||
.asSequence()
|
||||
.map { it.trim() }
|
||||
.filter { it.startsWith("public ") }
|
||||
.filterNot { it.startsWith("public final") }
|
||||
.filterNot { it.startsWith("public static") }
|
||||
.filterNot { it.startsWith("public ${substringBetween("class", "extends").trim()}()") }
|
||||
.map { it.substringAfter("public ") }
|
||||
.associateBy { it.substringBetween(" ", " ").takeIf { it.isNotBlank() } ?: it.substringBetween(" ", ";") }
|
||||
.mapKeys { it.key.trim() }
|
||||
|
||||
val source = buildString {
|
||||
append("@Serializable").append("\n")
|
||||
append("class $className(").append("\n")
|
||||
|
||||
ids.map { it.toUInt() }
|
||||
.map { protoFieldNumber(it) }
|
||||
.mapIndexed { index, tag ->
|
||||
var name = names[index].adjustName()
|
||||
var defaultValue = defaultValues[index].let {
|
||||
if (it.startsWith("var")) "EMPTY_BYTE_ARRAY" else it
|
||||
}
|
||||
|
||||
val originalName = names[index].substringBetween("\"", "\"")
|
||||
val javaDeclaration = pbTypedFields[originalName]
|
||||
|
||||
val inferredType: InferredType = if (javaDeclaration == null) {
|
||||
InferredType.CUSTOM(
|
||||
customTypedFields[originalName]?.substringBefore(" ")?.adjustClassName()?.replace("$", ".")
|
||||
?: error("找不到 customTypedFields for $originalName in class $className")
|
||||
)
|
||||
} else {
|
||||
when (val javaFieldType = javaDeclaration.substringBefore("Field")) {
|
||||
"Int8", "UInt8" -> InferredType.UNSIGNED8
|
||||
"Int16", "UInt16" -> InferredType.UNSIGNED16
|
||||
"Int32", "UInt32" -> InferredType.UNSIGNED32
|
||||
"Int64", "UInt64" -> InferredType.UNSIGNED64
|
||||
|
||||
"SInt8" -> InferredType.SIGNED8
|
||||
"SInt16" -> InferredType.SIGNED16
|
||||
"SInt32" -> InferredType.SIGNED32
|
||||
"SInt64" -> InferredType.SIGNED64
|
||||
|
||||
"Fixed8", "FInt8", "SFInt8" -> InferredType.FIXED8
|
||||
"Fixed16", "FInt16", "SFInt16" -> InferredType.FIXED16
|
||||
"Fixed32", "FInt32", "SFInt32" -> InferredType.FIXED32
|
||||
"Fixed64", "FInt64", "SFInt64" -> InferredType.FIXED64
|
||||
|
||||
"Bytes" -> InferredType.BYTES
|
||||
"String" -> InferredType.STRING
|
||||
"Double" -> InferredType.DOUBLE
|
||||
"Float" -> InferredType.FLOAT
|
||||
"Bool" -> InferredType.BOOLEAN
|
||||
"Enum" -> InferredType.ENUM
|
||||
|
||||
"Repeat", "RepeatMessage" -> InferredType.REPEATED(
|
||||
javaDeclaration.substringBetween("<", ">").adjustClassName().replace(
|
||||
"$",
|
||||
"."
|
||||
).replace("Integer", "Int")
|
||||
)
|
||||
else -> error("Unsupported type: $javaFieldType for $originalName in class $className")
|
||||
}
|
||||
}
|
||||
|
||||
fun adjustPropertyName(_name: String): String {
|
||||
@Suppress("NAME_SHADOWING")
|
||||
var name = _name
|
||||
when {
|
||||
|
||||
name.startsWith("string") -> {
|
||||
name = name.substringAfter("string").takeIf { it.isNotBlank() }?.adjustName() ?: "string"
|
||||
if (defaultValue == "EMPTY_BYTE_ARRAY")
|
||||
defaultValue = "\"\""
|
||||
}
|
||||
name.startsWith("str") -> {
|
||||
name = name.substringAfter("str").takeIf { it.isNotBlank() }?.adjustName() ?: "str"
|
||||
if (defaultValue == "EMPTY_BYTE_ARRAY")
|
||||
defaultValue = "\"\""
|
||||
}
|
||||
name.startsWith("uint32") -> {
|
||||
name = name.substringAfter("uint32").takeIf { it.isNotBlank() }?.adjustName() ?: "uint32"
|
||||
defaultValue = defaultValue.replace("D", "", ignoreCase = true)
|
||||
.replace("f", "", ignoreCase = true)
|
||||
.replace(".0", "", ignoreCase = true)
|
||||
.replace("l", "", ignoreCase = true)
|
||||
}
|
||||
name.startsWith("double") -> {
|
||||
name = name.substringAfter("double").takeIf { it.isNotBlank() }?.adjustName() ?: "double"
|
||||
defaultValue = defaultValue.replace("D", "", ignoreCase = true)
|
||||
.replace("f", "", ignoreCase = true)
|
||||
.replace("l", "", ignoreCase = true)
|
||||
}
|
||||
name.startsWith("float") -> {
|
||||
name = name.substringAfter("float").takeIf { it.isNotBlank() }?.adjustName() ?: "float"
|
||||
defaultValue = defaultValue.replace("D", "", ignoreCase = true)
|
||||
.replace("f", "", ignoreCase = true)
|
||||
.replace("l", "", ignoreCase = true) + "f"
|
||||
}
|
||||
name.startsWith("uint16") -> {
|
||||
name = name.substringAfter("uint16").takeIf { it.isNotBlank() }?.adjustName() ?: "uint16"
|
||||
defaultValue = defaultValue.replace("D", "", ignoreCase = true)
|
||||
.replace("f", "", ignoreCase = true)
|
||||
.replace(".0", "", ignoreCase = true)
|
||||
.replace("l", "", ignoreCase = true)
|
||||
}
|
||||
name.startsWith("uint8") -> {
|
||||
name = name.substringAfter("uint8").takeIf { it.isNotBlank() }?.adjustName() ?: "uint8"
|
||||
defaultValue = defaultValue.replace("D", "", ignoreCase = true)
|
||||
.replace("f", "", ignoreCase = true)
|
||||
.replace(".0", "", ignoreCase = true)
|
||||
.replace("l", "", ignoreCase = true)
|
||||
}
|
||||
name.startsWith("uint64") -> {
|
||||
name = name.substringAfter("uint64").takeIf { it.isNotBlank() }?.adjustName() ?: "uint64"
|
||||
defaultValue = defaultValue.replace("D", "", ignoreCase = true)
|
||||
.replace("f", "", ignoreCase = true)
|
||||
.replace(".0", "", ignoreCase = true)
|
||||
}
|
||||
name.startsWith("bytes") -> {
|
||||
name = name.substringAfter("bytes").takeIf { it.isNotBlank() }?.adjustName() ?: "bytes"
|
||||
}
|
||||
name.startsWith("rpt") -> {
|
||||
name = name.substringAfter("rpt").takeIf { it.isNotBlank() }?.substringAfter("_")?.let { adjustPropertyName(it) } ?: "rpt"
|
||||
}
|
||||
}
|
||||
return name
|
||||
}
|
||||
name = adjustPropertyName(name)
|
||||
|
||||
when (inferredType) {
|
||||
is InferredType.REPEATED -> {
|
||||
if (defaultValue.getNumericalValue() == 0 || defaultValue == "EMPTY_BYTE_ARRAY") {
|
||||
defaultValue = "null"
|
||||
}
|
||||
}
|
||||
is InferredType.STRING -> {
|
||||
if (defaultValue == "EMPTY_BYTE_ARRAY") {
|
||||
defaultValue = "\"\""
|
||||
}
|
||||
}
|
||||
is InferredType.BYTES -> {
|
||||
if (defaultValue == "\"\"") {
|
||||
defaultValue = "EMPTY_BYTE_ARRAY"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
name = name.adjustName()
|
||||
if (name[0] in '0'..'9') {
|
||||
name = "_" + name
|
||||
}
|
||||
|
||||
append(PBFieldInfo(tag, name, inferredType, defaultValue).toKotlinProtoBufClassParam())
|
||||
|
||||
if (ids.size - 1 != index) {
|
||||
append(",")
|
||||
}
|
||||
|
||||
append("\n")
|
||||
}
|
||||
|
||||
append(") : ProtoBuf")
|
||||
}
|
||||
|
||||
return GeneratedClass(superclasses, className, source)
|
||||
}
|
||||
|
||||
fun String.getNumericalValue(): Int? {
|
||||
return this.filter { it in '0'..'9' }.toDoubleOrNull()?.toInt()
|
||||
}
|
||||
|
||||
@OptIn(MiraiDebugAPI::class)
|
||||
fun ProtoType.mapToKotlinType(): String {
|
||||
return when (this) {
|
||||
ProtoType.VAR_INT -> "Int"
|
||||
ProtoType.BIT_64 -> "Long"
|
||||
ProtoType.LENGTH_DELIMI -> "String"
|
||||
ProtoType.BIT_32 -> "Float"
|
||||
else -> "UNKNOWN"
|
||||
}
|
||||
}
|
||||
|
||||
fun String.adjustClassName(): String {
|
||||
when (this.trim()) {
|
||||
"ByteStringMicro" -> return "ByteArray"
|
||||
}
|
||||
if(this.isEmpty()){
|
||||
return ""
|
||||
}
|
||||
return String(this.adjustName().toCharArray().apply { this[0] = this[0].toUpperCase() })
|
||||
}
|
||||
|
||||
fun String.adjustName(): String {
|
||||
val result = this.toCharArray()
|
||||
if(result.size == 0){
|
||||
return ""
|
||||
}
|
||||
result[0] = result[0].toLowerCase()
|
||||
for (index in result.indices) {
|
||||
if (result[index] == '_') {
|
||||
if (index + 1 in result.indices) {
|
||||
result[index + 1] = result[index + 1].toUpperCase()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return String(result).replace("_", "").trim().removePrefix("\"").removeSuffix("\"")
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package test
|
||||
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.File
|
||||
import java.util.zip.InflaterInputStream
|
||||
|
||||
object QLogReader {
|
||||
@JvmStatic
|
||||
fun main(args: Array<String>) {
|
||||
|
||||
println(readQLog(File("/Users/jiahua.liu/Downloads/wtlogin_20200129.log")))
|
||||
}
|
||||
|
||||
fun readQLog(file: File): String {
|
||||
return (decompress(file.readBytes()))
|
||||
}
|
||||
|
||||
fun readQLog(file: ByteArray): String {
|
||||
return (decompress(file))
|
||||
}
|
||||
|
||||
|
||||
fun decompress(array: ByteArray): String {
|
||||
return buildString {
|
||||
if (array.isNotEmpty()) {
|
||||
var i = 0
|
||||
var n = 0
|
||||
while (array.size > n + 3) {
|
||||
val buf_to_int32: Int = buf_to_int32(array, n)
|
||||
if (array.size <= n + buf_to_int32 + 3) {
|
||||
break
|
||||
}
|
||||
val buf = ByteArray(buf_to_int32)
|
||||
System.arraycopy(array, n + 4, buf, 0, buf_to_int32)
|
||||
n += 4 + buf_to_int32
|
||||
++i
|
||||
val byteArrayOutputStream = ByteArrayOutputStream()
|
||||
val `in` = ByteArrayInputStream(buf)
|
||||
val inflaterInputStream = InflaterInputStream(`in`)
|
||||
val array2 = ByteArray(1024)
|
||||
while (true) {
|
||||
val read = inflaterInputStream.read(array2)
|
||||
if (read == -1) {
|
||||
break
|
||||
}
|
||||
byteArrayOutputStream.write(array2, 0, read)
|
||||
}
|
||||
append(byteArrayOutputStream.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun buf_to_int32(array: ByteArray, n: Int): Int {
|
||||
return (array[n].toInt() shl 24 and -0x1000000) + (array[n + 1].toInt() shl 16 and 0xFF0000) + (array[n + 2].toInt() shl 8 and 0xFF00) + (array[n + 3].toInt() shl 0 and 0xFF)
|
||||
}
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
@file:Suppress("EXPERIMENTAL_API_USAGE")
|
||||
|
||||
package test
|
||||
|
||||
import net.mamoe.mirai.utils.cryptor.protoFieldNumber
|
||||
import net.mamoe.mirai.utils.cryptor.protoType
|
||||
|
||||
intArrayOf(
|
||||
8, 16, 24, 32, 40, 48, 56, 64, 74, 82
|
||||
).forEach {
|
||||
println(protoFieldNumber(it.toUInt()).toString() + " -> " + protoType(it.toUInt()))
|
||||
}
|
@ -1,102 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
@file:Suppress("EXPERIMENTAL_API_USAGE", "unused", "NO_REFLECTION_IN_CLASS_PATH")
|
||||
|
||||
package net.mamoe.mirai.utils.cryptor
|
||||
|
||||
import net.mamoe.mirai.utils.MiraiDebugAPI
|
||||
|
||||
// ProtoBuf utilities
|
||||
|
||||
|
||||
@Suppress("FunctionName", "SpellCheckingInspection")
|
||||
/*
|
||||
* Type Meaning Used For
|
||||
* 0 Varint int32, int64, uint32, uint64, sint32, sint64, bool, enum
|
||||
* 1 64-bit fixed64, sfixed64, double
|
||||
* 2 Length-delimi string, bytes, embedded messages, packed repeated fields
|
||||
* 3 Start group Groups (deprecated)
|
||||
* 4 End group Groups (deprecated)
|
||||
* 5 32-bit fixed32, sfixed32, float
|
||||
*
|
||||
* https://www.jianshu.com/p/f888907adaeb
|
||||
*/
|
||||
@MiraiDebugAPI
|
||||
fun ProtoFieldId(serializedId: UInt): ProtoFieldId =
|
||||
ProtoFieldId(
|
||||
protoFieldNumber(serializedId),
|
||||
protoType(serializedId)
|
||||
)
|
||||
|
||||
@MiraiDebugAPI
|
||||
data class ProtoFieldId(
|
||||
val fieldNumber: Int,
|
||||
val type: ProtoType
|
||||
) {
|
||||
override fun toString(): String = "$type $fieldNumber"
|
||||
}
|
||||
|
||||
@Suppress("SpellCheckingInspection")
|
||||
@MiraiDebugAPI
|
||||
enum class ProtoType(val value: Byte, private val typeName: String) {
|
||||
/**
|
||||
* int32, int64, uint32, uint64, sint32, sint64, bool, enum
|
||||
*/
|
||||
VAR_INT(0x00, "varint"),
|
||||
|
||||
/**
|
||||
* fixed64, sfixed64, double
|
||||
*/
|
||||
BIT_64(0x01, " 64bit"),
|
||||
|
||||
/**
|
||||
* string, bytes, embedded messages, packed repeated fields
|
||||
*/
|
||||
LENGTH_DELIMI(0x02, "delimi"),
|
||||
|
||||
/**
|
||||
* Groups (deprecated)
|
||||
*/
|
||||
START_GROUP(0x03, "startg"),
|
||||
|
||||
/**
|
||||
* Groups (deprecated)
|
||||
*/
|
||||
END_GROUP(0x04, " endg"),
|
||||
|
||||
/**
|
||||
* fixed32, sfixed32, float
|
||||
*/
|
||||
BIT_32(0x05, " 32bit"),
|
||||
;
|
||||
|
||||
override fun toString(): String = this.typeName
|
||||
|
||||
companion object {
|
||||
fun valueOf(value: Byte): ProtoType = values().firstOrNull { it.value == value } ?: error("Unknown ProtoType $value")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 由 ProtoBuf 序列化后的 id 得到类型
|
||||
*
|
||||
* serializedId = (fieldNumber << 3) | wireType
|
||||
*/
|
||||
@MiraiDebugAPI
|
||||
fun protoType(number: UInt): ProtoType =
|
||||
ProtoType.valueOf(number.toInt().shl(29).ushr(29).toByte())
|
||||
|
||||
/**
|
||||
* ProtoBuf 序列化后的 id 转为序列前标记的 id
|
||||
*
|
||||
* serializedId = (fieldNumber << 3) | wireType
|
||||
*/
|
||||
@MiraiDebugAPI
|
||||
fun protoFieldNumber(number: UInt): Int = number.toInt().ushr(3)
|
Loading…
Reference in New Issue
Block a user