From db3a1fb2d1d739f90f839066c09e94f49d383d24 Mon Sep 17 00:00:00 2001
From: Him188 <Him188@mamoe.net>
Date: Mon, 20 Jan 2020 00:17:45 +0800
Subject: [PATCH 01/14] Fix contentToString

---
 .../qqandroid/network/protocol/packet/login/LoginPacket.kt | 7 +------
 .../net/mamoe/mirai/utils/cryptor/contentToString.kt       | 2 +-
 .../kotlin/net.mamoe.mirai/utils/cryptor/Proto.kt          | 2 +-
 .../net/mamoe/mirai/utils/cryptor/contentToString.kt       | 1 +
 4 files changed, 4 insertions(+), 8 deletions(-)

diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/LoginPacket.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/LoginPacket.kt
index 20a9c63b4..fe31b17d9 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/LoginPacket.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/LoginPacket.kt
@@ -27,10 +27,6 @@ internal object LoginPacket : PacketFactory<LoginPacket.LoginPacketResponse>() {
         this._id = PacketId(commandId = 0x0810, commandName = "wtlogin.login")
     }
 
-    fun hahahaha() {
-
-    }
-
     object SubCommand9 {
         private const val appId = 16L
         private const val subAppId = 537062845L
@@ -61,8 +57,6 @@ internal object LoginPacket : PacketFactory<LoginPacket.LoginPacketResponse>() {
                     LoginType.PASSWORD
                 )
 
-                hahahaha()
-
                 /* // from GetStWithPasswd
                 int mMiscBitmap = this.mMiscBitmap;
                 if (t.uinDeviceToken) {
@@ -199,6 +193,7 @@ internal object LoginPacket : PacketFactory<LoginPacket.LoginPacketResponse>() {
         println("TLV KEYS: " + tlvMap.keys.joinToString { it.contentToString() })
 
         tlvMap[0x150]?.let { client.analysisTlv150(it) }
+        tlvMap[0x305]?.let { println("TLV 0x305=${it.toUHexString()}") }
         tlvMap[0x161]?.let { client.analysisTlv161(it) }
         tlvMap[0x119]?.let { t119Data ->
             t119Data.decryptBy(client.tgtgtKey).toReadPacket().debugPrint("0x119data").apply {
diff --git a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/cryptor/contentToString.kt b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/cryptor/contentToString.kt
index 6f33ae4c1..5e54798e7 100644
--- a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/cryptor/contentToString.kt
+++ b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/cryptor/contentToString.kt
@@ -5,7 +5,7 @@ import kotlin.reflect.full.allSuperclasses
 
 
 actual fun Any.contentToStringReflectively(prefix: String): String {
-    val newPrefix = prefix + ProtoMap.indent
+    val newPrefix = prefix
     return (this::class.simpleName ?: "<UnnamedClass>") + "#" + this::class.hashCode() + " {\n" +
             this.allFieldsFromSuperClassesMatching { it.name.startsWith("net.mamoe.mirai") }
                 .distinctBy { it.name }
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/Proto.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/Proto.kt
index afaf7279f..ad7905202 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/Proto.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/Proto.kt
@@ -163,7 +163,7 @@ fun Any?.contentToString(prefix: String = ""): String = when (this) {
     is Iterable<*> -> this.joinToString(prefix = "[", postfix = "]") { it.contentToString() }
     is Iterator<*> -> this.asSequence().joinToString(prefix = "[", postfix = "]") { it.contentToString() }
     is Sequence<*> -> this.joinToString(prefix = "[", postfix = "]") { it.contentToString() }
-    is Map<*, *> -> this.entries.joinToString(prefix = "{", postfix = "}") { it.key.contentToString() + "=" + it.value.contentToString() }
+    is Map<*, *> -> this.entries.joinToString(prefix = "{", postfix = "}") { it.key.contentToString(prefix) + "=" + it.value.contentToString(prefix) }
     else -> {
         if (this == null) "null"
         else if (this::class.isData) this.toString()
diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/cryptor/contentToString.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/cryptor/contentToString.kt
index 117f0cf53..507b5e2e7 100644
--- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/cryptor/contentToString.kt
+++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/cryptor/contentToString.kt
@@ -12,6 +12,7 @@ actual fun Any.contentToStringReflectively(prefix: String): String {
             this.allFieldsFromSuperClassesMatching { it.name.startsWith("net.mamoe.mirai") }
                 .distinctBy { it.name }
                 .filterNot { it.name.contains("$") || it.name == "Companion" || it.isSynthetic || it.name == "serialVersionUID" }
+                .filterNot { it.isEnumConstant }
                 .joinToStringPrefixed(
                     prefix = newPrefix
                 ) {

From 061f8fa89497d6072ac7c996e1cbb445a8838a72 Mon Sep 17 00:00:00 2001
From: Him188 <Him188@mamoe.net>
Date: Mon, 20 Jan 2020 20:19:41 +0800
Subject: [PATCH 02/14] Update README.md

---
 README.md | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/README.md b/README.md
index 6596ecd43..bbd840913 100644
--- a/README.md
+++ b/README.md
@@ -33,7 +33,7 @@
 [Mirai-Console](https://github.com/mamoe/mirai/tree/master/mirai-console) 插件支持, 在终端中启动Mirai并获得机器人服务
 
 ## Use as a library
-**把 Mirai 作为库内置于您的项目中使用.**  
+**mirai-core 为独立设计, 可以作为库内置于您的任意 Java/Android 项目中使用.**  
 Mirai 只上传在 `jcenter`, 因此请确保在 `build.gradle` 添加 `jcenter()` 仓库  
 ```kotlin
 repositories{
@@ -120,4 +120,4 @@ bot.subscribeAlways<MemberPermissionChangedEvent> {
 
 ## Acknowledgement
 特别感谢 [JetBrains](https://www.jetbrains.com/?from=mirai) 提供的免费 [IntelliJ IDEA](https://www.jetbrains.com/idea/?from=mirai) 等 IDE 授权  
-[<img src=".github/jetbrains-variant-3.png" width="200"/>](https://www.jetbrains.com/?from=mirai)
\ No newline at end of file
+[<img src=".github/jetbrains-variant-3.png" width="200"/>](https://www.jetbrains.com/?from=mirai)

From 767790f6e160e300b519565ca858c842190597b7 Mon Sep 17 00:00:00 2001
From: Him188 <Him188@mamoe.net>
Date: Mon, 20 Jan 2020 20:47:13 +0800
Subject: [PATCH 03/14] Introduce MiraiDebugAPI

---
 .../net.mamoe.mirai/utils/Annotations.kt       | 18 +++++++++++++++---
 1 file changed, 15 insertions(+), 3 deletions(-)

diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Annotations.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Annotations.kt
index 95cb38e13..ea99d5965 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Annotations.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Annotations.kt
@@ -25,12 +25,24 @@ annotation class MiraiInternalAPI(
  * 这些 API 不具有稳定性, 且可能会在任意时刻更改.
  * 不建议在发行版本中使用这些 API.
  */
-@Experimental(level = Experimental.Level.ERROR)
+@Experimental(level = Experimental.Level.WARNING)
 @Target(CLASS, TYPEALIAS, FUNCTION, PROPERTY, FIELD, CONSTRUCTOR)
 annotation class MiraiExperimentalAPI(
     val message: String = ""
 )
 
+/**
+ * 标记这个类, 类型, 函数, 属性, 字段, 或构造器为仅供调试阶段使用的.
+ *
+ * 这些 API 不具有稳定性, 可能会在任意时刻更改, 并且效率非常低下.
+ * 非常不建议在发行版本中使用这些 API.
+ */
+@Experimental(level = Experimental.Level.WARNING)
+@Target(CLASS, TYPEALIAS, FUNCTION, PROPERTY, FIELD, CONSTRUCTOR)
+annotation class MiraiDebugAPI(
+    val message: String = ""
+)
+
 /**
  * 标记这个 API 是自 Mirai 某个版本起才受支持.
  */
@@ -43,13 +55,13 @@ annotation class SinceMirai(val version: String)
  * 包的最后一次修改时间, 和分析时使用的 TIM 版本
  */
 @MustBeDocumented
-@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS, AnnotationTarget.PROPERTY)
+@Target(FUNCTION, CLASS, PROPERTY)
 @Retention(AnnotationRetention.SOURCE)
 annotation class PacketVersion(val date: String, val timVersion: String)
 
 /**
  * 带有这个注解的 [Packet] 将不会被记录在 log 中.
  */
-@Target(AnnotationTarget.CLASS)
+@Target(CLASS)
 @Retention(AnnotationRetention.RUNTIME)
 annotation class NoLog
\ No newline at end of file

From 2ffd62c88301f0e029953ac9109131ccaf5c6002 Mon Sep 17 00:00:00 2001
From: Him188 <Him188@mamoe.net>
Date: Mon, 20 Jan 2020 20:47:46 +0800
Subject: [PATCH 04/14] (internal api) Rename debugPrintIfFail to debugIfFail

---
 .../network/protocol/packet/PacketFactory.kt     |  8 +++-----
 .../network/protocol/packet/login/LoginPacket.kt |  4 ++++
 .../network/packet/event/MemberMute.kt           |  4 ++--
 .../message/internal/MessageDataInternal.kt      |  2 +-
 .../kotlin/net.mamoe.mirai/utils/io/DebugUtil.kt | 16 +++++++++++++++-
 .../net.mamoe.mirai/utils/io/TypeConversion.kt   | 14 ++++++++++++++
 6 files changed, 39 insertions(+), 9 deletions(-)

diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt
index ea37c371e..f095710a9 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt
@@ -8,8 +8,6 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.login.LoginPacket
 import net.mamoe.mirai.qqandroid.network.protocol.packet.login.NullPacketId
 import net.mamoe.mirai.qqandroid.network.protocol.packet.login.NullPacketId.commandName
 import net.mamoe.mirai.qqandroid.network.protocol.packet.login.PacketId
-import net.mamoe.mirai.utils.cryptor.Decrypter
-import net.mamoe.mirai.utils.cryptor.DecrypterType
 import net.mamoe.mirai.utils.cryptor.adjustToPublicKey
 import net.mamoe.mirai.utils.cryptor.decryptBy
 import net.mamoe.mirai.utils.io.*
@@ -59,12 +57,12 @@ internal object KnownPacketFactories : List<PacketFactory<*>> by mutableListOf(
 
     // do not inline. Exceptions thrown will not be reported correctly
     suspend fun parseIncomingPacket(bot: QQAndroidBot, rawInput: ByteReadPacket, consumer: PacketConsumer) =
-        rawInput.debugPrintIfFail("Incoming packet") {
+        rawInput.debugIfFail("Incoming packet") {
             require(remaining < Int.MAX_VALUE) { "rawInput is too long" }
             val expectedLength = readUInt().toInt() - 4
             if (expectedLength > 16e7) {
                 bot.logger.warning("Detect incomplete packet, ignoring.")
-                return@debugPrintIfFail
+                return@debugIfFail
             }
             check(remaining.toInt() == expectedLength) { "Invalid packet length. Expected $expectedLength, got ${rawInput.remaining} Probably packets merged? " }
             // login
@@ -87,7 +85,7 @@ internal object KnownPacketFactories : List<PacketFactory<*>> by mutableListOf(
 
     @UseExperimental(ExperimentalUnsignedTypes::class)
     private suspend fun parseLoginSsoPacket(bot: QQAndroidBot, rawInput: ByteReadPacket, consumer: PacketConsumer) =
-        rawInput.debugPrintIfFail("Login sso packet") {
+        rawInput.debugIfFail("Login sso packet") {
             val commandName: String
             val ssoSequenceId: Int
             readIoBuffer(readInt() - 4).withUse {
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/LoginPacket.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/LoginPacket.kt
index fe31b17d9..c8e81b8f9 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/LoginPacket.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/LoginPacket.kt
@@ -202,6 +202,10 @@ internal object LoginPacket : PacketFactory<LoginPacket.LoginPacketResponse>() {
                 // 01 08 00 10 A1 73 76 98 64 E0 38 C6 C8 18 73 FA D3 85 DA D6 01 6A 00 30 1D 99 4A 28 7E B3 B8 AC 74 B9 C4 BB 6D BB 41 72 F7 5C 9F 0F 79 8A 82 4F 1F 69 34 6D 10 D6 BB E8 A3 4A 2B 5D F1 C7 05 3C F8 72 EF CF 67 E4 3C 94 01 06 00 78 B4 ED 9F 44 ED 10 18 A8 85 0A 8A 85 79 45 47 7F 25 AA EE 2C 53 83 80 0A B3 B0 47 3E 95 51 A4 AE 3E CA A0 1D B4 91 F7 BB 2E 94 76 A8 C8 97 02 C4 5B 15 02 B7 03 9A FC C2 58 6D 17 92 46 AE EB 2F 6F 65 B8 69 6C D6 9D AC 18 6F 07 53 AC FE FA BC BD CE 57 13 10 2D 5A C6 50 AA C2 AE 18 D4 FD CD F2 E0 D1 25 29 56 21 35 8F 01 9D D6 69 44 8F 06 D0 23 26 D3 0E E6 E6 B7 01 0C 00 10 73 32 61 4E 2C 72 35 58 68 28 47 3E 2B 6E 52 62 01 0A 00 48 A4 DA 48 FB B4 8D DA 7B 86 D7 A7 FE 01 1B 70 6F 54 F8 55 38 B0 AD 1B 0C 0B B9 F6 94 24 F8 9E 30 32 22 99 0C 22 CD 44 B8 B0 8A A8 65 E1 B8 F0 49 EF E1 23 D7 0D A3 F1 BB 52 B7 4B AF BD 50 EA BF 15 02 78 2B 8B 10 FB 15 01 0D 00 10 29 75 38 72 21 5D 3F 24 37 46 67 79 2B 65 6D 34 01 14 00 60 00 01 5E 19 65 8C 00 58 93 DD 4D 2C 2D 01 44 99 62 B8 7A EF 04 C5 71 0B F1 BE 4C F4 21 F2 97 B0 14 67 0E 14 9F D8 A2 0B 93 40 90 80 F3 59 7A 69 45 D7 D4 53 4C 08 3A 56 1D C9 95 36 2C 7C 5E EE 36 47 5F AE 26 72 76 FD FD 69 E6 0C 2D 3A E8 CF D4 8D 76 C9 17 C3 E3 CD 21 AB 04 6B 70 C5 EC EC 01 0E 00 10 56 48 3E 29 3A 5A 21 74 55 6A 2C 72 58 73 79 71 01 03 00 30 9B A6 5D 85 5C 40 7C 28 E7 05 A9 25 CA F5 FC C0 51 40 85 F3 2F D2 37 F9 09 A6 E6 56 7F 7A 2E 7D 9F B9 1C 00 65 55 D2 A9 60 03 77 AB 6A F5 3F CE 01 33 00 30 F4 3A A7 08 E2 04 FA C8 9D 54 49 DE 63 EA F0 A5 1C C4 03 57 51 B6 AE 0B 55 41 F8 AB 22 F1 DC A3 B0 73 08 55 14 02 BF FF 55 87 42 4C 23 70 91 6A 01 34 00 10 61 C7 02 3F 1D BE A6 27 2F 24 D4 92 95 68 71 EF 05 28 00 1A 7B 22 51 49 4D 5F 69 6E 76 69 74 61 74 69 6F 6E 5F 62 69 74 22 3A 22 31 22 7D 03 22 00 10 CE 1E 2E DC 69 24 4F 9B FF 2F 52 D8 8F 69 DD 40 01 1D 00 76 5F 5E 10 E2 34 36 79 27 23 53 4D 65 6B 6A 33 6D 7D 4E 3C 5F 00 60 00 01 5E 19 65 8C 00 58 67 00 9C 02 E4 BC DB A3 93 98 A1 ED 4C 91 08 6F 0C 06 E0 12 6A DC 14 5B 4D 20 7C 82 83 AE 94 53 A2 4A A0 35 FF 59 9D F3 EF 82 42 61 67 2A 31 E7 87 7E 74 E7 A3 E7 5C A8 3C 87 CF 40 6A 9F E5 F7 20 4E 56 C6 4F 1C 98 3A 8B A9 4F 1D 10 35 C2 3B A1 08 7A 89 0B 25 0C 63 01 1F 00 0A 00 01 51 80 00 00 03 84 00 00 01 38 00 0E 00 00 00 01 01 0A 00 27 8D 00 00 00 00 00 01 1A 00 13 02 5B 06 01 0E 73 74 65 61 6D 63 68 69 6E 61 2E 66 75 6E 05 22 00 14 00 00 00 00 76 E4 B8 DD AB 53 02 9F 5E 19 65 8C 20 02 ED BD 05 37 00 17 01 01 00 00 00 00 76 E4 B8 DD 04 AB 53 02 9F 5E 19 65 8C 20 02 ED BD 01 20 00 0A 4D 39 50 57 50 6E 4C 31 65 4F 01 6D 00 2C 31 7A 50 7A 63 72 70 4D 30 43 6E 31 37 4C 32 32 6E 77 2D 36 7A 4E 71 48 48 59 41 35 48 71 77 41 37 6D 76 4F 63 2D 4A 56 77 47 51 5F 05 12 03 5D 00 0E 00 0A 74 65 6E 70 61 79 2E 63 6F 6D 00 2C 6E 4A 72 55 55 74 63 2A 34 7A 32 76 31 66 6A 75 77 6F 6A 65 73 72 76 4F 68 70 66 45 76 4A 75 55 4B 6D 34 43 2D 76 74 38 4D 77 38 5F 00 00 00 11 6F 70 65 6E 6D 6F 62 69 6C 65 2E 71 71 2E 63 6F 6D 00 2C 78 59 35 65 62 4D 74 48 44 6D 30 53 6F 68 56 71 68 33 43 79 79 34 6F 63 65 4A 46 6A 51 58 65 68 30 44 61 75 55 30 6C 78 65 52 6B 5F 00 00 00 0B 64 6F 63 73 2E 71 71 2E 63 6F 6D 00 2C 64 6A 62 79 47 57 45 4F 34 58 34 6A 36 4A 73 48 45 65 6B 73 69 74 72 78 79 62 57 69 77 49 68 46 45 70 72 4A 59 4F 2D 6B 36 47 6F 5F 00 00 00 0E 63 6F 6E 6E 65 63 74 2E 71 71 2E 63 6F 6D 00 2C 64 4C 31 41 79 32 41 31 74 33 58 36 58 58 2A 74 33 64 4E 70 2A 31 61 2D 50 7A 65 57 67 48 70 2D 65 47 78 6B 59 74 71 62 69 6C 55 5F 00 00 00 0C 71 7A 6F 6E 65 2E 71 71 2E 63 6F 6D 00 2C 75 6A 55 5A 4F 6A 4F 48 52 61 75 6B 32 55 50 38 77 33 34 68 36 69 46 38 2A 77 4E 50 35 2D 66 54 75 37 67 39 56 67 44 57 2A 6B 6F 5F 00 00 00 0A 76 69 70 2E 71 71 2E 63 6F 6D 00 2C 37 47 31 44 6F 54 2D 4D 57 50 63 2D 62 43 46 68 63 62 32 56 38 6E 77 4A 75 41 51 63 54 39 77 45 49 62 57 43 4A 4B 44 4D 6C 6D 34 5F 00 00 00 0A 71 75 6E 2E 71 71 2E 63 6F 6D 00 2C 7A 73 70 5A 56 43 59 45 7A 35 2A 4F 6B 4E 68 6E 74 79 61 69 6E 6F 68 4D 32 6B 41 6C 2A 74 31 63 7A 48 57 77 30 41 6A 4B 50 4B 6B 5F 00 00 00 0B 67 61 6D 65 2E 71 71 2E 63 6F 6D 00 2C 32 6F 2D 51 53 36 65 43 70 37 6A 43 4E 34 6A 74 6E 47 4F 4B 33 67 73 32 63 4A 6F 56 71 58 65 44 48 61 55 39 65 34 2D 32 34 64 30 5F 00 00 00 0C 71 71 77 65 62 2E 71 71 2E 63 6F 6D 00 2C 63 54 4D 79 64 51 43 35 50 74 43 45 51 72 6F 33 53 54 41 66 7A 56 2D 44 76 46 56 35 58 6D 56 6B 49 31 68 4C 55 48 4E 65 76 56 38 5F 00 00 00 0D 6F 66 66 69 63 65 2E 71 71 2E 63 6F 6D 00 2C 6F 73 72 54 36 32 69 37 66 76 6D 49 50 64 6F 58 4B 48 74 38 58 52 59 56 77 72 7A 6E 69 31 58 7A 57 4C 77 2A 71 36 33 44 74 73 6F 5F 00 00 00 09 74 69 2E 71 71 2E 63 6F 6D 00 2C 41 61 77 4D 78 4D 32 79 58 51 47 75 72 75 55 6C 66 53 58 79 5A 57 48 53 78 52 57 58 50 74 6B 6B 4F 78 6F 66 4A 59 47 6C 71 68 34 5F 00 00 00 0B 6D 61 69 6C 2E 71 71 2E 63 6F 6D 00 2C 67 72 57 68 58 77 34 4C 6E 4B 49 4F 67 63 78 45 71 70 33 61 45 67 37 38 46 7A 77 4E 6D 4B 48 56 6E 6F 50 4C 4F 32 6D 57 6D 6E 38 5F 00 00 00 09 71 7A 6F 6E 65 2E 63 6F 6D 00 2C 72 61 47 79 51 35 54 72 4D 55 7A 6E 74 31 4E 52 44 2D 50 72 74 72 41 55 43 35 6A 61 2D 49 47 2D 73 77 4C 6D 49 51 51 41 44 4C 41 5F 00 00 00 0A 6D 6D 61 2E 71 71 2E 63 6F 6D 00 2C 39 73 2D 4F 51 30 67 76 39 42 6A 37 58 71 52 49 4E 30 35 46 32 64 4D 47 67 47 43 58 57 4A 62 68 63 30 38 63 7A 4B 52 76 6B 78 6B 5F 00 00 03 05 00 10 77 75 6E 54 5F 7E 66 7A 72 40 3C 6E 35 50 53 46 01 43 00 40 3A AE 30 87 81 3D EE BA 31 9C EA 9D 0D D4 73 B1 81 12 E0 94 71 73 7A B0 47 3D 09 47 E5 1B E1 E2 06 1A CB A4 E3 71 9E A6 EA 2A 73 5C C8 D3 B1 2A B1 C7 DA 04 A6 6D 12 26 DF 6B 8B EC C7 12 F8 E1 01 18 00 05 00 00 00 01 00 01 63 00 10 67 6B 60 23 24 6A 55 39 4E 58 24 5E 39 2B 7A 69 01 38 00 5E 00 00 00 09 01 06 00 27 8D 00 00 00 00 00 01 0A 00 24 EA 00 00 00 00 00 01 1C 00 1A 5E 00 00 00 00 00 01 02 00 01 51 80 00 00 00 00 01 03 00 00 1C 20 00 00 00 00 01 20 00 01 51 80 00 00 00 00 01 36 00 1B AF 80 00 00 00 00 01 43 00 1B AF 80 00 00 00 00 01 64 00 1B AF 80 00 00 00 00 01 30 00 0E 00 00 5E 19 65 8C 9F 02 53 AB 00 00 00 00
                 val tlvMap119 = this.readTLVMap()
                 println("tlvMap119 KEYS: " + tlvMap119.keys.joinToString { it.contentToString() })
+                tlvMap119[0x305]?.let { println("TLV 0x305=${it.toUHexString()}") }
+                tlvMap119.filterValues { it.size == 16 }.forEach {
+                    println(it.key.toUHexString("") + "=" + it.value.toUHexString())
+                }
 
                 // ???
                 tlvMap119[0x1c]?.read {
diff --git a/mirai-core-timpc/src/commonMain/kotlin/net.mamoe.mirai.timpc/network/packet/event/MemberMute.kt b/mirai-core-timpc/src/commonMain/kotlin/net.mamoe.mirai.timpc/network/packet/event/MemberMute.kt
index f1c42d740..e2bf6f2a5 100644
--- a/mirai-core-timpc/src/commonMain/kotlin/net.mamoe.mirai.timpc/network/packet/event/MemberMute.kt
+++ b/mirai-core-timpc/src/commonMain/kotlin/net.mamoe.mirai.timpc/network/packet/event/MemberMute.kt
@@ -10,7 +10,7 @@ import net.mamoe.mirai.Bot
 import net.mamoe.mirai.contact.Group
 import net.mamoe.mirai.contact.Member
 import net.mamoe.mirai.data.*
-import net.mamoe.mirai.utils.io.debugPrintIfFail
+import net.mamoe.mirai.utils.io.debugIfFail
 import net.mamoe.mirai.utils.io.readQQ
 import net.mamoe.mirai.utils.io.readRemainingBytes
 import net.mamoe.mirai.utils.io.toUHexString
@@ -62,7 +62,7 @@ internal object MemberMuteEventPacketParserAndHandler : KnownEventParserAndHandl
                 Unknown0x02DCPacketFlag0x0EMaybeMutePacket(readRemainingBytes())
             }
 
-            0x11u -> debugPrintIfFail("解析禁言包(0x02DC)时"){ // 猜测这个失败是撤回??
+            0x11u -> debugIfFail("解析禁言包(0x02DC)时"){ // 猜测这个失败是撤回??
                 // 00 0A 00 04 01 00 00 00 00 0C 00 05 00 01 00 01 01 27 0B 60 E7 11 00 33 08 07 20 E7 C1 AD B8 02 5A 29 08 A6 FE C0 A4 0A 1A 19 08 BC 15 10 C1 95 BC F0 05 18 CA CA 8F DE 04 20 00 28 00 30 A6 FE C0 A4 0A 2A 02 08 00 30 00 38 00
                 // 失败
 
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/internal/MessageDataInternal.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/internal/MessageDataInternal.kt
index aca589b1c..cca576982 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/internal/MessageDataInternal.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/internal/MessageDataInternal.kt
@@ -9,7 +9,7 @@ import net.mamoe.mirai.utils.io.*
 import net.mamoe.mirai.utils.unzip
 
 internal fun IoBuffer.parseMessageFace(): Face {
-    debugPrintIfFail("Analyzing Face") {
+    debugIfFail("Analyzing Face") {
          discardExact(1)
         
         //00  01  AF  0B  00  08  00  01  00  04  52  CC  F5  D0  FF  00  02  14  F0
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/DebugUtil.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/DebugUtil.kt
index 80e9253fd..151b55b00 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/DebugUtil.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/DebugUtil.kt
@@ -5,6 +5,9 @@ import kotlinx.io.pool.useInstance
 import net.mamoe.mirai.utils.DefaultLogger
 import net.mamoe.mirai.utils.MiraiLogger
 import net.mamoe.mirai.utils.withSwitch
+import kotlin.contracts.ExperimentalContracts
+import kotlin.contracts.InvocationKind
+import kotlin.contracts.contract
 
 
 object DebugLogger : MiraiLogger by DefaultLogger("Packet Debug").withSwitch()
@@ -51,7 +54,18 @@ fun ByteReadPacket.debugPrint(name: String = ""): ByteReadPacket {
     }
 }
 
-inline fun <R> Input.debugPrintIfFail(name: String = "", block: ByteReadPacket.() -> R): R {
+/**
+ * 备份数据, 并在 [block] 失败后执行 [onFail].
+ *
+ * 此方法非常低效. 请仅在测试环境使用.
+ */
+@MiraiDebugAPI
+@UseExperimental(ExperimentalContracts::class)
+inline fun <R> Input.debugIfFail(name: String = "", onFail: (ByteArray) -> ByteReadPacket = { it.toReadPacket() }, block: ByteReadPacket.() -> R): R {
+    contract {
+        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
+        callsInPlace(onFail, InvocationKind.UNKNOWN)
+    }
     ByteArrayPool.useInstance {
         val count = this.readAvailable(it)
         try {
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/TypeConversion.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/TypeConversion.kt
index 6cfe2ff68..8c65f1e4a 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/TypeConversion.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/TypeConversion.kt
@@ -99,6 +99,8 @@ fun UByte.fixToUHex(): String = if (this.toInt() in 0..15) "0${this.toString(16)
 
 /**
  * 将无符号 Hex 转为 [ByteArray], 有根据 hex 的 [hashCode] 建立的缓存.
+ *
+ * 这个方法很累, 不建议经常使用.
  */
 fun String.hexToBytes(): ByteArray =
     this.split(" ")
@@ -110,12 +112,24 @@ fun String.hexToBytes(): ByteArray =
 
 /**
  * 每 2 char 为一组, 转换 Hex 为 [ByteArray]
+ *
+ * 这个方法很累, 不建议经常使用.
  */
 fun String.chunkedHexToBytes(): ByteArray =
     this.asSequence().chunked(2).map { (it[0].toString() + it[1]).toUByte(16).toByte() }.toList().toByteArray()
 
+/**
+ * 删掉全部空格和换行后每 2 char 为一组, 转换 Hex 为 [ByteArray].
+ *
+ * 这个方法很累, 不建议经常使用.
+ */
+fun String.autoHexToBytes(): ByteArray =
+    this.replace("\n", "").replace(" ", "").asSequence().chunked(2).map { (it[0].toString() + it[1]).toUByte(16).toByte() }.toList().toByteArray()
+
 /**
  * 将无符号 Hex 转为 [UByteArray], 有根据 hex 的 [hashCode] 建立的缓存.
+ *
+ * 这个方法很累, 不建议经常使用.
  */
 fun String.hexToUBytes(): UByteArray =
     this.split(" ")

From 621809b94b2fcf7724f0d932e6124a182462ea15 Mon Sep 17 00:00:00 2001
From: Him188 <Him188@mamoe.net>
Date: Mon, 20 Jan 2020 20:49:43 +0800
Subject: [PATCH 05/14] Add Retention

---
 .../src/commonMain/kotlin/net.mamoe.mirai/utils/Annotations.kt | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Annotations.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Annotations.kt
index ea99d5965..7efe84d8a 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Annotations.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Annotations.kt
@@ -8,6 +8,7 @@ import kotlin.annotation.AnnotationTarget.*
  * 这些 API 可能会在任意时刻更改, 且不会发布任何预警.
  * 非常不建议在发行版本中使用这些 API.
  */
+@Retention(AnnotationRetention.SOURCE)
 @Experimental(level = Experimental.Level.ERROR)
 @Target(
     CLASS, TYPEALIAS, FUNCTION, PROPERTY, FIELD, CONSTRUCTOR,
@@ -25,6 +26,7 @@ annotation class MiraiInternalAPI(
  * 这些 API 不具有稳定性, 且可能会在任意时刻更改.
  * 不建议在发行版本中使用这些 API.
  */
+@Retention(AnnotationRetention.SOURCE)
 @Experimental(level = Experimental.Level.WARNING)
 @Target(CLASS, TYPEALIAS, FUNCTION, PROPERTY, FIELD, CONSTRUCTOR)
 annotation class MiraiExperimentalAPI(
@@ -37,6 +39,7 @@ annotation class MiraiExperimentalAPI(
  * 这些 API 不具有稳定性, 可能会在任意时刻更改, 并且效率非常低下.
  * 非常不建议在发行版本中使用这些 API.
  */
+@Retention(AnnotationRetention.SOURCE)
 @Experimental(level = Experimental.Level.WARNING)
 @Target(CLASS, TYPEALIAS, FUNCTION, PROPERTY, FIELD, CONSTRUCTOR)
 annotation class MiraiDebugAPI(

From 2052159dc91a417eb692f7c86d2009736a65db1c Mon Sep 17 00:00:00 2001
From: Him188 <Him188@mamoe.net>
Date: Mon, 20 Jan 2020 20:56:43 +0800
Subject: [PATCH 06/14] Annotate with MiraiDebugAPI

---
 .../kotlin/net.mamoe.mirai/utils/io/DebugUtil.kt    | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)

diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/DebugUtil.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/DebugUtil.kt
index 151b55b00..4c25f58a2 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/DebugUtil.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/DebugUtil.kt
@@ -2,9 +2,7 @@ package net.mamoe.mirai.utils.io
 
 import kotlinx.io.core.*
 import kotlinx.io.pool.useInstance
-import net.mamoe.mirai.utils.DefaultLogger
-import net.mamoe.mirai.utils.MiraiLogger
-import net.mamoe.mirai.utils.withSwitch
+import net.mamoe.mirai.utils.*
 import kotlin.contracts.ExperimentalContracts
 import kotlin.contracts.InvocationKind
 import kotlin.contracts.contract
@@ -14,18 +12,22 @@ object DebugLogger : MiraiLogger by DefaultLogger("Packet Debug").withSwitch()
 
 fun Throwable.logStacktrace(message: String? = null) = DebugLogger.error(message, this)
 
+@MiraiDebugAPI("Low efficiency.")
 fun debugPrintln(any: Any?) = DebugLogger.debug(any)
 
+@MiraiDebugAPI("Low efficiency.")
 fun String.debugPrint(name: String): String {
     DebugLogger.debug("$name=$this")
     return this
 }
 
+@MiraiDebugAPI("Low efficiency.")
 fun ByteArray.debugPrint(name: String): ByteArray {
     DebugLogger.debug(name + "=" + this.toUHexString())
     return this
 }
 
+@MiraiDebugAPI("Low efficiency.")
 fun IoBuffer.debugPrint(name: String): IoBuffer {
     ByteArrayPool.useInstance {
         val count = this.readAvailable(it)
@@ -34,6 +36,7 @@ fun IoBuffer.debugPrint(name: String): IoBuffer {
     }
 }
 
+@MiraiDebugAPI("Low efficiency.")
 inline fun IoBuffer.debugCopyUse(block: IoBuffer.() -> Unit): IoBuffer {
     ByteArrayPool.useInstance {
         val count = this.readAvailable(it)
@@ -42,10 +45,12 @@ inline fun IoBuffer.debugCopyUse(block: IoBuffer.() -> Unit): IoBuffer {
     }
 }
 
+@MiraiDebugAPI("Low efficiency.")
 fun Input.debugDiscardExact(n: Number, name: String = "") {
     DebugLogger.debug("Discarded($n) $name=" + this.readBytes(n.toInt()).toUHexString())
 }
 
+@MiraiDebugAPI("Low efficiency.")
 fun ByteReadPacket.debugPrint(name: String = ""): ByteReadPacket {
     ByteArrayPool.useInstance {
         val count = this.readAvailable(it)
@@ -59,7 +64,7 @@ fun ByteReadPacket.debugPrint(name: String = ""): ByteReadPacket {
  *
  * 此方法非常低效. 请仅在测试环境使用.
  */
-@MiraiDebugAPI
+@MiraiDebugAPI("Low efficiency")
 @UseExperimental(ExperimentalContracts::class)
 inline fun <R> Input.debugIfFail(name: String = "", onFail: (ByteArray) -> ByteReadPacket = { it.toReadPacket() }, block: ByteReadPacket.() -> R): R {
     contract {

From 0345bfa4b3ee02a58a3c56bd604f32c0b3284e12 Mon Sep 17 00:00:00 2001
From: Him188 <Him188@mamoe.net>
Date: Mon, 20 Jan 2020 20:57:03 +0800
Subject: [PATCH 07/14] Use ByteArray to store ipAddress

---
 .../net/mamoe/mirai/qqandroid/utils/SystemDeviceInfo.kt      | 2 +-
 .../net/mamoe/mirai/qqandroid/network/protocol/packet/Tlv.kt | 5 +++--
 .../net/mamoe/mirai/qqandroid/utils/SystemDeviceInfo.kt      | 2 +-
 3 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/mirai-core-qqandroid/src/androidMain/kotlin/net/mamoe/mirai/qqandroid/utils/SystemDeviceInfo.kt b/mirai-core-qqandroid/src/androidMain/kotlin/net/mamoe/mirai/qqandroid/utils/SystemDeviceInfo.kt
index 2db2d14bc..c9936f510 100644
--- a/mirai-core-qqandroid/src/androidMain/kotlin/net/mamoe/mirai/qqandroid/utils/SystemDeviceInfo.kt
+++ b/mirai-core-qqandroid/src/androidMain/kotlin/net/mamoe/mirai/qqandroid/utils/SystemDeviceInfo.kt
@@ -76,7 +76,7 @@ actual class SystemDeviceInfo actual constructor(context: Context) : DeviceInfo(
                 (context.applicationContext.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager).deviceId
             }
         }.getOrElse { "" }
-    override val ipAddress: String get() = localIpAddress()
+    override val ipAddress: ByteArray get() = localIpAddress().split(".").map { it.toByte() }.takeIf { it.size == 4 }?.toByteArray() ?: byteArrayOf()
     override val androidId: ByteArray get() = Build.ID.toByteArray()
     override val apn: ByteArray get() = "wifi".toByteArray()
 
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/Tlv.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/Tlv.kt
index 7c78eb00d..008c8f70c 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/Tlv.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/Tlv.kt
@@ -16,14 +16,15 @@ import kotlin.random.Random
  */
 inline class Tlv(val value: ByteArray)
 
-fun BytePacketBuilder.t1(uin: Long, ip: String) {
+fun BytePacketBuilder.t1(uin: Long, ip: ByteArray) {
+    require(ip.size == 4) { "ip.size must == 4" }
     writeShort(0x1)
     writeShortLVPacket {
         writeShort(1) // _ip_ver
         writeInt(Random.nextInt())
         writeInt(uin.toInt())
         writeTime()
-        writeFully(ByteArray(4))
+        writeFully(ip)
         writeShort(0)
     } shouldEqualsTo 20
 }
diff --git a/mirai-core-qqandroid/src/jvmMain/kotlin/net/mamoe/mirai/qqandroid/utils/SystemDeviceInfo.kt b/mirai-core-qqandroid/src/jvmMain/kotlin/net/mamoe/mirai/qqandroid/utils/SystemDeviceInfo.kt
index ed924c03a..8716e2b8c 100644
--- a/mirai-core-qqandroid/src/jvmMain/kotlin/net/mamoe/mirai/qqandroid/utils/SystemDeviceInfo.kt
+++ b/mirai-core-qqandroid/src/jvmMain/kotlin/net/mamoe/mirai/qqandroid/utils/SystemDeviceInfo.kt
@@ -27,7 +27,7 @@ actual class SystemDeviceInfo actual constructor(context: Context) : DeviceInfo(
     override val imsiMd5: ByteArray
         get() = ubyteArrayOf(0xD4u, 0x1Du, 0x8Cu, 0xD9u, 0x8Fu, 0x00u, 0xB2u, 0x04u, 0xE9u, 0x80u, 0x09u, 0x98u, 0xECu, 0xF8u, 0x42u, 0x7Eu).toByteArray()
     override val imei: String get() = "858414369211993"
-    override val ipAddress: String get() = localIpAddress()
+    override val ipAddress: ByteArray get() = localIpAddress().split(".").map { it.toByte() }.takeIf { it.size == 4 }?.toByteArray() ?: byteArrayOf()
     override val androidId: ByteArray get() = "QSR1.190920.001".toByteArray()
     override val apn: ByteArray get() = "wifi".toByteArray()
 

From 5445a10e5fd7f75e7b4076d429dcbd78bc6a8b3f Mon Sep 17 00:00:00 2001
From: Him188 <Him188@mamoe.net>
Date: Mon, 20 Jan 2020 21:29:07 +0800
Subject: [PATCH 08/14] Fix

---
 .../src/commonMain/kotlin/net.mamoe.mirai/utils/io/DebugUtil.kt  | 1 +
 1 file changed, 1 insertion(+)

diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/DebugUtil.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/DebugUtil.kt
index 4c25f58a2..c7dc58de5 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/DebugUtil.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/DebugUtil.kt
@@ -76,6 +76,7 @@ inline fun <R> Input.debugIfFail(name: String = "", onFail: (ByteArray) -> ByteR
         try {
             return block(it.toReadPacket(0, count))
         } catch (e: Throwable) {
+            onFail(it.take(count).toByteArray()).readAvailable(it)
             DebugLogger.debug("Error in ByteReadPacket $name=" + it.toUHexString(offset = 0, length = count))
             throw e
         }

From 7c4263da0f7c4597d9ec884303500d29e669abf5 Mon Sep 17 00:00:00 2001
From: Him188 <Him188@mamoe.net>
Date: Mon, 20 Jan 2020 21:33:33 +0800
Subject: [PATCH 09/14] fix

---
 .../network/protocol/jce/SvcReqRegister.kt       | 16 ++++++++--------
 .../network/protocol/packet/login/LoginPacket.kt |  2 ++
 .../mamoe/mirai/qqandroid/utils/DeviceInfo.kt    |  4 +---
 3 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/jce/SvcReqRegister.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/jce/SvcReqRegister.kt
index 727155432..3bea4715f 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/jce/SvcReqRegister.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/jce/SvcReqRegister.kt
@@ -25,15 +25,15 @@ class SvcReqRegister(
     val lBid: Long = 0L,
     val lCpId: Long = 0L,
     val lUin: Long = 0L,
-    val sBuildVer: String? = "",
-    val sChannelNo: String? = "",
+    val sBuildVer: String? = null,
+    val sChannelNo: String? = null,
     val sOther: String = "",
-    val strDevName: String? = "",
-    val strDevType: String? = "",
-    val strIOSIdfa: String? = "",
-    val strOSVer: String? = "",
-    val strVendorName: String? = "",
-    val strVendorOSName: String? = "",
+    val strDevName: String? = null,
+    val strDevType: String? = null,
+    val strIOSIdfa: String? = null,
+    val strOSVer: String? = null,
+    val strVendorName: String? = null,
+    val strVendorOSName: String? = null,
     val timeStamp: Long = 0L,
     val uNewSSOIp: Long = 0L,
     val uOldSSOIp: Long = 0L,
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/LoginPacket.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/LoginPacket.kt
index c8e81b8f9..87b748041 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/LoginPacket.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/LoginPacket.kt
@@ -10,6 +10,7 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.*
 import net.mamoe.mirai.qqandroid.utils.GuidSource
 import net.mamoe.mirai.qqandroid.utils.MacOrAndroidIdChangeFlag
 import net.mamoe.mirai.qqandroid.utils.guidFlag
+import net.mamoe.mirai.utils.MiraiDebugAPI
 import net.mamoe.mirai.utils.MiraiInternalAPI
 import net.mamoe.mirai.utils.cryptor.contentToString
 import net.mamoe.mirai.utils.cryptor.decryptBy
@@ -174,6 +175,7 @@ internal object LoginPacket : PacketFactory<LoginPacket.LoginPacketResponse>() {
         object Success : LoginPacketResponse()
     }
 
+    @UseExperimental(MiraiDebugAPI::class)
     override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): LoginPacketResponse = this.debugPrint("login解析").run {
         // 00 09 sub cmd
         // 00 type
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/DeviceInfo.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/DeviceInfo.kt
index 0094bea83..327a775a8 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/DeviceInfo.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/DeviceInfo.kt
@@ -3,10 +3,8 @@ package net.mamoe.mirai.qqandroid.utils
 import kotlinx.serialization.SerialId
 import kotlinx.serialization.Serializable
 import kotlinx.serialization.protobuf.ProtoBuf
-import net.mamoe.mirai.qqandroid.network.protocol.packet.oidb.oidb0x769.Oidb0x769
 import net.mamoe.mirai.utils.cryptor.contentToString
 import net.mamoe.mirai.utils.getValue
-import net.mamoe.mirai.utils.io.encodeToString
 import net.mamoe.mirai.utils.unsafeWeakRef
 
 abstract class DeviceInfo(
@@ -42,7 +40,7 @@ abstract class DeviceInfo(
     abstract val imsiMd5: ByteArray
     abstract val imei: String
 
-    abstract val ipAddress: String
+    abstract val ipAddress: ByteArray
 
     abstract val androidId: ByteArray
 

From 6b6499e929fb066fc89a2ac4fac38431c0174ac1 Mon Sep 17 00:00:00 2001
From: Him188 <Him188@mamoe.net>
Date: Mon, 20 Jan 2020 22:30:33 +0800
Subject: [PATCH 10/14] gradle init

---
 .../mirai/qqandroid/network/io/JceInput.kt     | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)
 create mode 100644 mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/io/JceInput.kt

diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/io/JceInput.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/io/JceInput.kt
new file mode 100644
index 000000000..f1dd872a1
--- /dev/null
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/io/JceInput.kt
@@ -0,0 +1,18 @@
+package net.mamoe.mirai.qqandroid.network.io
+
+import kotlinx.io.core.Input
+
+@UseExperimental(ExperimentalUnsignedTypes::class)
+inline class JceHead(private val value: Long) {
+    val tag: Int get() = (value ushr 32).toInt()
+    val type: Int get() = value.toUInt().toInt()
+}
+
+class JceInput(
+    private val input: Input
+): Input by input {
+
+    private fun readHead(): JceHead {
+
+    }
+}
\ No newline at end of file

From e44e7b395fdce5f5ee216df80b73ac614a029774 Mon Sep 17 00:00:00 2001
From: Him188 <Him188@mamoe.net>
Date: Tue, 21 Jan 2020 01:52:29 +0800
Subject: [PATCH 11/14] Daily QQA update: JceInput

---
 .../mirai/qqandroid/network/io/JceInput.kt    | 322 +++++++++++++++++-
 .../mirai/qqandroid/network/io/JceStruct.kt   |   6 +-
 .../network/protocol/jce/RequestPacket.kt     |   7 +
 .../network/protocol/jce/SvcReqRegister.kt    |   8 +-
 .../network/protocol/packet/PacketFactory.kt  |   4 +-
 5 files changed, 338 insertions(+), 9 deletions(-)

diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/io/JceInput.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/io/JceInput.kt
index f1dd872a1..cabecf645 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/io/JceInput.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/io/JceInput.kt
@@ -1,18 +1,330 @@
 package net.mamoe.mirai.qqandroid.network.io
 
-import kotlinx.io.core.Input
+import kotlinx.io.core.*
+import net.mamoe.mirai.utils.io.readString
+import kotlin.experimental.and
 
 @UseExperimental(ExperimentalUnsignedTypes::class)
 inline class JceHead(private val value: Long) {
+    constructor(tag: Int, type: Byte) : this(tag.shl(32).toLong() and type.toLong())
+
     val tag: Int get() = (value ushr 32).toInt()
-    val type: Int get() = value.toUInt().toInt()
+    val type: Byte get() = value.toUInt().toByte()
 }
 
+@Suppress("MemberVisibilityCanBePrivate")
+@UseExperimental(ExperimentalUnsignedTypes::class)
 class JceInput(
-    private val input: Input
-): Input by input {
+    @PublishedApi
+    internal val input: Input
+) {
 
-    private fun readHead(): JceHead {
+    @PublishedApi
+    internal fun readHead(): JceHead = input.run {
+        val var2 = readByte()
+        val type = var2 and 15
+        var tag = (var2.toInt() and 240) shr 4
+        if (tag == 15)
+            tag = readByte().toInt() and 255
+        return JceHead(tag = tag, type = type)
+    }
 
+    fun readBoolean(tag: Int): Boolean = readBooleanOrNull(tag) ?: error("cannot find tag $tag")
+    fun readByte(tag: Int): Byte = readByteOrNull(tag) ?: error("cannot find tag $tag")
+    fun readShort(tag: Int): Short = readShortOrNull(tag) ?: error("cannot find tag $tag")
+    fun readInt(tag: Int): Int = readIntOrNull(tag) ?: error("cannot find tag $tag")
+    fun readLong(tag: Int): Long = readLongOrNull(tag) ?: error("cannot find tag $tag")
+    fun readFloat(tag: Int): Float = readFloatOrNull(tag) ?: error("cannot find tag $tag")
+    fun readDouble(tag: Int): Double = readDoubleOrNull(tag) ?: error("cannot find tag $tag")
+
+    fun readString(tag: Int): String = readStringOrNull(tag) ?: error("cannot find tag $tag")
+
+    fun readByteArray(tag: Int): ByteArray = readByteArrayOrNull(tag) ?: error("cannot find tag $tag")
+    fun readShortArray(tag: Int): ShortArray = readShortArrayOrNull(tag) ?: error("cannot find tag $tag")
+    fun readLongArray(tag: Int): LongArray = readLongArrayOrNull(tag) ?: error("cannot find tag $tag")
+    fun readFloatArray(tag: Int): FloatArray = readFloatArrayOrNull(tag) ?: error("cannot find tag $tag")
+    fun readDoubleArray(tag: Int): DoubleArray = readDoubleArrayOrNull(tag) ?: error("cannot find tag $tag")
+    fun readIntArray(tag: Int): IntArray = readIntArrayOrNull(tag) ?: error("cannot find tag $tag")
+    fun readBooleanArray(tag: Int): BooleanArray = readBooleanArrayOrNull(tag) ?: error("cannot find tag $tag")
+    fun <K, V> readMap(defaultKey: K, defaultValue: V, tag: Int): Map<K, V> = readMapOrNull(defaultKey, defaultValue, tag) ?: error("cannot find tag $tag")
+    fun <T> readList(defaultElement: T, tag: Int): List<T> = readListOrNull(defaultElement, tag) ?: error("cannot find tag $tag")
+    fun <T> readSimpleArray(defaultElement: T, tag: Int): Array<T> = readArrayOrNull(defaultElement, tag) ?: error("cannot find tag $tag")
+    fun <J : JceStruct> readJceStruct(factory: JceStruct.Factory<J>, tag: Int): J = readJceStructOrNull(factory, tag) ?: error("cannot find tag $tag")
+    fun readStringArray(tag: Int): Array<String> = readArrayOrNull("", tag) ?: error("cannot find tag $tag")
+
+    fun readLongOrNull(tag: Int): Long? = skipToTagOrNull(tag) {
+        return when (it.type.toInt()) {
+            12 -> 0
+            0 -> input.readByte().toLong()
+            1 -> input.readShort().toLong()
+            3 -> input.readLong()
+            else -> error("type mismatch: ${it.type}")
+        }
+    }
+
+    fun readShortOrNull(tag: Int): Short? = skipToTagOrNull(tag) {
+        return when (it.type.toInt()) {
+            12 -> 0
+            0 -> input.readByte().toShort()
+            1 -> input.readShort()
+            else -> error("type mismatch: ${it.type}")
+        }
+    }
+
+    fun readIntOrNull(tag: Int): Int? = skipToTagOrNull(tag) {
+        return when (it.type.toInt()) {
+            12 -> 0
+            0 -> input.readByte().toInt()
+            1 -> input.readShort().toInt()
+            2 -> input.readInt()
+            else -> error("type mismatch: ${it.type}")
+        }
+    }
+
+    fun readByteOrNull(tag: Int): Byte? = skipToTagOrNull(tag) {
+        return when (it.type.toInt()) {
+            12 -> 0
+            0 -> input.readByte()
+            else -> error("type mismatch")
+        }
+    }
+
+    fun readFloatOrNull(tag: Int): Float? = skipToTagOrNull(tag) {
+        return when (it.type.toInt()) {
+            12 -> 0f
+            4 -> input.readFloat()
+            else -> error("type mismatch: ${it.type}")
+        }
+    }
+
+    fun readDoubleOrNull(tag: Int): Double? = skipToTagOrNull(tag) {
+        return when (it.type.toInt()) {
+            12 -> 0.0
+            4 -> input.readFloat().toDouble()
+            5 -> input.readDouble()
+            else -> error("type mismatch: ${it.type}")
+        }
+    }
+
+    fun readBooleanOrNull(tag: Int): Boolean? = this.readByteOrNull(tag)?.let { it.toInt() != 0 }
+
+    fun readByteArrayOrNull(tag: Int): ByteArray? = skipToTagOrNull(tag) {
+        when (it.type.toInt()) {
+            9 -> ByteArray(input.readInt()) { readByte(tag) }
+            13 -> {
+                val head = readHead()
+                check(head.type.toInt() == 0) { "type mismatch" }
+                input.readBytes(input.readInt())
+            }
+            else -> error("type mismatch")
+        }
+    }
+
+    fun readShortArrayOrNull(tag: Int): ShortArray? = skipToTagOrNull(tag) {
+        require(it.type.toInt() == 9) { "type mismatch" }
+        ShortArray(input.readInt()) { readShort(tag) }
+    }
+
+    fun readDoubleArrayOrNull(tag: Int): DoubleArray? = skipToTagOrNull(tag) {
+        require(it.type.toInt() == 9) { "type mismatch" }
+        DoubleArray(input.readInt()) { readDouble(tag) }
+    }
+
+    fun readFloatArrayOrNull(tag: Int): FloatArray? = skipToTagOrNull(tag) {
+        require(it.type.toInt() == 9) { "type mismatch" }
+        FloatArray(input.readInt()) { readFloat(tag) }
+    }
+
+    fun readIntArrayOrNull(tag: Int): IntArray? = skipToTagOrNull(tag) {
+        require(it.type.toInt() == 9) { "type mismatch" }
+        IntArray(input.readInt()) { readInt(tag) }
+    }
+
+    fun readLongArrayOrNull(tag: Int): LongArray? = skipToTagOrNull(tag) {
+        require(it.type.toInt() == 9) { "type mismatch" }
+        LongArray(input.readInt()) { readLong(tag) }
+    }
+
+    @Suppress("UNCHECKED_CAST")
+    inline fun <reified T> readArrayOrNull(tag: Int): Array<T>? = skipToTagOrNull(tag) {
+        require(it.type.toInt() == 9) { "type mismatch" }
+        Array(input.readInt()) { readSimpleObject<T>(tag) }
+    }
+
+    @Suppress("UNCHECKED_CAST")
+    fun <T> readArrayOrNull(defaultElement: T, tag: Int): Array<T>? = skipToTagOrNull(tag) {
+        require(it.type.toInt() == 9) { "type mismatch" }
+        Array(input.readInt()) { readObject(defaultElement, tag) as Any } as Array<T>
+    }
+
+    @Suppress("UNCHECKED_CAST")
+    fun <J : JceStruct> readJceStructArrayOrNull(factory: JceStruct.Factory<J>, tag: Int): Array<J>? = skipToTagOrNull(tag) {
+        require(it.type.toInt() == 9) { "type mismatch" }
+        Array(input.readInt()) { readObject(factory, tag) as Any } as Array<J>
+    }
+
+    fun readBooleanArrayOrNull(tag: Int): BooleanArray? = skipToTagOrNull(tag) {
+        require(it.type.toInt() == 9) { "type mismatch" }
+        BooleanArray(input.readInt()) { readBoolean(tag) }
+    }
+
+    fun readStringOrNull(tag: Int): String? = skipToTagOrNull(tag) { head ->
+        return when (head.type.toInt()) {
+            6 -> input.readString(input.readUByte().toInt())
+            7 -> input.readString(input.readUInt().also { require(it.toInt() in 1 until 104857600) { "bad string length: $it" } }.toInt())
+            else -> error("type mismatch: ${head.type}")
+        }
+    }
+
+    fun <T : Map<K, V>, K, V> readMapOrNull(defaultKey: K, defaultValue: V, tag: Int): Map<K, V>? = skipToTagOrNull(tag) {
+        check(it.type.toInt() == 8) { "type mismatch" }
+        val size = readInt(0)
+        val map = HashMap<K, V>(size)
+        repeat(size) {
+            map[readObject(defaultKey, 0)] = readObject(defaultValue, 0)
+        }
+        return map
+    }
+
+    fun <T> readListOrNull(defaultElement: T, tag: Int): List<T>? = skipToTagOrNull(tag) { head ->
+        check(head.type.toInt() == 9) { "type mismatch" }
+        val size = readInt(0)
+        val list = ArrayList<T>(size)
+        repeat(size) {
+            list[it] = readObject(defaultElement, tag)
+        }
+        return list
+    }
+
+    fun <J : JceStruct> readJceStructOrNull(factory: JceStruct.Factory<J>, tag: Int): J? = skipToTagOrNull(tag) { head ->
+        readHead()
+        return factory.newInstanceFrom(this).also { skipToStructEnd() }
+    }
+
+    @Suppress("UNCHECKED_CAST")
+    fun <T> readArrayOrNull(default: Array<T>, tag: Int): Array<T>? = skipToTagOrNull(tag) { head ->
+        val defaultElement = default[0]
+        check(head.type.toInt() == 9) { "type mismatch" }
+        return Array(readInt(0)) { readObject(defaultElement, tag) as Any } as Array<T>
+    }
+
+    @Suppress("IMPLICIT_CAST_TO_ANY", "UNCHECKED_CAST")
+    fun <T> readObject(default: T, tag: Int): T = when (default) {
+        is Byte -> readByte(tag)
+        is Boolean -> readBoolean(tag)
+        is Short -> readShort(tag)
+        is Int -> readInt(tag)
+        is Long -> readLong(tag)
+        is Float -> readFloat(tag)
+        is Double -> readDouble(tag)
+        is String -> readString(tag)
+        is BooleanArray -> readBooleanArray(tag)
+        is ShortArray -> readShortArray(tag)
+        is IntArray -> readIntArray(tag)
+        is LongArray -> readLongArray(tag)
+        is ByteArray -> readByteArray(tag)
+        is FloatArray -> readByteArray(tag)
+        is DoubleArray -> readDoubleArrayOrNull(tag)
+        is JceStruct.Factory<JceStruct> -> readJceStruct(default, tag) as T
+        is List<*> -> {
+            readList(default[0], tag)
+        }
+        is Map<*, *> -> {
+            val entry = default.entries.first()
+            readMap(entry.key, entry.value, tag)
+        }
+        is Array<*> -> readSimpleArray(default, tag)
+        else -> error("unsupported type")
+    } as T
+
+    @Suppress("IMPLICIT_CAST_TO_ANY", "UNCHECKED_CAST")
+    inline fun <reified T> readSimpleObject(tag: Int): T = when (T::class) {
+        Byte::class -> readByte(tag)
+        Boolean::class -> readBoolean(tag)
+        Short::class -> readShort(tag)
+        Int::class -> readInt(tag)
+        Long::class -> readLong(tag)
+        Float::class -> readFloat(tag)
+        Double::class -> readDouble(tag)
+
+        String::class -> readString(tag)
+
+        BooleanArray::class -> readBooleanArray(tag)
+        ShortArray::class -> readShortArray(tag)
+        IntArray::class -> readIntArray(tag)
+        LongArray::class -> readLongArray(tag)
+        ByteArray::class -> readByteArray(tag)
+        FloatArray::class -> readByteArray(tag)
+        DoubleArray::class -> readDoubleArrayOrNull(tag)
+        else -> error("Type is not supported: ${T::class.simpleName}")
+    } as T
+
+    private fun skipField() {
+        skipField(readHead().type)
+    }
+
+    private fun skipToStructEnd() {
+        var head: JceHead
+        do {
+            head = readHead()
+            skipField(head.type)
+        } while (head.type.toInt() != 11)
+    }
+
+    @UseExperimental(ExperimentalUnsignedTypes::class)
+    @PublishedApi
+    internal fun skipField(type: Byte) = when (type.toInt()) {
+        0 -> this.input.discardExact(1)
+        1 -> this.input.discardExact(2)
+        2 -> this.input.discardExact(4)
+        3 -> this.input.discardExact(8)
+        4 -> this.input.discardExact(4)
+        5 -> this.input.discardExact(8)
+        6 -> this.input.discardExact(this.input.readUByte().toInt())
+        7 -> this.input.discardExact(this.input.readInt())
+        8 -> { // map
+            val length = this.readInt(0)
+            var read = 0
+            while (read < length * 2) {
+                skipField()
+                ++read
+            }
+        }
+        9 -> { // list
+            val length = this.readInt(0)
+            var read = 0
+            while (read < length) {
+                skipField()
+                ++read
+            }
+        }
+        10 -> this.skipToStructEnd()
+        11, 12 -> {
+
+        }
+        13 -> {
+            val head = readHead()
+            check(head.type.toInt() == 0) { "skipField with invalid type, type value: " + type + ", " + head.type }
+            this.input.discardExact(this.readInt(0))
+        }
+        else -> error("invalid type.")
+    }
+}
+
+private inline fun <R> JceInput.skipToTag(tag: Int, block: (JceHead) -> R): R {
+    return skipToTagOrNull(tag) { block(it) } ?: error("cannot find required tag $tag")
+}
+
+@PublishedApi
+internal inline fun <R> JceInput.skipToTagOrNull(tag: Int, block: (JceHead) -> R): R? {
+    while (true) {
+        if (this.input.endOfInput)
+            return null
+
+        val head = readHead()
+        if (head.tag == tag) {
+            return block(head)
+        }
+        this.skipField(head.type)
     }
 }
\ No newline at end of file
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/io/JceStruct.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/io/JceStruct.kt
index 73ebe85b4..c02c493dc 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/io/JceStruct.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/io/JceStruct.kt
@@ -1,7 +1,9 @@
 package net.mamoe.mirai.qqandroid.network.io
 
-import kotlinx.io.core.BytePacketBuilder
-
 abstract class JceStruct {
     abstract fun writeTo(builder: JceOutput)
+
+   interface Factory<out T : JceStruct> {
+        fun newInstanceFrom(input: JceInput): T
+    }
 }
\ No newline at end of file
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/jce/RequestPacket.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/jce/RequestPacket.kt
index fc10c55fe..f34ad0887 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/jce/RequestPacket.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/jce/RequestPacket.kt
@@ -1,5 +1,6 @@
 package net.mamoe.mirai.qqandroid.network.protocol.jce
 
+import net.mamoe.mirai.qqandroid.network.io.JceInput
 import net.mamoe.mirai.qqandroid.network.io.JceOutput
 import net.mamoe.mirai.qqandroid.network.io.JceStruct
 
@@ -17,6 +18,12 @@ class RequestPacket(
     val sServantName: String = "",
     val status: Map<String, String> = EMPTY_MAP
 ) : JceStruct() {
+    companion object : Factory<RequestPacket> {
+        override fun newInstanceFrom(input: JceInput): RequestPacket {
+            TODO("not implemented")
+        }
+    }
+
     override fun writeTo(builder: JceOutput) {
         builder.write(this.iVersion, 1)
         builder.write(this.cPacketType, 2)
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/jce/SvcReqRegister.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/jce/SvcReqRegister.kt
index 3bea4715f..31d1e8670 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/jce/SvcReqRegister.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/jce/SvcReqRegister.kt
@@ -1,5 +1,6 @@
 package net.mamoe.mirai.qqandroid.network.protocol.jce
 
+import net.mamoe.mirai.qqandroid.network.io.JceInput
 import net.mamoe.mirai.qqandroid.network.io.JceOutput
 import net.mamoe.mirai.qqandroid.network.io.JceStruct
 
@@ -41,7 +42,12 @@ class SvcReqRegister(
     val vecGuid: ByteArray? = null,
     val vecServerBuf: ByteArray? = null
 ) : JceStruct() {
-    
+    companion object : Factory<RequestPacket> {
+        override fun newInstanceFrom(input: JceInput): RequestPacket {
+            TODO("not implemented")
+        }
+    }
+
     override fun writeTo(builder: JceOutput) {
         builder.write(lUin, 0)
         builder.write(lBid, 1)
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt
index f095710a9..54ed76d7c 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt
@@ -8,6 +8,7 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.login.LoginPacket
 import net.mamoe.mirai.qqandroid.network.protocol.packet.login.NullPacketId
 import net.mamoe.mirai.qqandroid.network.protocol.packet.login.NullPacketId.commandName
 import net.mamoe.mirai.qqandroid.network.protocol.packet.login.PacketId
+import net.mamoe.mirai.qqandroid.network.protocol.packet.login.SvcReqRegisterPacket
 import net.mamoe.mirai.utils.cryptor.adjustToPublicKey
 import net.mamoe.mirai.utils.cryptor.decryptBy
 import net.mamoe.mirai.utils.io.*
@@ -48,7 +49,8 @@ internal typealias PacketConsumer = suspend (packet: Packet, packetId: PacketId,
 
 @UseExperimental(ExperimentalUnsignedTypes::class)
 internal object KnownPacketFactories : List<PacketFactory<*>> by mutableListOf(
-    LoginPacket
+    LoginPacket,
+    SvcReqRegisterPacket
 ) {
 
     fun findPacketFactory(commandName: String): PacketFactory<*> = this.first { it.id.commandName == commandName }

From a17f000052b841d895d6a6734d82615621b87147 Mon Sep 17 00:00:00 2001
From: Him188 <Him188@mamoe.net>
Date: Tue, 21 Jan 2020 14:49:42 +0800
Subject: [PATCH 12/14] f**k android

---
 gradle/android.gradle                 |  8 ++++++++
 mirai-core-qqandroid/build.gradle.kts |  9 +--------
 mirai-core-timpc/build.gradle.kts     | 10 +---------
 mirai-core/build.gradle.kts           | 10 +---------
 4 files changed, 11 insertions(+), 26 deletions(-)
 create mode 100644 gradle/android.gradle

diff --git a/gradle/android.gradle b/gradle/android.gradle
new file mode 100644
index 000000000..731a77753
--- /dev/null
+++ b/gradle/android.gradle
@@ -0,0 +1,8 @@
+apply plugin: "com.android.library"
+
+android {
+    compileSdkVersion(29)
+    defaultConfig {
+        minSdkVersion(15)
+    }
+}
\ No newline at end of file
diff --git a/mirai-core-qqandroid/build.gradle.kts b/mirai-core-qqandroid/build.gradle.kts
index 2eab231a9..8ae9a0733 100644
--- a/mirai-core-qqandroid/build.gradle.kts
+++ b/mirai-core-qqandroid/build.gradle.kts
@@ -3,7 +3,6 @@
 plugins {
     kotlin("multiplatform")
     id("kotlinx-atomicfu")
-    id("com.android.library")
     id("kotlinx-serialization")
     `maven-publish`
     id("com.jfrog.bintray") version "1.8.4-jetbrains-3" // DO NOT CHANGE THIS VERSION UNLESS YOU WANT TO WASTE YOUR TIME
@@ -32,15 +31,9 @@ version = rootProject.ext.get("mirai_version")!!.toString()
 
 val isAndroidSDKAvailable: Boolean by project
 
-android {
-    compileSdkVersion(29)
-    defaultConfig {
-        minSdkVersion(15)
-    }
-}
-
 kotlin {
     if (isAndroidSDKAvailable) {
+        apply(from = rootProject.file("gradle/android.gradle"))
         android("android") {
             publishAllLibraryVariants()
         }
diff --git a/mirai-core-timpc/build.gradle.kts b/mirai-core-timpc/build.gradle.kts
index 2ab19aa9f..3f28b9755 100644
--- a/mirai-core-timpc/build.gradle.kts
+++ b/mirai-core-timpc/build.gradle.kts
@@ -3,7 +3,6 @@
 plugins {
     kotlin("multiplatform")
     id("kotlinx-atomicfu")
-    id("com.android.library")
     id("kotlinx-serialization")
     `maven-publish`
     id("com.jfrog.bintray") version "1.8.4-jetbrains-3" // DO NOT CHANGE THIS VERSION UNLESS YOU WANT TO WASTE YOUR TIME
@@ -32,18 +31,11 @@ version = rootProject.ext.get("mirai_version")!!.toString()
 
 val isAndroidSDKAvailable: Boolean by project
 
-android {
-    compileSdkVersion(29)
-    defaultConfig {
-        minSdkVersion(15)
-    }
-}
-
 kotlin {
     if (isAndroidSDKAvailable) {
+        apply(from = rootProject.file("gradle/android.gradle"))
         android("android") {
             publishAllLibraryVariants()
-            project.apply(plugin = "com.android.library")
         }
     } else {
         println(
diff --git a/mirai-core/build.gradle.kts b/mirai-core/build.gradle.kts
index cb327ba39..e8a073c18 100644
--- a/mirai-core/build.gradle.kts
+++ b/mirai-core/build.gradle.kts
@@ -3,7 +3,6 @@
 plugins {
     kotlin("multiplatform")
     id("kotlinx-atomicfu")
-    id("com.android.library")
     id("kotlinx-serialization")
     `maven-publish`
     id("com.jfrog.bintray") version "1.8.4-jetbrains-3" // DO NOT CHANGE THIS VERSION UNLESS YOU WANT TO WASTE YOUR TIME
@@ -31,16 +30,9 @@ description = "QQ protocol library"
 
 val isAndroidSDKAvailable: Boolean by project
 
-android {
-    compileSdkVersion(29)
-    defaultConfig {
-        minSdkVersion(15)
-    }
-}
-
 kotlin {
     if (isAndroidSDKAvailable) {
-        project.apply(plugin = "com.android.library")
+        apply(from = rootProject.file("gradle/android.gradle"))
         android("android") {
             publishAllLibraryVariants()
         }

From 1de776f09219003ee5487c5d1da60186530d6793 Mon Sep 17 00:00:00 2001
From: Him188 <Him188@mamoe.net>
Date: Tue, 21 Jan 2020 15:15:11 +0800
Subject: [PATCH 13/14] Add source project reference

---
 .../src/commonMain/kotlin/net.mamoe.mirai/utils/io/Varint.kt    | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/Varint.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/Varint.kt
index f67c52a75..f3c656ca1 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/Varint.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/Varint.kt
@@ -14,6 +14,8 @@ import kotlin.jvm.JvmSynthetic
  *
  * Some code from http://wiki.vg/Protocol.
  *
+ * Source project: [Nukkit](http://github.com/nukkit/nukkit)
+ *
  * @author MagicDroidX of Nukkit Project
  * @author lmlstarqaq of Nukkit Project
  */

From 9353a5af7b0c61ed9e20dd32a16db7627ab6db7c Mon Sep 17 00:00:00 2001
From: Him188 <Him188@mamoe.net>
Date: Tue, 21 Jan 2020 21:30:43 +0800
Subject: [PATCH 14/14] QQA Debugging update

---
 .../mirai/qqandroid/network/io/JceInput.kt    |  28 +++-
 .../mirai/qqandroid/network/io/JceOutput.kt   |  22 +++-
 .../network/protocol/jce/RequestPacket.kt     |  60 +++++++--
 .../qqandroid/network/protocol/jce/uni.kt     |   4 +-
 .../network/protocol/packet/PacketFactory.kt  |   2 +
 .../packet/login/SvcReqRegisterPacket.kt      | 121 ++++++++----------
 .../mirai/qqandroid/utils/SystemDeviceInfo.kt |   4 +-
 7 files changed, 152 insertions(+), 89 deletions(-)

diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/io/JceInput.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/io/JceInput.kt
index cabecf645..2f931e376 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/io/JceInput.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/io/JceInput.kt
@@ -29,6 +29,22 @@ class JceInput(
         return JceHead(tag = tag, type = type)
     }
 
+    fun read(default: Byte, tag: Int): Byte = readByteOrNull(tag) ?: default
+    fun read(default: Short, tag: Int): Short = readShortOrNull(tag) ?: default
+    fun read(default: Int, tag: Int): Int = readIntOrNull(tag) ?: default
+    fun read(default: Long, tag: Int): Long = readLongOrNull(tag) ?: default
+    fun read(default: Float, tag: Int): Float = readFloatOrNull(tag) ?: default
+    fun read(default: Double, tag: Int): Double = readDoubleOrNull(tag) ?: default
+    fun read(default: Boolean, tag: Int): Boolean = readBooleanOrNull(tag) ?: default
+
+    fun read(default: ByteArray, tag: Int): ByteArray = readByteArrayOrNull(tag) ?: default
+    fun read(default: ShortArray, tag: Int): ShortArray = readShortArrayOrNull(tag) ?: default
+    fun read(default: IntArray, tag: Int): IntArray = readIntArrayOrNull(tag) ?: default
+    fun read(default: LongArray, tag: Int): LongArray = readLongArrayOrNull(tag) ?: default
+    fun read(default: FloatArray, tag: Int): FloatArray = readFloatArrayOrNull(tag) ?: default
+    fun read(default: DoubleArray, tag: Int): DoubleArray = readDoubleArrayOrNull(tag) ?: default
+    fun read(default: BooleanArray, tag: Int): BooleanArray = readBooleanArrayOrNull(tag) ?: default
+
     fun readBoolean(tag: Int): Boolean = readBooleanOrNull(tag) ?: error("cannot find tag $tag")
     fun readByte(tag: Int): Byte = readByteOrNull(tag) ?: error("cannot find tag $tag")
     fun readShort(tag: Int): Short = readShortOrNull(tag) ?: error("cannot find tag $tag")
@@ -176,7 +192,7 @@ class JceInput(
         }
     }
 
-    fun <T : Map<K, V>, K, V> readMapOrNull(defaultKey: K, defaultValue: V, tag: Int): Map<K, V>? = skipToTagOrNull(tag) {
+    fun <K, V> readMapOrNull(defaultKey: K, defaultValue: V, tag: Int): Map<K, V>? = skipToTagOrNull(tag) {
         check(it.type.toInt() == 8) { "type mismatch" }
         val size = readInt(0)
         val map = HashMap<K, V>(size)
@@ -186,6 +202,16 @@ class JceInput(
         return map
     }
 
+    inline fun <reified K, reified V> readMapOrNull(tag: Int): Map<K, V>? = skipToTagOrNull(tag) {
+        check(it.type.toInt() == 8) { "type mismatch" }
+        val size = readInt(0)
+        val map = HashMap<K, V>(size)
+        repeat(size) {
+            map[readSimpleObject(0)] = readSimpleObject(0)
+        }
+        return map
+    }
+
     fun <T> readListOrNull(defaultElement: T, tag: Int): List<T>? = skipToTagOrNull(tag) { head ->
         check(head.type.toInt() == 9) { "type mismatch" }
         val size = readInt(0)
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/io/JceOutput.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/io/JceOutput.kt
index ba352b960..9357a05a9 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/io/JceOutput.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/io/JceOutput.kt
@@ -2,21 +2,31 @@ package net.mamoe.mirai.qqandroid.network.io
 
 import kotlinx.io.charsets.Charset
 import kotlinx.io.core.*
-import kotlinx.io.pool.useInstance
-import net.mamoe.mirai.utils.io.ByteArrayPool
-import net.mamoe.mirai.utils.io.readRemainingBytes
 import kotlin.reflect.KClass
 
-private val CharsetGBK = Charset.forName("GBK")
+@PublishedApi
+internal val CharsetGBK = Charset.forName("GBK")
 
-fun buildJcePacket(stringCharset: Charset = CharsetGBK, block: JceOutput.() -> Unit): ByteReadPacket {
+inline fun buildJcePacket(stringCharset: Charset = CharsetGBK, block: JceOutput.() -> Unit): ByteReadPacket {
     return JceOutput(stringCharset).apply(block).build()
 }
 
-fun BytePacketBuilder.writeJcePacket(stringCharset: Charset = CharsetGBK, block: JceOutput.() -> Unit) {
+inline fun BytePacketBuilder.writeJcePacket(stringCharset: Charset = CharsetGBK, block: JceOutput.() -> Unit) {
     return this.writePacket(buildJcePacket(stringCharset, block))
 }
 
+fun jceStruct(tag: Int, struct: JceStruct): ByteArray{
+    return buildJcePacket {
+        writeJceStruct(struct, tag)
+    }.readBytes()
+}
+
+fun <K, V> jceMap(tag: Int, vararg entries: Pair<K, V>): ByteArray {
+    return buildJcePacket {
+        writeMap(mapOf(*entries), tag)
+    }.readBytes()
+}
+
 /**
  *
  * From: com.qq.taf.jce.JceOutputStream
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/jce/RequestPacket.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/jce/RequestPacket.kt
index f34ad0887..649eae846 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/jce/RequestPacket.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/jce/RequestPacket.kt
@@ -6,21 +6,55 @@ import net.mamoe.mirai.qqandroid.network.io.JceStruct
 
 private val EMPTY_MAP = mapOf<String, String>()
 
-class RequestPacket(
-    val sBuffer: ByteArray,
-    val cPacketType: Byte = 0,
-    val iMessageType: Int = 0,
-    val iRequestId: Int = 0,
-    val iTimeout: Int = 3000,
-    val iVersion: Short = 3,
-    val context: Map<String, String> = EMPTY_MAP,
-    val sFuncName: String = "",
-    val sServantName: String = "",
-    val status: Map<String, String> = EMPTY_MAP
-) : JceStruct() {
+class RequestPacket() : JceStruct() {
+    lateinit var sBuffer: ByteArray
+    var cPacketType: Byte = 0
+    var iMessageType: Int = 0
+    var iRequestId: Int = 0
+    var iTimeout: Int = 3000
+    var iVersion: Short = 3
+    var context: Map<String, String> = EMPTY_MAP
+    var sFuncName: String = ""
+    var sServantName: String = ""
+    var status: Map<String, String> = EMPTY_MAP
+
+    constructor(
+        sBuffer: ByteArray,
+        cPacketType: Byte = 0,
+        iMessageType: Int = 0,
+        iRequestId: Int = 0,
+        iTimeout: Int = 3000,
+        iVersion: Short = 3,
+        context: Map<String, String> = EMPTY_MAP,
+        sFuncName: String = "",
+        sServantName: String = "",
+        status: Map<String, String> = EMPTY_MAP
+    ) : this() {
+        this.sBuffer = sBuffer
+        this.cPacketType = cPacketType
+        this.iMessageType = iMessageType
+        this.iRequestId = iRequestId
+        this.iTimeout = iTimeout
+        this.iVersion = iVersion
+        this.context = context
+        this.sFuncName = sFuncName
+        this.sServantName = sServantName
+        this.status = status
+    }
+
     companion object : Factory<RequestPacket> {
         override fun newInstanceFrom(input: JceInput): RequestPacket {
-            TODO("not implemented")
+            val iVersion = input.readShort(1)
+            val cPacketType = input.readByte(2)
+            val iMessageType = input.readInt(3)
+            val iRequestId = input.readInt(4)
+            val sServantName = input.readString(5)
+            val sFuncName = input.readString(6)
+            val sBuffer = input.readByteArray(7)
+            val iTimeout = input.readInt(8)
+            val context = input.readMap("", "", 9)
+            val status = input.readMap("", "", 10)
+            return RequestPacket(sBuffer, cPacketType, iMessageType, iRequestId, iTimeout, iVersion, context, sFuncName, sServantName, status)
         }
     }
 
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/jce/uni.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/jce/uni.kt
index d68d3fb42..80d671141 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/jce/uni.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/jce/uni.kt
@@ -5,8 +5,8 @@ import net.mamoe.mirai.qqandroid.network.io.JceOutput
 import net.mamoe.mirai.qqandroid.network.io.buildJcePacket
 import net.mamoe.mirai.qqandroid.network.io.writeJcePacket
 
-fun BytePacketBuilder.writeUniRequestPacket(requestPacket: RequestPacket) {
+inline fun BytePacketBuilder.writeUniRequestPacket(requestPacket: RequestPacket.() -> Unit) {
     writeJcePacket {
-        requestPacket.writeTo(this)
+        RequestPacket().apply(requestPacket).writeTo(this)
     }
 }
\ No newline at end of file
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt
index 54ed76d7c..e6f8553ae 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt
@@ -81,6 +81,8 @@ internal object KnownPacketFactories : List<PacketFactory<*>> by mutableListOf(
                     }
                     else -> error("Illegal flag2. Expected 0x02, got $flag2")
                 }
+                // 00 00 00 60 00 00 00 0B 02 00 00 00 00 0E 31 39 39 34 37 30 31 30 32 31 CE 35 53 19 84 A8 1A B8 5B 48 E3 7C D0 A6 BA 58 6A EB CE 50 B9 A0 98 D5 B9 D0 1C 72 E2 86 24 FC 55 44 6C 6E E3 F9 15 6C EC 6C 6B 94 40 F7 B4 45 CF B4 D0 79 84 FE 30 EA 98 84 44 84 02 32 70 DD D7 07 07 72 DE 87 59 AC
+                0x0B ->
                 else -> error("Illegal flag1. Expected 0x0A or 0x0B, got $flag1")
             }
         }
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/SvcReqRegisterPacket.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/SvcReqRegisterPacket.kt
index ee3e17b3f..04dc823e9 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/SvcReqRegisterPacket.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/SvcReqRegisterPacket.kt
@@ -1,13 +1,12 @@
 package net.mamoe.mirai.qqandroid.network.protocol.packet.login
 
 import kotlinx.io.core.ByteReadPacket
-import kotlinx.io.core.readBytes
 import kotlinx.serialization.protobuf.ProtoBuf
 import net.mamoe.mirai.data.Packet
 import net.mamoe.mirai.qqandroid.QQAndroidBot
 import net.mamoe.mirai.qqandroid.network.QQAndroidClient
-import net.mamoe.mirai.qqandroid.network.io.buildJcePacket
-import net.mamoe.mirai.qqandroid.network.protocol.jce.RequestPacket
+import net.mamoe.mirai.qqandroid.network.io.jceMap
+import net.mamoe.mirai.qqandroid.network.io.jceStruct
 import net.mamoe.mirai.qqandroid.network.protocol.jce.SvcReqRegister
 import net.mamoe.mirai.qqandroid.network.protocol.jce.writeUniRequestPacket
 import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket
@@ -38,73 +37,63 @@ internal object SvcReqRegisterPacket : PacketFactory<SvcReqRegisterPacket.Respon
     operator fun invoke(
         client: QQAndroidClient,
         regPushReason: RegPushReason = RegPushReason.setOnlineStatus
-    ): OutgoingPacket = buildOutgingPacket(client, key = client.wLoginSigInfo.wtSessionTicketKey) {
-        writeUniRequestPacket(
-            RequestPacket(
-                sServantName = "PushService",
-                sFuncName = "SvcReqRegister",
-                sBuffer = buildJcePacket {
-                    writeMap(
-                        mapOf(
-                            "SvcReqRegister" to buildJcePacket {
-                                writeJceStruct(
-                                    SvcReqRegister(
-                                        cConnType = 0,
-                                        lBid = 1 or 2 or 4,
-                                        lUin = client.uin,
-                                        iStatus = client.onlineStatus.id,
-                                        bKikPC = 0, // 是否把 PC 踢下线
-                                        bKikWeak = 0,
-                                        timeStamp = currentTimeSeconds, // millis or seconds??
-                                        iLargeSeq = 0,
-                                        bRegType =
-                                        if (regPushReason == RegPushReason.appRegister ||
-                                            regPushReason == RegPushReason.fillRegProxy ||
-                                            regPushReason == RegPushReason.createDefaultRegInfo ||
-                                            regPushReason == RegPushReason.setOnlineStatus
-                                        ) {
-                                            0
-                                        } else {
-                                            1
-                                        }.toByte(),
-                                        bIsSetStatus = if (regPushReason == RegPushReason.setOnlineStatus) 1 else 0,
-                                        iOSVersion = client.device.version.sdk.toLong(),
-                                        cNetType = if (client.networkType == NetworkType.WIFI) 1 else 0,
-                                        vecGuid = client.device.guid,
-                                        strDevName = client.device.model.encodeToString(),
-                                        strDevType = client.device.model.encodeToString(),
-                                        strOSVer = client.device.version.release.encodeToString(),
+    ): OutgoingPacket = buildOutgingPacket(client, key = client.wLoginSigInfo.d2Key) {
+        writeUniRequestPacket {
+            sServantName = "PushService"
+            sFuncName = "SvcReqRegister"
+            sBuffer = jceMap(
+                0,
+                "SvcReqRegister" to jceStruct(
+                    0,
+                    SvcReqRegister(
+                        cConnType = 0,
+                        lBid = 1 or 2 or 4,
+                        lUin = client.uin,
+                        iStatus = client.onlineStatus.id,
+                        bKikPC = 0, // 是否把 PC 踢下线
+                        bKikWeak = 0,
+                        timeStamp = currentTimeSeconds, // millis or seconds??
+                        iLargeSeq = 0,
+                        bRegType =
+                        (if (regPushReason == RegPushReason.appRegister ||
+                            regPushReason == RegPushReason.fillRegProxy ||
+                            regPushReason == RegPushReason.createDefaultRegInfo ||
+                            regPushReason == RegPushReason.setOnlineStatus
+                        ) 0 else 1).toByte(),
+                        bIsSetStatus = if (regPushReason == RegPushReason.setOnlineStatus) 1 else 0,
+                        iOSVersion = client.device.version.sdk.toLong(),
+                        cNetType = if (client.networkType == NetworkType.WIFI) 1 else 0,
+                        vecGuid = client.device.guid,
+                        strDevName = client.device.model.encodeToString(),
+                        strDevType = client.device.model.encodeToString(),
+                        strOSVer = client.device.version.release.encodeToString(),
 
-                                        // register 时还需要
-                                        /*
-                                        var44.uNewSSOIp = field_127445;
-                                        var44.uOldSSOIp = field_127444;
-                                        var44.strVendorName = ROMUtil.getRomName();
-                                        var44.strVendorOSName = ROMUtil.getRomVersion(20);
-                                        */
-                                        bytes_0x769_reqbody = ProtoBuf.dump(
-                                            Oidb0x769.RequestBody.serializer(), Oidb0x769.RequestBody(
-                                                rpt_config_list = listOf(
-                                                    Oidb0x769.ConfigSeq(
-                                                        type = 46,
-                                                        version = 4
-                                                    ),
-                                                    Oidb0x769.ConfigSeq(
-                                                        type = 283,
-                                                        version = 0
-                                                    )
-                                                )
-                                            )
-                                        ),
-                                        bSetMute = 0
-                                    ), 0
+                        // register 时还需要
+                        /*
+                        var44.uNewSSOIp = field_127445;
+                        var44.uOldSSOIp = field_127444;
+                        var44.strVendorName = ROMUtil.getRomName();
+                        var44.strVendorOSName = ROMUtil.getRomVersion(20);
+                        */
+                        bytes_0x769_reqbody = ProtoBuf.dump(
+                            Oidb0x769.RequestBody.serializer(), Oidb0x769.RequestBody(
+                                rpt_config_list = listOf(
+                                    Oidb0x769.ConfigSeq(
+                                        type = 46,
+                                        version = 4
+                                    ),
+                                    Oidb0x769.ConfigSeq(
+                                        type = 283,
+                                        version = 0
+                                    )
                                 )
-                            }.readBytes()
-                        ), 0
+                            )
+                        ),
+                        bSetMute = 0
                     )
-                }.readBytes()
+                )
             )
-        )
+        }
     }
 
     override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): Response {
diff --git a/mirai-core-qqandroid/src/jvmMain/kotlin/net/mamoe/mirai/qqandroid/utils/SystemDeviceInfo.kt b/mirai-core-qqandroid/src/jvmMain/kotlin/net/mamoe/mirai/qqandroid/utils/SystemDeviceInfo.kt
index 8716e2b8c..e941522e9 100644
--- a/mirai-core-qqandroid/src/jvmMain/kotlin/net/mamoe/mirai/qqandroid/utils/SystemDeviceInfo.kt
+++ b/mirai-core-qqandroid/src/jvmMain/kotlin/net/mamoe/mirai/qqandroid/utils/SystemDeviceInfo.kt
@@ -27,7 +27,9 @@ actual class SystemDeviceInfo actual constructor(context: Context) : DeviceInfo(
     override val imsiMd5: ByteArray
         get() = ubyteArrayOf(0xD4u, 0x1Du, 0x8Cu, 0xD9u, 0x8Fu, 0x00u, 0xB2u, 0x04u, 0xE9u, 0x80u, 0x09u, 0x98u, 0xECu, 0xF8u, 0x42u, 0x7Eu).toByteArray()
     override val imei: String get() = "858414369211993"
-    override val ipAddress: ByteArray get() = localIpAddress().split(".").map { it.toByte() }.takeIf { it.size == 4 }?.toByteArray() ?: byteArrayOf()
+    @UseExperimental(ExperimentalUnsignedTypes::class)
+    override val ipAddress: ByteArray
+        get() = localIpAddress().split(".").map { it.toUByte().toByte() }.takeIf { it.size == 4 }?.toByteArray() ?: byteArrayOf()
     override val androidId: ByteArray get() = "QSR1.190920.001".toByteArray()
     override val apn: ByteArray get() = "wifi".toByteArray()