From 88add06f8e37978a6c2bc29aa6804f5598b47f09 Mon Sep 17 00:00:00 2001
From: Him188 <Him188@mamoe.net>
Date: Mon, 30 Dec 2019 16:31:47 +0800
Subject: [PATCH] Add Long.toByteArray

---
 .../qqandroid/network/protocol/packet/Tlv.kt  | 629 ++++++++++++++++++
 .../network/protocol/packet/TouchPacket.kt    |  27 -
 .../protocol/packet/login/LoginPacket.kt      | 138 +++-
 .../network/protocol/packet/tlv/Tlv.kt        | 401 -----------
 .../utils/io/TypeConversion.kt                |  14 +
 5 files changed, 767 insertions(+), 442 deletions(-)
 create mode 100644 mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/Tlv.kt
 delete mode 100644 mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/TouchPacket.kt
 delete mode 100644 mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/tlv/Tlv.kt

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
new file mode 100644
index 000000000..8f61cf347
--- /dev/null
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/Tlv.kt
@@ -0,0 +1,629 @@
+package net.mamoe.mirai.qqandroid.network.protocol.packet
+
+import kotlinx.io.core.BytePacketBuilder
+import kotlinx.io.core.ByteReadPacket
+import kotlinx.io.core.toByteArray
+import kotlinx.io.core.writeFully
+import net.mamoe.mirai.utils.currentTime
+import net.mamoe.mirai.utils.io.*
+import net.mamoe.mirai.utils.md5
+import kotlin.random.Random
+
+inline class LoginType(
+    val value: Int
+) {
+    companion object {
+        /**
+         * 短信验证登录
+         */
+        val SMS = LoginType(3)
+        /**
+         * 密码登录
+         */
+        val PASSWORD = LoginType(1)
+        /**
+         * 微信一键登录
+         */
+        val WE_CHAT = LoginType(4)
+    }
+}
+
+@Suppress("MemberVisibilityCanBePrivate")
+fun BytePacketBuilder.t1(uin: Long, ip: ByteArray) {
+    require(ip.size == 4)
+    writeShort(0x1)
+    writeShortLVPacket {
+        writeShort(1) // _ip_ver
+        writeInt(Random.nextInt())
+        writeInt(uin.toInt())
+        writeTime()
+        writeFully(ip)
+        writeShort(0)
+    } shouldEqualsTo 20
+}
+
+fun BytePacketBuilder.t2(captchaCode: String, captchaToken: ByteArray, sigVer: Short = 0) {
+    writeShort(0x2)
+    writeShortLVPacket {
+        writeShort(sigVer)
+        writeShortLVString(captchaCode)
+        writeShortLVByteArray(captchaToken)
+    }
+}
+
+fun BytePacketBuilder.t8(
+    localId: Int = 2052
+) {
+    writeShort(0x8)
+    writeShortLVPacket {
+        writeShort(0)
+        writeInt(localId) // localId
+        writeShort(0)
+    }
+}
+
+fun BytePacketBuilder.t18(
+    appId: Long,
+    appClientVersion: Int,
+    uin: Long,
+    constant1_always_0: Int = 0
+) {
+    writeShort(0x18)
+    writeShortLVPacket {
+        writeShort(1) //_ping_version
+        writeInt(1536) //_sso_version
+        writeInt(appId.toInt())
+        writeInt(appClientVersion)
+        writeInt(uin.toInt())
+        writeShort(constant1_always_0.toShort())
+        writeShort(0)
+    } shouldEqualsTo 22
+}
+
+fun BytePacketBuilder.t106(
+    appId: Long,
+    subAppId: Long,
+    appClientVersion: Int,
+    uin: Long,
+    ipAddress: ByteArray,
+    n5_always_1: Int = 1,
+    passwordMd5: ByteArray,
+    salt: Long,
+    uinAccount: ByteArray,
+    tgtgtKey: ByteArray,
+    isGuidAvailable: Boolean = true,
+    guid: ByteArray?, // notnull if isGuidAvailable
+    loginType: LoginType
+) {
+    writeShort(0x106)
+
+    writeShortLVPacket {
+        encryptAndWrite(md5(passwordMd5 + (salt.takeIf { it != 0L } ?: uin).toInt().toByteArray())) {
+            writeShort(4)//TGTGTVer
+            writeInt(Random.nextInt())
+            writeInt(5)//ssoVer
+            writeInt(appId.toInt())
+            writeInt(appClientVersion)
+
+            if (uin == 0L) {
+                writeLong(salt)
+            } else {
+                writeLong(uin)
+            }
+
+            writeTime()
+            writeFully(ipAddress)
+            writeByte(n5_always_1.toByte())
+            writeFully(passwordMd5)
+            writeFully(tgtgtKey)
+            writeInt(0)
+            writeByte(isGuidAvailable.toByte())
+            if (isGuidAvailable) {
+                require(guid != null) { "Guid must not be null when isGuidAvailable==true" }
+            }
+            if (guid == null) {
+                repeat(4) {
+                    writeInt(Random.nextInt())
+                }
+            } else {
+                writeFully(guid)
+            }
+            writeInt(subAppId.toInt())
+            writeInt(loginType.value)
+            writeShortLVByteArray(uinAccount)
+        }
+    } shouldEqualsTo 98
+}
+
+fun BytePacketBuilder.t116(
+    miscBitmap: Int,
+    subSigMap: Int,
+    appIdList: LongArray
+) {
+    writeShort(0x116)
+    writeShortLVPacket {
+        writeByte(0) // _ver
+        writeInt(miscBitmap)
+        writeInt(subSigMap)
+        writeByte(appIdList.size.toByte())
+        appIdList.forEach {
+            writeInt(it.toInt())
+        }
+    }
+}
+
+fun BytePacketBuilder.t100(
+    appId: Long,
+    subAppId: Long,
+    appClientVersion: Int,
+    sigMap: Int
+) {
+    writeShort(0x100)
+    writeShortLVPacket {
+        writeShort(1)//db_buf_ver
+        writeInt(5)//sso_ver
+        writeInt(appId.toInt())
+        writeInt(subAppId.toInt())
+        writeInt(appClientVersion)
+        writeInt(sigMap)
+    } shouldEqualsTo 22
+}
+
+fun BytePacketBuilder.t107(
+    picType: Int,
+    const1_always_0: Int = 0,
+    const2_always_0: Int = 0,
+    const3_always_1: Int = 1
+) {
+    writeShort(0x107)
+    writeShortLVPacket {
+        writeShort(picType.toShort())
+        writeByte(const1_always_0.toByte())
+        writeShort(const2_always_0.toShort())
+        writeByte(const3_always_1.toByte())
+    } shouldEqualsTo 6
+}
+
+fun BytePacketBuilder.t108(
+    ksid: ByteArray
+) {
+    writeShort(0x108)
+    writeShortLVPacket {
+        writeFully(ksid)
+    }
+}
+
+fun BytePacketBuilder.t104(
+    t104Data: ByteArray
+) {
+    writeShort(0x104)
+    writeShortLVPacket {
+        writeFully(t104Data)
+    }
+}
+
+/**
+ * @param apkId application.getPackageName().getBytes()
+ */
+fun BytePacketBuilder.t142(
+    apkId: ByteArray
+) {
+    writeShort(0x142)
+    writeShortLVPacket {
+        writeShort(0) //_version
+        writeShortLVByteArrayLimitedLength(apkId, 32)
+    }
+}
+
+fun BytePacketBuilder.t112(
+    nonNumberUin: ByteArray
+) {
+    writeShort(0x112)
+    writeShortLVPacket {
+        writeFully(nonNumberUin)
+    }
+}
+
+fun BytePacketBuilder.t144(
+    // t109
+    androidId: ByteArray,
+
+    // t52d
+    androidDevInfo: ByteArray,
+
+    // t124
+    osType: ByteArray = "android".toByteArray(),
+    osVersion: ByteArray,
+    ipv6NetType: Int,
+    simInfo: ByteArray,
+    unknown: ByteArray,
+    apn: ByteArray = "wifi".toByteArray(),
+
+    // t128
+    isGuidFromFileNull: Boolean = false,
+    isGuidAvailable: Boolean = true,
+    isGuidChanged: Boolean = false,
+    guidFlag: Long,
+    buildModel: ByteArray,
+    guid: ByteArray,
+    buildBrand: ByteArray,
+
+    // encrypt
+    tgtgtKey: ByteArray
+) {
+    writeShort(0x144)
+    writeShortLVPacket {
+        encryptAndWrite(tgtgtKey) {
+            t109(androidId)
+            t52d(androidDevInfo)
+            t124(osType, osVersion, ipv6NetType, simInfo, unknown, apn)
+            t128(isGuidFromFileNull, isGuidAvailable, isGuidChanged, guidFlag, buildModel, guid, buildBrand)
+            t16e(buildModel)
+        }
+    }
+}
+
+fun BytePacketBuilder.t109(
+    androidId: ByteArray
+) {
+    writeShort(0x109)
+    writeShortLVPacket {
+        writeFully(androidId)
+    }
+}
+
+fun BytePacketBuilder.t52d(
+    androidDevInfo: ByteArray // oicq.wlogin_sdk.tools.util#get_android_dev_info
+) {
+    writeShort(0x52d)
+    writeShortLVPacket {
+        writeFully(androidDevInfo)
+    }
+}
+
+fun BytePacketBuilder.t124(
+    osType: ByteArray = "android".toByteArray(),
+    osVersion: ByteArray, // Build.VERSION.RELEASE.toByteArray()
+    ipv6NetType: Int,  //oicq.wlogin_sdk.tools.util#get_network_type
+    simInfo: ByteArray, // oicq.wlogin_sdk.tools.util#get_sim_operator_name
+    unknown: ByteArray,
+    apn: ByteArray = "wifi".toByteArray() // oicq.wlogin_sdk.tools.util#get_apn_string
+) {
+    writeShort(0x124)
+    writeShortLVPacket {
+        writeShortLVByteArrayLimitedLength(osType, 16)
+        writeShortLVByteArrayLimitedLength(osVersion, 16)
+        writeShort(ipv6NetType.toShort())
+        writeShortLVByteArrayLimitedLength(simInfo, 16)
+        writeShortLVByteArrayLimitedLength(unknown, 32)
+        writeShortLVByteArrayLimitedLength(apn, 16)
+    }
+}
+
+fun BytePacketBuilder.t128(
+    isGuidFromFileNull: Boolean = false, // 保存到文件的 GUID 是否为 null
+    isGuidAvailable: Boolean = true, // GUID 是否可用(计算/读取成功)
+    isGuidChanged: Boolean = false, // GUID 是否有变动
+    /**
+     * guidFlag:
+     * ```java
+     * GUID_FLAG = 0;
+     * GUID_FLAG |= GUID_SRC << 24 & 0xFF000000;
+     * GUID_FLAG |= FLAG_MAC_ANDROIDID_GUID_CHANGE << 8 & 0xFF00;
+     * ```
+     *
+     *
+     * GUID_SRC:
+     * 0: 初始值;
+     * 1: 以前保存的文件;
+     * 20: 以前没保存且现在生成失败;
+     * 17: 以前没保存但现在生成成功;
+     *
+     *
+     * FLAG_MAC_ANDROIDID_GUID_CHANGE:
+     * ```java
+     * if (!Arrays.equals(currentMac, get_last_mac)) {
+     *     oicq.wlogin_sdk.request.t.FLAG_MAC_ANDROIDID_GUID_CHANGEMENT |= 0x1;
+     * }
+     * if (!Arrays.equals(currentAndroidId, get_last_android_id)) {
+     *     oicq.wlogin_sdk.request.t.FLAG_MAC_ANDROIDID_GUID_CHANGEMENT |= 0x2;
+     * }
+     * if (!Arrays.equals(currentGuid, get_last_guid)) {
+     *     oicq.wlogin_sdk.request.t.FLAG_MAC_ANDROIDID_GUID_CHANGEMENT |= 0x4;
+     * }
+     * ```
+     */
+    guidFlag: Long,
+    buildModel: ByteArray,  // android.os.Build.MODEL
+    /**
+     * defaults `"%4;7t>;28<fc.5*6".toByteArray()`
+     */
+    guid: ByteArray,
+    buildBrand: ByteArray // android.os.Build.BRAND
+) {
+    writeShort(0x128)
+    writeShortLVPacket {
+        writeShort(0)
+        writeByte(isGuidFromFileNull.toByte())
+        writeByte(isGuidAvailable.toByte())
+        writeByte(isGuidChanged.toByte())
+        writeInt(guidFlag.toInt())
+        writeShortLVByteArrayLimitedLength(buildModel, 32)
+        writeShortLVByteArrayLimitedLength(guid, 16)
+        writeShortLVByteArrayLimitedLength(buildBrand, 16)
+    }
+}
+
+fun BytePacketBuilder.t16e(
+    buildModel: ByteArray
+) {
+    writeShort(0x16e)
+    writeShortLVPacket {
+        writeFully(buildModel)
+    }
+}
+
+fun BytePacketBuilder.t145(
+    guid: ByteArray
+) {
+    writeShort(0x145)
+    writeShortLVPacket {
+        writeFully(guid)
+    }
+}
+
+fun BytePacketBuilder.t147(
+    appId: Long,
+    apkVersionName: ByteArray,
+    apkSignatureMd5: ByteArray
+) {
+    writeShort(0x147)
+    writeShortLVPacket {
+        writeLong(appId)
+        writeShortLVByteArrayLimitedLength(apkVersionName, 32)
+        writeShortLVByteArrayLimitedLength(apkSignatureMd5, 32)
+    }
+}
+
+fun BytePacketBuilder.t166(
+    imageType: Int
+) {
+    writeShort(0x166)
+    writeShortLVPacket {
+        writeByte(imageType.toByte())
+    }
+}
+
+fun BytePacketBuilder.t16a(
+    noPicSig: ByteArray // unknown source
+) {
+    writeShort(0x16a)
+    writeShortLVPacket {
+        writeFully(noPicSig)
+    }
+}
+
+fun BytePacketBuilder.t154(
+    ssoSequenceId: Int // starts from 0
+) {
+    writeShort(0x154)
+    writeShortLVPacket {
+        writeInt(ssoSequenceId)
+    }
+}
+
+fun BytePacketBuilder.t141(
+    simInfo: ByteArray,
+    ipv6NetType: Int,
+    apn: ByteArray
+) {
+    writeShort(0x141)
+    writeShortLVPacket {
+        writeShort(1) // version
+        writeShortLVByteArray(simInfo)
+        writeShort(ipv6NetType.toShort())
+        writeShortLVByteArray(apn)
+    }
+}
+
+fun BytePacketBuilder.t511(
+    domains: List<String>
+) {
+    writeShort(0x511)
+    writeShortLVPacket {
+        val list = domains.filter { it.isNotEmpty() }
+        writeShort(list.size.toShort())
+        list.forEach { element ->
+            if (element.startsWith('(')) {
+                val split = element.drop(1).split(')')
+
+                val flag = split[0].toInt()
+                var n = (flag and 0x100000 > 0).toInt()
+                if (flag and 0x8000000 > 0) {
+                    n = n or 0x2
+                }
+                writeByte(n.toByte())
+
+                writeShortLVString(split[1])
+            } else {
+                writeByte(1)
+                writeShortLVString(element)
+            }
+        }
+    }
+}
+
+fun BytePacketBuilder.t172(
+    rollbackSig: ByteArray // 由服务器发来的 tlv_t172 获得
+) {
+    writeShort(0x172)
+    writeShortLVPacket {
+        writeFully(rollbackSig)
+    }
+}
+
+fun BytePacketBuilder.t185() {
+    writeShort(0x185)
+    writeShortLVPacket {
+        writeByte(1)
+        writeByte(1)
+    }
+}
+
+fun BytePacketBuilder.t400(
+    g: ByteArray, // 用于加密这个 tlv
+    uin: Long,
+    guid: ByteArray,
+    dpwd: ByteArray,
+    appId: Long,
+    subAppId: Long,
+    randomSeed: ByteArray
+) {
+    writeShort(0x400)
+    writeShortLVPacket {
+        writeByte(1) // version
+        writeLong(uin)
+
+        encryptAndWrite(g) {
+            writeFully(guid)
+            writeFully(dpwd)
+            writeInt(appId.toInt())
+            writeInt(subAppId.toInt())
+            writeLong(currentTime)
+            writeFully(randomSeed)
+        }
+    }
+}
+
+fun BytePacketBuilder.t187(
+    macAddress: ByteArray
+) {
+    writeShort(0x187)
+    writeShortLVPacket {
+        writeFully(macAddress)
+    }
+}
+
+fun BytePacketBuilder.t188(
+    androidId: ByteArray
+) {
+    writeShort(0x188)
+    writeShortLVPacket {
+        writeFully(androidId)
+    }
+}
+
+fun BytePacketBuilder.t194(
+    imsiMd5: ByteArray
+) {
+    writeShort(0x194)
+    writeShortLVPacket {
+        writeFully(imsiMd5)
+    }
+}
+
+fun BytePacketBuilder.t191(
+    K: Int = 0
+) {
+    writeShort(0x191)
+    writeShortLVPacket {
+        writeByte(K.toByte())
+    }
+}
+
+fun BytePacketBuilder.t201(
+    L: ByteArray = byteArrayOf(), // unknown
+    channelId: ByteArray = byteArrayOf(),
+    clientType: ByteArray = "qq".toByteArray(),
+    N: ByteArray
+) {
+    writeShort(0x201)
+    writeShortLVPacket {
+        writeShortLVByteArray(L)
+        writeShortLVByteArray(channelId)
+        writeShortLVByteArray(clientType)
+        writeShortLVByteArray(N)
+    }
+}
+
+fun BytePacketBuilder.t202(
+    wifiBSSID: ByteArray,
+    wifiSSID: ByteArray
+) {
+    writeShort(0x202)
+    writeShortLVPacket {
+        writeShortLVByteArrayLimitedLength(wifiBSSID, 16)
+        writeShortLVByteArrayLimitedLength(wifiSSID, 32)
+    }
+}
+
+fun BytePacketBuilder.t177(
+    unknown1: Long = 1571193922L,
+    unknown2: String = "6.0.0.2413"
+) {
+    writeShort(0x177)
+    writeShortLVPacket {
+        writeByte(1)
+        writeLong(unknown1)
+        writeShortLVString(unknown2)
+    }
+}
+
+fun BytePacketBuilder.t516( // 1302
+    sourceType: Int = 0 // always 0
+) {
+    writeShort(0x516)
+    writeShortLVPacket {
+        writeInt(sourceType)
+    }
+}
+
+fun BytePacketBuilder.t521( // 1313
+    productType: Int = 0, // coz setProductType is never used
+    unknown: Short = 0 // const
+) {
+    writeShort(0x521)
+    writeShortLVPacket {
+        writeInt(productType)
+        writeShort(unknown)
+    }
+}
+
+fun BytePacketBuilder.t536( // 1334
+    loginExtraData: ByteArray
+) {
+    writeShort(0x536)
+    writeShortLVPacket {
+        writeFully(loginExtraData)
+    }
+}
+
+fun BytePacketBuilder.t525(
+    t536: ByteReadPacket
+) {
+    writeShort(0x525)
+    writeShortLVPacket {
+        writeShort(1)
+        writePacket(t536)
+    }
+}
+
+fun BytePacketBuilder.t318(
+    tgtQR: ByteArray // unknown
+) {
+    writeShort(0x318)
+    writeShortLVPacket {
+        writeFully(tgtQR)
+    }
+}
+
+private fun Boolean.toByte(): Byte = if (this) 1 else 0
+private fun Boolean.toInt(): Int = if (this) 1 else 0
+
+private infix fun Int.shouldEqualsTo(int: Int) = require(this == int)
+
+fun randomAndroidId(): String = buildString(15) {
+    repeat(15) { append(Random.nextInt(10)) }
+}
+
+// AndroidDevInfo: oicq.wlogin_sdk.tools.util#get_android_dev_info
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/TouchPacket.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/TouchPacket.kt
deleted file mode 100644
index a21f2843d..000000000
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/TouchPacket.kt
+++ /dev/null
@@ -1,27 +0,0 @@
-package net.mamoe.mirai.qqandroid.network.protocol.packet
-
-import kotlinx.io.core.ByteReadPacket
-import net.mamoe.mirai.data.Packet
-import net.mamoe.mirai.network.BotNetworkHandler
-import net.mamoe.mirai.qqandroid.network.protocol.packet.login.PacketId
-import net.mamoe.mirai.utils.cryptor.DecrypterByteArray
-import net.mamoe.mirai.utils.cryptor.DecrypterType
-
-
-object TouchKey : DecrypterByteArray,
-    DecrypterType<TouchKey> {
-    override val value: ByteArray
-        get() = TODO("not implemented")
-}
-
-object TouchPacket : PacketFactory<TouchPacketResponse, TouchKey>(
-    TouchKey
-) {
-    @UseExperimental(ExperimentalUnsignedTypes::class)
-    override suspend fun ByteReadPacket.decode(id: PacketId, sequenceId: UShort, handler: BotNetworkHandler): TouchPacketResponse {
-        TODO("not implemented")
-    }
-}
-
-
-interface TouchPacketResponse : Packet
\ No newline at end of file
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 d3ce46b2b..19a9aa770 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
@@ -2,32 +2,142 @@ package net.mamoe.mirai.qqandroid.network.protocol.packet.login
 
 
 import kotlinx.io.core.ByteReadPacket
+import kotlinx.io.core.buildPacket
+import kotlinx.io.core.readBytes
 import net.mamoe.mirai.data.Packet
 import net.mamoe.mirai.network.BotNetworkHandler
-import net.mamoe.mirai.qqandroid.network.QQAndroidDevice
-import net.mamoe.mirai.qqandroid.network.protocol.packet.EncryptMethod
-import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket
-import net.mamoe.mirai.qqandroid.network.protocol.packet.PacketFactory
-import net.mamoe.mirai.qqandroid.network.protocol.packet.buildOutgoingPacket
-import net.mamoe.mirai.qqandroid.network.protocol.packet.tlv.writeTLVList
+import net.mamoe.mirai.qqandroid.network.QQAndroidClient
+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.cryptor.DecrypterByteArray
 import net.mamoe.mirai.utils.cryptor.DecrypterType
+import net.mamoe.mirai.utils.io.toByteArray
 
 class LoginPacketDecrypter(override val value: ByteArray) : DecrypterByteArray {
-    companion object : DecrypterType<LoginPacketDecrypter> {
-
-    }
+    companion object : DecrypterType<LoginPacketDecrypter>
 }
 
 @UseExperimental(ExperimentalUnsignedTypes::class)
-object LoginPacket : PacketFactory<LoginPacket.LoginPacketResponse, LoginPacketDecrypter>(LoginPacketDecrypter) {
+internal object LoginPacket : PacketFactory<LoginPacket.LoginPacketResponse, LoginPacketDecrypter>(LoginPacketDecrypter) {
 
-    fun invoke(
-        device: QQAndroidDevice
-    ): OutgoingPacket = buildOutgoingPacket(device, EncryptMethod.ByECDH135) {
-        writeTLVList {
+    operator fun invoke(
+        client: QQAndroidClient
+    ): OutgoingPacket = buildOutgoingPacket(client, EncryptMethod.ByECDH135) {
+        writeECDHEncryptedPacket(client.ecdh) {
+            writeShort(9) // subCommand
+            writeShort(LoginType.PASSWORD.value.toShort())
 
+            client.run {
+                client.device.run {
+                    val appId = 16L
+                    val subAppId = 2L
+
+                    t18(appId, appClientVersion, account.id)
+                    t1(account.id, ipAddress)
+                    t106(
+                        appId,
+                        subAppId /* maybe 1*/,
+                        appClientVersion,
+                        account.id,
+                        ipAddress,
+                        1,
+                        account.passwordMd5,
+                        0,
+                        account.id.toByteArray(),
+                        tgtgtKey,
+                        true,
+                        guid,
+                        LoginType.PASSWORD
+                    )
+
+                    /* // from GetStWithPasswd
+                    int mMiscBitmap = this.mMiscBitmap;
+                    if (t.uinDeviceToken) {
+                        mMiscBitmap = (this.mMiscBitmap | 0x2000000);
+                    }
+
+
+                    // defaults true
+                    if (ConfigManager.get_loginWithPicSt()) appIdList = longArrayOf(1600000226L)
+                    */
+                    t116(miscBitMap, subSigMap, longArrayOf(1600000226L))
+                    t100(appId, subAppId, appClientVersion, mainSigMap or 0xC0)
+                    t107(0)
+                    t108(byteArrayOf())
+                    // ignored: t104()
+                    t142(apkId)
+
+                    // if login with non-number uin
+                    // t112()
+                    t144(
+                        androidId = androidId,
+                        androidDevInfo = generateDeviceInfoData(),
+                        osType = osType,
+                        osVersion = version.release,
+                        ipv6NetType = ipv6NetType,
+                        simInfo = simInfo,
+                        unknown = byteArrayOf(), apn = apn,
+                        isGuidFromFileNull = false,
+                        isGuidAvailable = true,
+                        isGuidChanged = false,
+                        guidFlag = guidFlag(GuidSource.FROM_STORAGE, MacOrAndroidIdChangeFlag.NoChange),
+                        buildModel = model,
+                        guid = guid,
+                        buildBrand = brand,
+                        tgtgtKey = tgtgtKey
+                    )
+
+                    t145(guid)
+                    t147(appId, apkVersionName, apkSignatureMd5)
+
+                    if (miscBitMap and 0x80 != 0) {
+                        t166(1)
+                    }
+
+                    // ignored t16a because array5 is null
+
+                    t154(ssoSequenceId)
+                    t141(simInfo, ipv6NetType, apn)
+                    t8(2052)
+
+                    // ignored t511 because domain is null
+                    // ignored t172 because rollbackSig is null
+                    // ignored t185 because loginType is not SMS
+                    // ignored t400 because of first login
+
+                    t187(macAddress)
+                    t188(androidId)
+                    if (imsiMd5.isNotEmpty()) {
+                        t194(imsiMd5)
+                    }
+                    t191()
+                    t201(N = byteArrayOf())
+
+                    val bssid = wifiBSSID
+                    val ssid = wifiSSID
+                    if (bssid != null && ssid != null) {
+                        t202(bssid, ssid)
+                    }
+
+                    t177()
+                    t516()
+                    t521()
+
+                    t525(buildPacket {
+                        t536(buildPacket {
+                            //com.tencent.loginsecsdk.ProtocolDet#packExtraData
+                            writeByte(1) // const
+                            writeByte(0) // data count
+                        }.readBytes())
+                    })
+
+                    // ignored t318 because not logging in by QR
+                }
+            }
         }
+
     }
 
 
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/tlv/Tlv.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/tlv/Tlv.kt
deleted file mode 100644
index 63e124d41..000000000
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/tlv/Tlv.kt
+++ /dev/null
@@ -1,401 +0,0 @@
-package net.mamoe.mirai.qqandroid.network.protocol.packet.tlv
-
-import kotlinx.io.core.*
-import net.mamoe.mirai.utils.io.*
-import net.mamoe.mirai.utils.md5
-import kotlin.random.Random
-
-
-fun BytePacketBuilder.writeTLVList(block: TlvBuilder.() -> Unit) {
-    var tlvCount = 0
-    val tlvList = buildPacket { block(TlvBuilder { tlvCount++ }) }
-    writeShort(tlvCount.toShort())
-    writePacket(tlvList)
-}
-
-
-inline class LoginType(
-    val value: Int
-) {
-    companion object {
-        val SMS = LoginType(3)
-        val PASSWORD = LoginType(1)
-        val WE_CHAT = LoginType(4)
-    }
-}
-
-inline class TlvBuilder(
-    val counter: () -> Unit
-) {
-    fun BytePacketBuilder.t1(uin: Long, ip: ByteArray) {
-        require(ip.size == 4)
-        writeShort(0x1)
-        writeShortLVPacket {
-            writeShort(1) // _ip_ver
-            writeInt(Random.nextInt())
-            writeInt(uin.toInt())
-            writeTime()
-            writeFully(ip)
-            writeShort(0)
-        } shouldEqualsTo 20
-    }
-
-    fun BytePacketBuilder.t2(captchaCode: String, captchaToken: ByteArray, sigVer: Short = 0) {
-        writeShort(0x2)
-        writeShortLVPacket {
-            writeShort(sigVer)
-            writeShortLVString(captchaCode)
-            writeShortLVByteArray(captchaToken)
-        }
-    }
-
-    fun BytePacketBuilder.t8() {
-        writeShort(0x0008)
-        writeShortLVPacket {
-            writeShort(0)
-            writeInt(2052) // localId
-            writeShort(0)
-        }
-    }
-
-    fun BytePacketBuilder.t18(appId: Long, appClientVersion: Int, uin: Long, constant1_always_0: Int) {
-        writeShort(0x18)
-        writeShortLVPacket {
-            writeShort(1) //_ping_version
-            writeInt(1536) //_sso_version
-            writeInt(appId.toInt())
-            writeInt(appClientVersion)
-            writeInt(uin.toInt())
-            writeShort(constant1_always_0.toShort())
-            writeShort(0)
-        } shouldEqualsTo 22
-    }
-
-    fun BytePacketBuilder.t106(
-        appId: Long,
-        subAppId: Long,
-        appClientVersion: Int,
-        uin: Long,
-        ipAddress: ByteArray,
-        n5_always_1: Int = 1,
-        temp_pwd: ByteArray,
-        salt: Long,
-        uinAccount: ByteArray,
-        tgtgtKey: ByteArray,
-        n7: Int,
-        array_6_may_be_null: ByteArray?,
-        loginType: LoginType
-    ) {
-        writeShort(0x106)
-
-        writeShortLVPacket {
-            encryptAndWrite(
-                if (salt == 0L) {
-                    md5(buildPacket { writeFully(temp_pwd); writeInt(uin.toInt()) }.readBytes())
-                } else {
-                    md5(buildPacket { writeFully(temp_pwd); writeInt(salt.toInt()) }.readBytes())
-                }
-            ) {
-                writeShort(4)//TGTGTVer
-                writeInt(Random.nextInt())
-                writeInt(5)//ssoVer
-                writeInt(appId.toInt())
-                writeInt(appClientVersion)
-
-                if (uin == 0L) {
-                    writeLong(salt)
-                } else {
-                    writeLong(uin)
-                }
-
-                writeTime()
-                writeFully(ipAddress)
-                writeByte(n5_always_1.toByte())
-                writeFully(temp_pwd)
-                writeFully(tgtgtKey)
-                writeInt(0)
-                writeByte(n7.toByte())
-                if (array_6_may_be_null == null) {
-                    repeat(4) {
-                        writeInt(Random.nextInt())
-                    }
-                } else {
-                    writeFully(array_6_may_be_null)
-                }
-                writeInt(subAppId.toInt())
-                writeInt(loginType.value)
-                writeShortLVByteArray(uinAccount)
-            }
-        } shouldEqualsTo 98
-    }
-
-    fun BytePacketBuilder.t116(
-        miscBitmap: Int,
-        subSigMap: Int,
-        appIdList: LongArray
-    ) {
-        writeShort(0x116)
-        writeShortLVPacket {
-            writeByte(0) // _ver
-            writeInt(miscBitmap)
-            writeInt(subSigMap)
-            writeByte(appIdList.size.toByte())
-            appIdList.forEach {
-                writeInt(it.toInt())
-            }
-        }
-    }
-
-    fun BytePacketBuilder.t100(
-        appId: Long,
-        subAppId: Long,
-        appClientVersion: Int,
-        mainSigMap: Int
-    ) {
-        writeShort(0x100)
-        writeShortLVPacket {
-            writeShort(1)//db_buf_ver
-            writeInt(5)//sso_ver
-            writeInt(appId.toInt())
-            writeInt(subAppId.toInt())
-            writeInt(appClientVersion)
-            writeInt(mainSigMap)
-        } shouldEqualsTo 22
-    }
-
-    fun BytePacketBuilder.t107(
-        picType: Int,
-        const1_always_0: Int = 0,
-        const2_always_0: Int = 0,
-        const3_always_1: Int = 1
-    ) {
-        writeShort(0x107)
-        writeShortLVPacket {
-            writeShort(picType.toShort())
-            writeByte(const1_always_0.toByte())
-            writeShort(const2_always_0.toShort())
-            writeByte(const3_always_1.toByte())
-        } shouldEqualsTo 6
-    }
-
-    fun BytePacketBuilder.t108(
-        to_verify_passwd_img: ByteArray
-    ) {
-        writeShort(0x108)
-        writeShortLVPacket {
-            writeFully(to_verify_passwd_img)
-        }
-    }
-
-    fun BytePacketBuilder.t104(
-        t104Data: ByteArray
-    ) {
-        writeShort(0x104)
-        writeShortLVPacket {
-            writeFully(t104Data)
-        }
-    }
-
-    /**
-     * @param apkId application.getPackageName().getBytes()
-     */
-    fun BytePacketBuilder.t142(
-        apkId: ByteArray
-    ) {
-        writeShort(0x142)
-        writeShortLVPacket {
-            writeShort(0) //_version
-            writeShortLVByteArrayLimitedLength(apkId, 32)
-        }
-    }
-
-    fun BytePacketBuilder.t112(
-        nonNumberUin: ByteArray
-    ) {
-        writeShort(0x112)
-        writeShortLVPacket {
-            writeFully(nonNumberUin)
-        }
-    }
-
-    fun BytePacketBuilder.t144(
-        // t109
-        androidId: ByteArray,
-
-        // t52d
-        androidDevInfo: ByteArray,
-
-        // t124
-        osType: ByteArray = "android".toByteArray(),
-        osVersion: ByteArray,
-        ipv6NetType: Int,
-        simInfo: ByteArray,
-        unknown: ByteArray,
-        apn: ByteArray = "wifi".toByteArray(),
-
-        // t128
-        isGuidFromFileNull: Boolean = false,
-        isGuidAvailable: Boolean = true,
-        isGuidChanged: Boolean = false,
-        guidFlag: Int,
-        buildModel: ByteArray,
-        guid: ByteArray,
-        buildBrand: ByteArray,
-
-        // encrypt
-        tgtgtKey: ByteArray
-    ) {
-        writeShort(0x144)
-        writeShortLVPacket {
-            encryptAndWrite(tgtgtKey) {
-                t109(androidId)
-                t52d(androidDevInfo)
-                t124(osType, osVersion, ipv6NetType, simInfo, unknown, apn)
-                t128(isGuidFromFileNull, isGuidAvailable, isGuidChanged, guidFlag, buildModel, guid, buildBrand)
-                t16e(buildModel)
-            }
-        }
-    }
-
-    fun BytePacketBuilder.t109(
-        androidId: ByteArray
-    ) {
-        writeShort(0x109)
-        writeShortLVPacket {
-            writeFully(androidId)
-        }
-    }
-
-    fun BytePacketBuilder.t52d(
-        androidDevInfo: ByteArray // oicq.wlogin_sdk.tools.util#get_android_dev_info
-    ) {
-        writeShort(0x52d)
-        writeShortLVPacket {
-            writeFully(androidDevInfo)
-        }
-    }
-
-    fun BytePacketBuilder.t124(
-        osType: ByteArray = "android".toByteArray(),
-        osVersion: ByteArray, // Build.VERSION.RELEASE.toByteArray()
-        ipv6NetType: Int,  //oicq.wlogin_sdk.tools.util#get_network_type
-        simInfo: ByteArray, // oicq.wlogin_sdk.tools.util#get_sim_operator_name
-        unknown: ByteArray,
-        apn: ByteArray = "wifi".toByteArray() // oicq.wlogin_sdk.tools.util#get_apn_string
-    ) {
-        writeShort(0x124)
-        writeShortLVPacket {
-            writeShortLVByteArrayLimitedLength(osType, 16)
-            writeShortLVByteArrayLimitedLength(osVersion, 16)
-            writeShort(ipv6NetType.toShort())
-            writeShortLVByteArrayLimitedLength(simInfo, 16)
-            writeShortLVByteArrayLimitedLength(unknown, 32)
-            writeShortLVByteArrayLimitedLength(apn, 16)
-        }
-    }
-
-    fun BytePacketBuilder.t128(
-        isGuidFromFileNull: Boolean = false, // 保存到文件的 GUID 是否为 null
-        isGuidAvailable: Boolean = true, // GUID 是否可用(计算/读取成功)
-        isGuidChanged: Boolean = false, // GUID 是否有变动
-        /**
-         * guidFlag:
-         * ```java
-         * GUID_FLAG |= GUID_SRC << 24 & 0xFF000000;
-         * GUID_FLAG |= FLAG_MAC_ANDROIDID_GUID_CHANGE << 8 & 0xFF00;
-         * ```
-         *
-         * FLAG_MAC_ANDROIDID_GUID_CHANGE:
-         * ```java
-         * if (!Arrays.equals(currentMac, get_last_mac)) {
-         *     oicq.wlogin_sdk.request.t.FLAG_MAC_ANDROIDID_GUID_CHANGEMENT |= 0x1;
-         * }
-         * if (!Arrays.equals(currentAndroidId, get_last_android_id)) {
-         *     oicq.wlogin_sdk.request.t.FLAG_MAC_ANDROIDID_GUID_CHANGEMENT |= 0x2;
-         * }
-         * if (!Arrays.equals(currentGuid, get_last_guid)) {
-         *     oicq.wlogin_sdk.request.t.FLAG_MAC_ANDROIDID_GUID_CHANGEMENT |= 0x4;
-         * }
-         * ```
-         */
-        guidFlag: Int,
-        buildModel: ByteArray,  // android.os.Build.MODEL
-        /**
-         * [generateGuid] or `"%4;7t>;28<fc.5*6".toByteArray()`
-         */
-        guid: ByteArray,
-        buildBrand: ByteArray // android.os.Build.BRAND
-    ) {
-        writeShort(0x128)
-        writeShortLVPacket {
-            writeShort(0)
-            writeByte(isGuidFromFileNull.toByte())
-            writeByte(isGuidAvailable.toByte())
-            writeByte(isGuidChanged.toByte())
-            writeInt(guidFlag)
-            writeShortLVByteArrayLimitedLength(buildModel, 32)
-            writeShortLVByteArrayLimitedLength(guid, 16)
-            writeShortLVByteArrayLimitedLength(buildBrand, 16)
-        }
-    }
-
-    fun BytePacketBuilder.t16e(
-        buildModel: ByteArray
-    ) {
-        writeShort(0x16e)
-        writeShortLVPacket {
-            writeFully(buildModel)
-        }
-    }
-
-    fun BytePacketBuilder.t145(
-        guid: ByteArray
-    ) {
-        writeShort(0x145)
-        writeShortLVPacket {
-            writeFully(guid)
-        }
-    }
-
-    fun BytePacketBuilder.t147(
-        appId: Long,
-        apkVersionName: ByteArray,
-        apkSignatureMd5: ByteArray
-    ) {
-        writeShort(0x147)
-        writeShortLVPacket {
-            writeLong(appId)
-            writeShortLVByteArrayLimitedLength(apkVersionName, 32)
-            writeShortLVByteArrayLimitedLength(apkSignatureMd5, 32)
-        }
-    }
-
-    fun BytePacketBuilder.t166(
-        imageType: Int
-    ) {
-        writeShort(0x166)
-        writeShortLVPacket {
-            writeByte(imageType.toByte())
-        }
-    }
-}
-
-private fun Boolean.toByte(): Byte = if (this) 1 else 0
-
-private infix fun Int.shouldEqualsTo(int: Int) = require(this == int)
-
-fun randomAndroidId(): String = buildString(15) {
-    repeat(15) { append(Random.nextInt(10)) }
-}
-
-/**
- * Defaults "%4;7t>;28<fc.5*6".toByteArray()
- */
-fun generateGuid(androidId: String, macAddress: String): ByteArray {
-    return md5(androidId + macAddress)
-}
-
-fun getMacAddr(): String = "02:00:00:00:00:00"
-
-
-// AndroidDevInfo: oicq.wlogin_sdk.tools.util#get_android_dev_info
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 7d0c10f57..e95b0823f 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
@@ -22,6 +22,20 @@ fun Int.toByteArray(): ByteArray = byteArrayOf(
     (shr(0) and 0xFF).toByte()
 )
 
+/**
+ * 255 -> 00 00 00 FF
+ */
+fun Long.toByteArray(): ByteArray = byteArrayOf(
+    (shr(56) and 0xFF).toByte(),
+    (shr(48) and 0xFF).toByte(),
+    (shr(40) and 0xFF).toByte(),
+    (shr(32) and 0xFF).toByte(),
+    (shr(24) and 0xFF).toByte(),
+    (shr(16) and 0xFF).toByte(),
+    (shr(8) and 0xFF).toByte(),
+    (shr(0) and 0xFF).toByte()
+)
+
 fun Int.toUHexString(separator: String = " "): String = this.toByteArray().toUHexString(separator)
 
 /**