diff --git a/mirai-core-qqandroid/src/jvmTest/kotlin/net.mamoe.mirai.qqandroid.io.serialization/JceDecoderTest.kt b/mirai-core-qqandroid/src/jvmTest/kotlin/net.mamoe.mirai.qqandroid.io.serialization/JceDecoderTest.kt deleted file mode 100644 index a782d38f3..000000000 --- a/mirai-core-qqandroid/src/jvmTest/kotlin/net.mamoe.mirai.qqandroid.io.serialization/JceDecoderTest.kt +++ /dev/null @@ -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 = listOf(1, 2, 3), // error here - @SerialId(9) val map: Map> = mapOf("哈哈" to mapOf("哈哈" to byteArrayOf(1, 2, 3))), - // @SerialId(10) val nestedJceStruct: TestSimpleJceStruct = TestSimpleJceStruct(), - @SerialId(11) val byteList2: List> = 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? = listOf(1, 2, 3), // error here - @SerialId(9) val map: Map>? = mapOf("哈哈" to mapOf("哈哈" to byteArrayOf(1, 2, 3))), - @SerialId(10) val nestedJceStruct: TestComplexJceStruct? = TestComplexJceStruct(), - @SerialId(11) val byteList2: List>? = 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> = 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> = 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 = 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 = 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> = 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 = 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> = 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? = null, - @SerialId(9) val map: Map>? = null, - @SerialId(10) val nestedJceStruct: TestComplexJceStruct? = null, - @SerialId(11) val byteList2: List>? = 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 - ) : 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() - ) - } -} - - */ \ No newline at end of file diff --git a/mirai-core-qqandroid/src/jvmTest/kotlin/net.mamoe.mirai.qqandroid.io.serialization/TestRequesetPacket.kt b/mirai-core-qqandroid/src/jvmTest/kotlin/net.mamoe.mirai.qqandroid.io.serialization/TestRequesetPacket.kt deleted file mode 100644 index 56b7742d2..000000000 --- a/mirai-core-qqandroid/src/jvmTest/kotlin/net.mamoe.mirai.qqandroid.io.serialization/TestRequesetPacket.kt +++ /dev/null @@ -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) { - - 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)) - } - } -} \ No newline at end of file diff --git a/mirai-core-qqandroid/src/jvmTest/kotlin/test/DataClassGenerator.kt b/mirai-core-qqandroid/src/jvmTest/kotlin/test/DataClassGenerator.kt deleted file mode 100644 index eca393496..000000000 --- a/mirai-core-qqandroid/src/jvmTest/kotlin/test/DataClassGenerator.kt +++ /dev/null @@ -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() - 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() - 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; - } - } - } -} - - - diff --git a/mirai-core-qqandroid/src/jvmTest/kotlin/test/JceDataClassGenerator.kt b/mirai-core-qqandroid/src/jvmTest/kotlin/test/JceDataClassGenerator.kt deleted file mode 100644 index c68ee1dcd..000000000 --- a/mirai-core-qqandroid/src/jvmTest/kotlin/test/JceDataClassGenerator.kt +++ /dev/null @@ -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? = null//seems useless - lateinit var properties: List - - 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() - 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; -} \ No newline at end of file diff --git a/mirai-core-qqandroid/src/jvmTest/kotlin/test/ProtoBufDataClassGenerator.kt b/mirai-core-qqandroid/src/jvmTest/kotlin/test/ProtoBufDataClassGenerator.kt deleted file mode 100644 index 358ca562a..000000000 --- a/mirai-core-qqandroid/src/jvmTest/kotlin/test/ProtoBufDataClassGenerator.kt +++ /dev/null @@ -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 -) { - fun toKotlinProtoBufClass(): String { - return if (innerClasses.isNotEmpty()) { - """ - $source { - ${innerClasses.joinToString("\n\n") { " ${it.toKotlinProtoBufClass()}" }} - } - """.trimIndent() - } else source - } - - override fun toString(): String = toKotlinProtoBufClass() -} - -fun MutableList.arrangeClasses(): List { - val tree: MutableMap = 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 { - 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, - 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("\"") -} \ No newline at end of file diff --git a/mirai-core-qqandroid/src/jvmTest/kotlin/test/QLogReader.kt b/mirai-core-qqandroid/src/jvmTest/kotlin/test/QLogReader.kt deleted file mode 100644 index f0f8931b2..000000000 --- a/mirai-core-qqandroid/src/jvmTest/kotlin/test/QLogReader.kt +++ /dev/null @@ -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) { - - 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) - } -} \ No newline at end of file diff --git a/mirai-core-qqandroid/src/jvmTest/kotlin/test/dumpProtobufId.kts b/mirai-core-qqandroid/src/jvmTest/kotlin/test/dumpProtobufId.kts deleted file mode 100644 index 6af83baad..000000000 --- a/mirai-core-qqandroid/src/jvmTest/kotlin/test/dumpProtobufId.kts +++ /dev/null @@ -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())) -} \ No newline at end of file diff --git a/mirai-core-qqandroid/src/jvmTest/kotlin/test/protoBuf.kt b/mirai-core-qqandroid/src/jvmTest/kotlin/test/protoBuf.kt deleted file mode 100644 index 7c9793da7..000000000 --- a/mirai-core-qqandroid/src/jvmTest/kotlin/test/protoBuf.kt +++ /dev/null @@ -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)