Browse Source

小更新,github 远端不再同步更新,本地分叉改为闭源

akemimadoka 6 years ago
parent
commit
26484a7e25
11 changed files with 323 additions and 175 deletions
  1. 2 1
      Docs/研究.md
  2. 7 6
      YumeBot/Jce.cpp
  3. 9 9
      YumeBot/Jce.h
  4. 3 2
      YumeBot/Misc.h
  5. 132 57
      YumeBot/Request.h
  6. 1 1
      YumeBot/Session.h
  7. 4 4
      YumeBot/Tlv.cpp
  8. 146 75
      YumeBot/Tlv.h
  9. 14 15
      YumeBot/Wup.cpp
  10. 4 4
      YumeBot/Wup.h
  11. 1 1
      conanfile.txt

+ 2 - 1
Docs/研究.md

@@ -27,7 +27,8 @@ com.tencent.mobileqq.msf.sdk.MsfServiceSdk.getLoginMsg(String uin, byte[] pwd) 
 
 Account
 ----
-tgtgt_key = Md5(随机 16 字节 + IMEI)
+Guid = Md5(Imei + mac地址)
+tgtgt_key = Md5(随机 16 字节 + Guid)
 
 MsgCookie
 ----

+ 7 - 6
YumeBot/Jce.cpp

@@ -8,12 +8,13 @@ JceStruct::~JceStruct()
 {
 }
 
-JceInputStream::JceInputStream(Cafe::Io::InputStream* stream)
+JceInputStream::JceInputStream(Cafe::Io::SeekableStream<Cafe::Io::InputStream>* stream)
     : m_Reader{ stream, std::endian::little }
 {
 }
 
-Cafe::Io::BinaryReader& JceInputStream::GetReader() noexcept
+Cafe::Io::BinaryReader<Cafe::Io::SeekableStream<Cafe::Io::InputStream>>&
+JceInputStream::GetReader() noexcept
 {
 	return m_Reader;
 }
@@ -39,8 +40,7 @@ std::pair<HeadData, std::size_t> JceInputStream::ReadHead() const
 
 std::pair<HeadData, std::size_t> JceInputStream::PeekHead() const
 {
-	const auto underlyingStream = dynamic_cast<Cafe::Io::SeekableStreamBase*>(m_Reader.GetStream());
-	assert(underlyingStream);
+	const auto underlyingStream = m_Reader.GetStream();
 	const auto pos = underlyingStream->GetPosition();
 	const auto head = ReadHead();
 	underlyingStream->SeekFromBegin(pos);
@@ -438,12 +438,13 @@ bool JceInputStream::doRead(std::uint32_t tag, gsl::span<std::byte> const& value
 	return false;
 }
 
-JceOutputStream::JceOutputStream(Cafe::Io::OutputStream* stream)
+JceOutputStream::JceOutputStream(Cafe::Io::SeekableStream<Cafe::Io::OutputStream>* stream)
     : m_Writer{ stream, std::endian::little }
 {
 }
 
-Cafe::Io::BinaryWriter& JceOutputStream::GetWriter() noexcept
+Cafe::Io::BinaryWriter<Cafe::Io::SeekableStream<Cafe::Io::OutputStream>>&
+JceOutputStream::GetWriter() noexcept
 {
 	return m_Writer;
 }

+ 9 - 9
YumeBot/Jce.h

@@ -93,9 +93,10 @@ namespace YumeBot::Jce
 	class JceInputStream
 	{
 	public:
-		explicit JceInputStream(Cafe::Io::InputStream* stream);
+		explicit JceInputStream(Cafe::Io::SeekableStream<Cafe::Io::InputStream>* stream);
 
-		[[nodiscard]] Cafe::Io::BinaryReader& GetReader() noexcept;
+		[[nodiscard]] Cafe::Io::BinaryReader<Cafe::Io::SeekableStream<Cafe::Io::InputStream>>&
+		GetReader() noexcept;
 
 		std::pair<HeadData, std::size_t> ReadHead() const;
 		std::pair<HeadData, std::size_t> PeekHead() const;
@@ -118,9 +119,7 @@ namespace YumeBot::Jce
 		template <typename T>
 		[[nodiscard]] bool Read(std::uint32_t tag, T& value, Detail::NoneType = Detail::None)
 		{
-			const auto underlyingStream =
-			    dynamic_cast<Cafe::Io::SeekableStreamBase*>(m_Reader.GetStream());
-			assert(underlyingStream);
+			const auto underlyingStream = m_Reader.GetStream();
 			const auto currentPos = underlyingStream->GetPosition();
 			CAFE_SCOPE_FAIL
 			{
@@ -159,7 +158,7 @@ namespace YumeBot::Jce
 		}
 
 	private:
-		Cafe::Io::BinaryReader m_Reader;
+		Cafe::Io::BinaryReader<Cafe::Io::SeekableStream<Cafe::Io::InputStream>> m_Reader;
 
 		bool doRead(std::uint32_t tag, std::uint8_t& value);
 		bool doRead(std::uint32_t tag, std::byte& value);
@@ -380,9 +379,10 @@ namespace YumeBot::Jce
 	class JceOutputStream
 	{
 	public:
-		explicit JceOutputStream(Cafe::Io::OutputStream* stream);
+		explicit JceOutputStream(Cafe::Io::SeekableStream<Cafe::Io::OutputStream>* stream);
 
-		[[nodiscard]] Cafe::Io::BinaryWriter& GetWriter() noexcept;
+		[[nodiscard]] Cafe::Io::BinaryWriter<Cafe::Io::SeekableStream<Cafe::Io::OutputStream>>&
+		GetWriter() noexcept;
 
 		void WriteHead(HeadData head);
 
@@ -411,7 +411,7 @@ namespace YumeBot::Jce
 		}
 
 	private:
-		Cafe::Io::BinaryWriter m_Writer;
+		Cafe::Io::BinaryWriter<Cafe::Io::SeekableStream<Cafe::Io::OutputStream>> m_Writer;
 
 		void doWrite(std::uint32_t tag, std::uint8_t value);
 		void doWrite(std::uint32_t tag, std::byte value);

+ 3 - 2
YumeBot/Misc.h

@@ -12,7 +12,7 @@ namespace YumeBot
 	constexpr std::uint32_t DefualtSigSrc = 1;
 	constexpr std::uint32_t DefaultBitmap = 0x7F7C;
 	constexpr std::uint32_t DefaultGetSig = 0x10400;
-	constexpr std::uint32_t DefaultGetSig1 = 0x1E1060;
+	constexpr std::uint32_t DefaultGetSig1 = 0x1E10E0;
 
 	constexpr UsingStringView DefaultDomains[]{ CAFE_UTF8_SV("game.qq.com") };
 
@@ -20,7 +20,7 @@ namespace YumeBot
 	{
 		std::uint8_t Content[4]{};
 
-		constexpr bool IsUnspecified() const noexcept
+		[[nodiscard]] constexpr bool IsUnspecified() const noexcept
 		{
 			for (auto item : Content)
 			{
@@ -58,6 +58,7 @@ namespace YumeBot
 		Vpn = 17
 	};
 
+	constexpr auto DefaultApkId = CAFE_UTF8_SV("com.tencent.mobileqq");
 	constexpr auto DefaultApkVersion = CAFE_UTF8_SV("5.0.0");
 
 	constexpr std::uint16_t DefaultClientVersion = 8001;

+ 132 - 57
YumeBot/Request.h

@@ -4,6 +4,15 @@
 
 namespace YumeBot::Request
 {
+	template <std::ptrdiff_t Extent>
+	void GenerateRandomBytes(gsl::span<std::byte, Extent> dest)
+	{
+		std::random_device rd;
+		std::default_random_engine engine{ rd() };
+		std::uniform_int_distribution<> dist{ 0, std::numeric_limits<std::uint8_t>::max() };
+		std::generate_n(dest.data(), dest.size(), [&] { return static_cast<std::byte>(dist(engine)); });
+	}
+
 	struct KeyStorage
 	{
 		std::byte PubKey[25];
@@ -27,30 +36,51 @@ namespace YumeBot::Request
 		explicit KeyStorage(RandomizeTag)
 		{
 			Cryptography::Ecdh::GenerateKeyPair(gsl::make_span(PubKey), gsl::make_span(ShareKey));
+			GenerateRandomBytes(gsl::make_span(RandomKey));
+		}
+	};
+
+	class StringMd5Container
+	{
+	public:
+		StringMd5Container(UsingString value) : m_Value{ std::move(value) }
+		{
+			Cryptography::Md5::Calculate(gsl::as_bytes(m_Value.GetView().GetTrimmedSpan()), m_Md5);
+		}
 
-			std::random_device rd;
-			std::default_random_engine engine{ rd() };
-			std::uniform_int_distribution<> dist{ 0, std::numeric_limits<std::uint8_t>::max() };
-			std::generate(std::begin(RandomKey), std::end(RandomKey),
-			              [&] { return static_cast<std::byte>(dist(engine)); });
+		UsingString const& GetValue() const noexcept
+		{
+			return m_Value;
 		}
+
+		gsl::span<const std::byte, 16> GetMd5() const noexcept
+		{
+			return m_Md5;
+		}
+
+	private:
+		UsingString m_Value;
+		std::array<std::byte, 16> m_Md5;
 	};
 
 	struct RequestContext
 	{
 		std::uint32_t Uin;
 		std::array<std::byte, 16> PasswordMd5;
+
+		StringMd5Container Imei;
+		StringMd5Container WifiMac;
+		StringMd5Container AndroidId;
+
 		std::uint32_t ServerTime = Utility::GetPosixTime();
 		LocaleIdEnum CurrentLocaleId = LocaleIdEnum::ZH_CN;
 
 		UsingString OsVersion = DefaultOsVersion;
 
-		UsingString Imei;
-		UsingString WifiMac;
-		UsingString AndroidId;
-
 		KeyStorage Keys{ KeyStorage::Randomize };
 
+		std::uint32_t MSalt{};
+
 		UsingString SimOperatorName;
 		ConnectionTypeEnum ConnectionType;
 		UsingString Apn;
@@ -60,6 +90,12 @@ namespace YumeBot::Request
 
 		SsoVersion UsingSsoVersion = SsoVersion::Version8;
 
+		std::uint32_t AppId = DefaultAppId;
+		UsingString ApkId = DefaultApkId;
+
+		IpV4Addr ClientIp;
+		std::uint32_t InitTime;
+
 		std::array<std::byte, 16> const& GetGuid() const
 		{
 			if (m_Guid.has_value())
@@ -67,7 +103,7 @@ namespace YumeBot::Request
 				return m_Guid.value();
 			}
 
-			const auto tmp = Imei + WifiMac;
+			const auto tmp = Imei.GetValue() + WifiMac.GetValue();
 			std::array<std::byte, 16> result;
 			Cryptography::Md5::Calculate(gsl::as_bytes(tmp.GetView().GetTrimmedSpan()),
 			                             gsl::make_span(result));
@@ -84,10 +120,32 @@ namespace YumeBot::Request
 			return std::exchange(m_ClientSeq, (m_ClientSeq + 1) % 200);
 		}
 
+		std::array<std::byte, 16> const& GetTGTGTKey() const
+		{
+			if (m_TGTGTKey.has_value())
+			{
+				return m_TGTGTKey.value();
+			}
+
+			std::array<std::byte, 32> buffer;
+			GenerateRandomBytes(gsl::make_span(buffer).subspan(0, 16));
+			std::memcpy(buffer.data() + 16, Imei.GetMd5().data(), 16);
+
+			auto& key = m_TGTGTKey.emplace();
+			Cryptography::Md5::Calculate(gsl::make_span(buffer), gsl::make_span(key));
+			return key;
+		}
+
+		void ResetTGTGTKey() const
+		{
+			m_TGTGTKey.reset();
+		}
+
 	private:
 		mutable std::optional<std::array<std::byte, 16>> m_Guid;
 		mutable std::size_t m_RequestSeq{};
 		mutable std::size_t m_ClientSeq{};
+		mutable std::optional<std::array<std::byte, 16>> m_TGTGTKey;
 	};
 
 	/// @brief  加密类型
@@ -116,7 +174,7 @@ namespace YumeBot::Request
 	{
 		static constexpr std::uint16_t SubCmd = SubCmdValue;
 
-		void ProcessResponse(Cafe::Io::BinaryReader& reader, RequestContext& context,
+		void ProcessResponse(Cafe::Io::BinaryReader<>& reader, RequestContext& context,
 		                     std::size_t seq) const
 		{
 			return static_cast<const T*>(this)->DoProcessResponse(reader, context, seq);
@@ -135,13 +193,13 @@ namespace YumeBot::Request
 		/// @return Seq
 		template <typename T, std::uint16_t CmdValue, std::uint16_t SubCmdValue,
 		          EncryptType EncryptTypeValue>
-		std::size_t WriteRequest(Cafe::Io::OutputStream* stream,
+		std::size_t WriteRequest(Cafe::Io::SeekableStream<Cafe::Io::OutputStream>* stream,
 		                         RequestBase<T, CmdValue, SubCmdValue, EncryptTypeValue> const& request)
 		{
 			const auto seq = m_Context.AcquireRequestSeq();
 
 			Cafe::Io::MemoryStream unencryptedBodyStream;
-			Cafe::Io::BinaryWriter writer{ &unencryptedBodyStream, std::endian::big };
+			Cafe::Io::BinaryWriter<> writer{ &unencryptedBodyStream, std::endian::big };
 
 			writer.Write(SubCmdValue);
 
@@ -158,7 +216,9 @@ namespace YumeBot::Request
 			writer.Write(tlvNum);
 
 			Cafe::Io::MemoryStream requestContentStream;
-			Cafe::Io::BinaryWriter requestWriter{ &requestContentStream, std::endian::big };
+			Cafe::Io::BinaryWriter<Cafe::Io::SeekableStream<Cafe::Io::OutputStream>> requestWriter{
+				&requestContentStream, std::endian::big
+			};
 
 			// 写入 Head
 			const auto clientSeq = m_Context.AcquireClientSeq();
@@ -191,7 +251,7 @@ namespace YumeBot::Request
 			// 写入 End
 			requestWriter.Write(std::uint8_t{ 3 });
 
-			EncodeRequest(stream, requestContentStream.GetInternalStorage());
+			EncodeRequest(stream, requestContentStream.GetInternalStorage(), seq);
 
 			return seq;
 		}
@@ -199,7 +259,7 @@ namespace YumeBot::Request
 		template <EncryptType EncryptTypeValue>
 		void EncryptBody(Cafe::Io::OutputStream* stream, gsl::span<const std::byte> const& body)
 		{
-			Cafe::Io::BinaryWriter writer{ stream, std::endian::big };
+			Cafe::Io::BinaryWriter<> writer{ stream, std::endian::big };
 
 			const auto teaKey = [&] {
 				if constexpr (EncryptTypeValue == EncryptType::Ecdh)
@@ -225,33 +285,48 @@ namespace YumeBot::Request
 			Cryptography::Tea::Encrypt(body, stream, teaKey);
 		}
 
-		void EncodeRequest(Cafe::Io::OutputStream* stream, gsl::span<const std::byte> const& request)
+		void EncodeRequest(Cafe::Io::SeekableStream<Cafe::Io::OutputStream>* stream,
+		                   gsl::span<const std::byte> const& request, std::size_t seq)
 		{
 			if (m_Context.UsingSsoVersion == SsoVersion::Version8)
 			{
-				EncodeRequestV8(stream, request);
+				EncodeRequestV8(stream, request, seq);
 			}
 			else
 			{
 				assert(m_Context.UsingSsoVersion == SsoVersion::Version9);
-				EncodeRequestV9(stream, request);
+				EncodeRequestV9(stream, request, seq);
 			}
 		}
 
 	private:
 		RequestContext m_Context;
 
-		void EncodeRequestV8(Cafe::Io::OutputStream* stream, gsl::span<const std::byte> const& request)
+		void EncodeRequestV8(Cafe::Io::SeekableStream<Cafe::Io::OutputStream>* stream,
+		                     gsl::span<const std::byte> const& request, std::size_t seq)
 		{
 			constexpr std::uint32_t ssoVersion = static_cast<std::uint32_t>(SsoVersion::Version8);
 
-			Cafe::Io::BinaryWriter writer{ stream, std::endian::big };
-			writer.Write(ssoVersion);
-			writer.Write(std::uint8_t{}); // unknown byte
+			Cafe::Io::BinaryWriter<Cafe::Io::SeekableStream<Cafe::Io::OutputStream>> writer{
+				stream, std::endian::big
+			};
+
+			// 序列化 CSSOReqHead
+			const auto lengthPos = stream->GetPosition();
+			writer.Write(std::uint32_t{}); // length
+			writer.Write(static_cast<std::uint32_t>(seq));
+			writer.Write(std::uint32_t{ 100 }); // appId
+			writer.Write(m_Context.AppId);      // msfAppId
+			writer.Write(static_cast<std::uint8_t>(m_Context.ConnectionType));
+			
+			std::byte dummy[11]{};
+			stream->WriteBytes(gsl::make_span(dummy));  // Unknown array
+			
 			
 		}
 
-		void EncodeRequestV9(Cafe::Io::OutputStream* stream, gsl::span<const std::byte> const& request)
+		void EncodeRequestV9(Cafe::Io::SeekableStream<Cafe::Io::OutputStream>* stream,
+		                     gsl::span<const std::byte> const& request, std::size_t seq)
 		{
 			// TODO
 		}
@@ -263,18 +338,39 @@ namespace YumeBot::Request
 		{
 			const auto& guid = context.GetGuid();
 
-			tlvBuilder.WriteTlv(Tlv::TlvT<0x106>{ AppId, SubAppId, ClientVersion, Uin, InitTime, ClientIp,
-			                                      false, PasswordMd5, 0, TGTGTKey, false, guid, 1 });
-			tlvBuilder.WriteTlv(Tlv::TlvT<0x100>{ AppId, SubAppId, WxAppId, GetSig1 });
-			tlvBuilder.WriteTlv(Tlv::TlvT<0x107>{ PicType, CapType, PicSize, RetType });
-			tlvBuilder.WriteTlv(Tlv::TlvT<0x116>{ Bitmap, GetSig, SubAppIdList });
+			constexpr std::uint32_t rc = 0;
+			// 意义不明。。。这个 AppId 不遵守其他地方的约定,总是 16
+			constexpr auto appId = 16;
+			constexpr auto clientVersion = 1;
+			constexpr auto savePwd = false;
+			const auto passwordMd5 = context.PasswordMd5;
+			const auto tgtgtKey = context.GetTGTGTKey();
+			constexpr auto sigSrc = DefualtSigSrc;
+			constexpr auto bitmap = DefaultBitmap;
+			constexpr auto getSig = DefaultGetSig;
+			constexpr auto getSig1 = DefaultGetSig1;
+			const auto wxAppId = context.AppId;
+			constexpr auto picType = 0;
+			constexpr auto capType = 0;
+			constexpr auto picSize = 0;
+			constexpr auto retType = 1;
+			constexpr auto& domains = DefaultDomains;
+
+			tlvBuilder.WriteTlv(Tlv::TlvT<0x18>{ appId, clientVersion, context.Uin, rc });
+			tlvBuilder.WriteTlv(Tlv::TlvT<0x1>{ context.Uin, context.InitTime, context.ClientIp });
+			tlvBuilder.WriteTlv(Tlv::TlvT<0x106>{
+			    appId, context.AppId, clientVersion, context.Uin, context.InitTime, context.ClientIp,
+			    savePwd, passwordMd5, context.MSalt, tgtgtKey, false, guid, sigSrc });
+			tlvBuilder.WriteTlv(Tlv::TlvT<0x100>{ appId, context.AppId, wxAppId, getSig1 });
+			tlvBuilder.WriteTlv(Tlv::TlvT<0x107>{ picType, capType, picSize, retType });
+			tlvBuilder.WriteTlv(Tlv::TlvT<0x116>{ bitmap, getSig, {} });
 			tlvBuilder.WriteTlv(Tlv::TlvT<0x145>{ guid });
 			tlvBuilder.WriteTlv(Tlv::TlvT<0x154>{ static_cast<std::uint32_t>(seq) });
 			tlvBuilder.WriteTlv(
 			    Tlv::TlvT<0x141>{ context.SimOperatorName, context.ConnectionType, context.Apn });
 			tlvBuilder.WriteTlv(Tlv::TlvT<0x8>{ 0, context.CurrentLocaleId, 0 });
 			tlvBuilder.WriteTlv(
-			    Tlv::TlvT<0x147>{ AppId, DefaultApkVersion, gsl::as_bytes(gsl::make_span(Signature)) });
+			    Tlv::TlvT<0x147>{ appId, DefaultApkVersion, gsl::as_bytes(gsl::make_span(Signature)) });
 			tlvBuilder.WriteTlv(Tlv::TlvT<0x177>{ BuildTime, SdkVersion });
 
 			if (!Ksid.empty())
@@ -282,46 +378,25 @@ namespace YumeBot::Request
 				tlvBuilder.WriteTlv(Tlv::TlvT<0x108>{ std::vector<std::byte>(Ksid.begin(), Ksid.end()) });
 			}
 
-			if (!context.WifiMac.IsEmpty())
+			if (!context.WifiMac.GetValue().IsEmpty())
 			{
-				tlvBuilder.WriteTlv(Tlv::TlvT<0x187>{ context.WifiMac });
+				tlvBuilder.WriteTlv(Tlv::TlvT<0x187>{ context.WifiMac.GetMd5() });
 			}
 
-			if (!context.AndroidId.IsEmpty())
+			if (!context.AndroidId.GetValue().IsEmpty())
 			{
-				tlvBuilder.WriteTlv(Tlv::TlvT<0x188>{ context.AndroidId });
+				tlvBuilder.WriteTlv(Tlv::TlvT<0x188>{ context.AndroidId.GetMd5() });
 			}
 
-			if (!context.Imei.IsEmpty())
+			if (!context.Imei.GetValue().IsEmpty())
 			{
-				tlvBuilder.WriteTlv(Tlv::TlvT<0x109>{ context.Imei });
+				tlvBuilder.WriteTlv(Tlv::TlvT<0x109>{ context.Imei.GetMd5() });
 			}
 
 			// TODO
 		}
 
-		std::uint32_t AppId;
-		std::uint32_t SubAppId;
-		std::uint32_t ClientVersion;
-		std::uint32_t Uin;
-		std::uint32_t Rc;
-		IpV4Addr ClientIp;
-		gsl::span<const std::byte> InitTime;
-		gsl::span<const std::byte, 16> PasswordMd5;
-		gsl::span<const std::byte> TGTGTKey;
-		std::uint32_t SigSrc;
-		std::uint32_t Bitmap;
-		std::uint32_t GetSig;
-		gsl::span<const std::uint32_t> SubAppIdList;
-		std::uint32_t GetSig1;
-		std::uint32_t WxAppId;
-		std::uint16_t PicType;
-		std::uint8_t CapType;
-		std::uint16_t PicSize;
-		std::uint8_t RetType;
 		gsl::span<const std::byte> Ksid;
 		gsl::span<const std::byte> SigSession;
-		UsingStringView ApkId;
-		gsl::span<const UsingStringView> Domains;
 	};
 } // namespace YumeBot::Request

+ 1 - 1
YumeBot/Session.h

@@ -84,7 +84,7 @@ namespace YumeBot
 			{
 				Cafe::Io::MemoryStream stream;
 				// TODO
-				m_RequestBuilder.WriteRequest(stream, Request::RequestTGTGT{});
+				m_RequestBuilder.WriteRequest(&stream, Request::RequestTGTGT{});
 				m_Socket.PushData(stream.GetInternalStorage());
 
 				// TODO: 验证 Response

+ 4 - 4
YumeBot/Tlv.cpp

@@ -3,10 +3,10 @@
 using namespace YumeBot;
 using namespace Tlv;
 
-TlvBuilder::TlvBuilder(Cafe::Io::OutputStream* stream, std::size_t initialTlvCount)
+TlvBuilder::TlvBuilder(Cafe::Io::SeekableStream<Cafe::Io::OutputStream>* stream,
+                       std::size_t initialTlvCount)
     : m_Writer{ stream, std::endian::big }, m_TlvCount{ initialTlvCount }
 {
-	assert(dynamic_cast<Cafe::Io::SeekableStreamBase*>(stream) && "stream should be seekable.");
 }
 
 std::size_t TlvBuilder::GetTlvCount() const noexcept
@@ -14,7 +14,7 @@ std::size_t TlvBuilder::GetTlvCount() const noexcept
 	return m_TlvCount;
 }
 
-TlvReader::TlvReader(Cafe::Io::InputStream* stream) : m_Reader{ stream, std::endian::big }
+TlvReader::TlvReader(Cafe::Io::SeekableStream<Cafe::Io::InputStream>* stream)
+    : m_Reader{ stream, std::endian::big }
 {
-	assert(dynamic_cast<Cafe::Io::SeekableStreamBase*>(stream) && "stream should be seekable.");
 }

+ 146 - 75
YumeBot/Tlv.h

@@ -14,11 +14,14 @@ namespace YumeBot::Tlv
 	template <std::uint16_t Cmd>
 	struct TlvT
 	{
-		void Write(Cafe::Io::BinaryWriter& writer) const
+		void
+		Write(Cafe::Io::BinaryWriter<Cafe::Io::SeekableStream<Cafe::Io::OutputStream>>& writer) const
 		{
 		}
 
-		static TlvT<Cmd> Read(Cafe::Io::BinaryReader& reader, std::size_t bodySize)
+		static TlvT<Cmd>
+		Read(Cafe::Io::BinaryReader<Cafe::Io::SeekableStream<Cafe::Io::InputStream>>& reader,
+		     std::size_t bodySize)
 		{
 			return {};
 		}
@@ -30,8 +33,9 @@ namespace YumeBot::Tlv
 	};
 
 	template <std::uint16_t Cmd>
-	struct IsWritableTlvTrait<
-	    Cmd, std::void_t<decltype(TlvT<Cmd>::Write(std::declval<Cafe::Io::BinaryWriter&>()))>>
+	struct IsWritableTlvTrait<Cmd, std::void_t<decltype(TlvT<Cmd>::Write(
+	                                   std::declval<Cafe::Io::BinaryWriter<
+	                                       Cafe::Io::SeekableStream<Cafe::Io::OutputStream>>&>()))>>
 	    : std::true_type
 	{
 	};
@@ -46,8 +50,10 @@ namespace YumeBot::Tlv
 
 	template <std::uint16_t Cmd>
 	struct IsReadableTlvTrait<
-	    Cmd, std::void_t<decltype(TlvT<Cmd>::Read(std::declval<Cafe::Io::BinaryReader&>(),
-	                                              std::declval<std::size_t>()))>> : std::true_type
+	    Cmd,
+	    std::void_t<decltype(TlvT<Cmd>::Read(
+	        std::declval<Cafe::Io::BinaryReader<Cafe::Io::SeekableStream<Cafe::Io::InputStream>>&>(),
+	        std::declval<std::size_t>()))>> : std::true_type
 	{
 	};
 
@@ -57,23 +63,23 @@ namespace YumeBot::Tlv
 	class TlvBuilder
 	{
 	public:
-		explicit TlvBuilder(Cafe::Io::OutputStream* stream, std::size_t initialTlvCount = 0);
+		explicit TlvBuilder(Cafe::Io::SeekableStream<Cafe::Io::OutputStream>* stream,
+		                    std::size_t initialTlvCount = 0);
 
 		template <std::uint16_t Cmd>
 		void WriteTlv(TlvT<Cmd> const& tlv)
 		{
-			const auto seekableStream = dynamic_cast<Cafe::Io::SeekableStreamBase*>(m_Writer.GetStream());
-			assert(seekableStream);
+			const auto stream = m_Writer.GetStream();
 
 			m_Writer.Write(Cmd);
-			const auto lengthPos = seekableStream->GetPosition();
+			const auto lengthPos = stream->GetPosition();
 			m_Writer.Write(std::uint16_t{});
 
 			tlv.Write(m_Writer);
 
-			const auto length = seekableStream->GetPosition() - lengthPos - 2;
+			const auto length = stream->GetPosition() - lengthPos - 2;
 			assert(length <= std::numeric_limits<std::uint16_t>::max());
-			seekableStream->SeekFromBegin(lengthPos);
+			stream->SeekFromBegin(lengthPos);
 			m_Writer.Write(static_cast<std::uint16_t>(length));
 
 			++m_TlvCount;
@@ -82,22 +88,20 @@ namespace YumeBot::Tlv
 		std::size_t GetTlvCount() const noexcept;
 
 	private:
-		Cafe::Io::BinaryWriter m_Writer;
+		Cafe::Io::BinaryWriter<Cafe::Io::SeekableStream<Cafe::Io::OutputStream>> m_Writer;
 		std::size_t m_TlvCount;
 	};
 
 	class TlvReader
 	{
 	public:
-		explicit TlvReader(Cafe::Io::InputStream* stream);
+		explicit TlvReader(Cafe::Io::SeekableStream<Cafe::Io::InputStream>* stream);
 
 		template <std::uint16_t Cmd>
 		std::optional<TlvT<Cmd>> ReadTlv()
 		{
 			const auto stream = m_Reader.GetStream();
-			const auto seekableStream = dynamic_cast<Cafe::Io::SeekableStreamBase*>(stream);
-			assert(seekableStream);
-			seekableStream->SeekFromBegin(0);
+			stream->SeekFromBegin(0);
 
 			while (stream->GetAvailableBytes() > 4)
 			{
@@ -109,7 +113,7 @@ namespace YumeBot::Tlv
 				}
 				else
 				{
-					seekableStream->Seek(Cafe::Io::SeekOrigin::Current, bodySize);
+					stream->Seek(Cafe::Io::SeekOrigin::Current, bodySize);
 				}
 			}
 
@@ -117,13 +121,14 @@ namespace YumeBot::Tlv
 		}
 
 	private:
-		Cafe::Io::BinaryReader m_Reader;
+		Cafe::Io::BinaryReader<Cafe::Io::SeekableStream<Cafe::Io::InputStream>> m_Reader;
 	};
 
 	template <>
 	struct TlvT<0x1>
 	{
-		void Write(Cafe::Io::BinaryWriter& writer) const
+		void
+		Write(Cafe::Io::BinaryWriter<Cafe::Io::SeekableStream<Cafe::Io::OutputStream>>& writer) const
 		{
 			writer.Write(Uin);
 			writer.Write(ServerTime);
@@ -143,7 +148,8 @@ namespace YumeBot::Tlv
 	{
 		static constexpr std::uint16_t SigVer = 0;
 
-		void Write(Cafe::Io::BinaryWriter& writer) const
+		void
+		Write(Cafe::Io::BinaryWriter<Cafe::Io::SeekableStream<Cafe::Io::OutputStream>>& writer) const
 		{
 			writer.Write(SigVer);
 			const auto codeSize = Code.size();
@@ -165,7 +171,8 @@ namespace YumeBot::Tlv
 	template <>
 	struct TlvT<0x8>
 	{
-		void Write(Cafe::Io::BinaryWriter& writer) const
+		void
+		Write(Cafe::Io::BinaryWriter<Cafe::Io::SeekableStream<Cafe::Io::OutputStream>>& writer) const
 		{
 			writer.Write(TimeZoneVer);
 			writer.Write(static_cast<std::uint32_t>(LocaleId));
@@ -183,7 +190,8 @@ namespace YumeBot::Tlv
 		static constexpr std::uint16_t PingVersion = 1;
 		static constexpr std::uint32_t SsoVersion = 0x0600;
 
-		void Write(Cafe::Io::BinaryWriter& writer) const
+		void
+		Write(Cafe::Io::BinaryWriter<Cafe::Io::SeekableStream<Cafe::Io::OutputStream>>& writer) const
 		{
 			writer.Write(PingVersion);
 			writer.Write(SsoVersion);
@@ -206,7 +214,8 @@ namespace YumeBot::Tlv
 		static constexpr std::uint16_t DbBufVer = 1;
 		static constexpr std::uint32_t SsoVer = 5;
 
-		void Write(Cafe::Io::BinaryWriter& writer) const
+		void
+		Write(Cafe::Io::BinaryWriter<Cafe::Io::SeekableStream<Cafe::Io::OutputStream>>& writer) const
 		{
 			writer.Write(DbBufVer);
 			writer.Write(SsoVer);
@@ -225,7 +234,8 @@ namespace YumeBot::Tlv
 	template <>
 	struct TlvT<0x104>
 	{
-		void Write(Cafe::Io::BinaryWriter& writer) const
+		void
+		Write(Cafe::Io::BinaryWriter<Cafe::Io::SeekableStream<Cafe::Io::OutputStream>>& writer) const
 		{
 			writer.GetStream()->WriteBytes(SigSession);
 		}
@@ -239,11 +249,13 @@ namespace YumeBot::Tlv
 		static constexpr std::uint16_t TGTGTVer = 3;
 		static constexpr std::uint32_t SsoVer = 5;
 
-		void Write(Cafe::Io::BinaryWriter& writer) const
+		void
+		Write(Cafe::Io::BinaryWriter<Cafe::Io::SeekableStream<Cafe::Io::OutputStream>>& writer) const
 		{
 			std::byte unencryptedBody[98]{};
 			Cafe::Io::ExternalMemoryOutputStream unencryptedBodyStream{ gsl::make_span(unencryptedBody) };
-			Cafe::Io::BinaryWriter unencryptedBodyWriter{ &unencryptedBodyStream, std::endian::big };
+			Cafe::Io::BinaryWriter<Cafe::Io::SeekableStream<Cafe::Io::OutputStream>>
+			    unencryptedBodyWriter{ &unencryptedBodyStream, std::endian::big };
 
 			unencryptedBodyWriter.Write(TGTGTVer);
 
@@ -257,7 +269,7 @@ namespace YumeBot::Tlv
 			unencryptedBodyWriter.Write(AppId);
 			unencryptedBodyWriter.Write(ClientVer);
 			unencryptedBodyWriter.Write(Uin);
-			unencryptedBodyStream.WriteBytes(InitTime);
+			unencryptedBodyWriter.Write(InitTime);
 			if (!ClientIp.IsUnspecified())
 			{
 				unencryptedBodyStream.WriteBytes(gsl::as_bytes(gsl::make_span(ClientIp.Content)));
@@ -286,7 +298,9 @@ namespace YumeBot::Tlv
 
 			std::byte encryptKeyContent[24];
 			Cafe::Io::ExternalMemoryOutputStream encryptKeyStream{ gsl::make_span(encryptKeyContent) };
-			Cafe::Io::BinaryWriter encryptKeyWriter{ &encryptKeyStream, std::endian::big };
+			Cafe::Io::BinaryWriter<Cafe::Io::SeekableStream<Cafe::Io::OutputStream>> encryptKeyWriter{
+				&encryptKeyStream, std::endian::big
+			};
 
 			encryptKeyStream.WriteBytes(Md5);
 			if (MSalt)
@@ -314,7 +328,7 @@ namespace YumeBot::Tlv
 		std::uint32_t SubAppId;
 		std::uint32_t ClientVer;
 		std::uint64_t Uin;
-		gsl::span<const std::byte> InitTime;
+		std::uint32_t InitTime;
 		IpV4Addr ClientIp;
 		bool SavePwd;
 		gsl::span<const std::byte, 16> Md5;
@@ -328,7 +342,8 @@ namespace YumeBot::Tlv
 	template <>
 	struct TlvT<0x107>
 	{
-		void Write(Cafe::Io::BinaryWriter& writer) const
+		void
+		Write(Cafe::Io::BinaryWriter<Cafe::Io::SeekableStream<Cafe::Io::OutputStream>>& writer) const
 		{
 			writer.Write(PicType);
 			writer.Write(CapType);
@@ -345,12 +360,15 @@ namespace YumeBot::Tlv
 	template <>
 	struct TlvT<0x108>
 	{
-		void Write(Cafe::Io::BinaryWriter& writer) const
+		void
+		Write(Cafe::Io::BinaryWriter<Cafe::Io::SeekableStream<Cafe::Io::OutputStream>>& writer) const
 		{
 			writer.GetStream()->WriteBytes(gsl::make_span(Ksid));
 		}
 
-		static TlvT<0x108> Read(Cafe::Io::BinaryReader& reader, std::size_t bodySize)
+		static TlvT<0x108>
+		Read(Cafe::Io::BinaryReader<Cafe::Io::SeekableStream<Cafe::Io::InputStream>>& reader,
+		     std::size_t bodySize)
 		{
 			TlvT<0x108> tlv{ std::vector<std::byte>(bodySize) };
 			reader.GetStream()->ReadBytes(gsl::make_span(tlv.Ksid));
@@ -363,19 +381,20 @@ namespace YumeBot::Tlv
 	template <>
 	struct TlvT<0x109>
 	{
-		void Write(Cafe::Io::BinaryWriter& writer) const
+		void
+		Write(Cafe::Io::BinaryWriter<Cafe::Io::SeekableStream<Cafe::Io::OutputStream>>& writer) const
 		{
-			const auto imei = gsl::as_bytes(Imei.GetTrimmedSpan());
-			writer.GetStream()->WriteBytes(imei);
+			writer.GetStream()->WriteBytes(Imei);
 		}
 
-		UsingStringView Imei;
+		gsl::span<const std::byte, 16> Imei;
 	};
 
 	template <>
 	struct TlvT<0x10A>
 	{
-		void Write(Cafe::Io::BinaryWriter& writer) const
+		void
+		Write(Cafe::Io::BinaryWriter<Cafe::Io::SeekableStream<Cafe::Io::OutputStream>>& writer) const
 		{
 			writer.GetStream()->WriteBytes(TGT);
 		}
@@ -386,7 +405,8 @@ namespace YumeBot::Tlv
 	template <>
 	struct TlvT<0x112>
 	{
-		void Write(Cafe::Io::BinaryWriter& writer) const
+		void
+		Write(Cafe::Io::BinaryWriter<Cafe::Io::SeekableStream<Cafe::Io::OutputStream>>& writer) const
 		{
 			writer.GetStream()->WriteBytes(gsl::as_bytes(Name.GetTrimmedSpan()));
 		}
@@ -399,7 +419,8 @@ namespace YumeBot::Tlv
 	{
 		static constexpr std::uint8_t Ver = 0;
 
-		void Write(Cafe::Io::BinaryWriter& writer) const
+		void
+		Write(Cafe::Io::BinaryWriter<Cafe::Io::SeekableStream<Cafe::Io::OutputStream>>& writer) const
 		{
 			writer.Write(Ver);
 			writer.Write(Bitmap);
@@ -421,7 +442,8 @@ namespace YumeBot::Tlv
 	template <>
 	struct TlvT<0x124>
 	{
-		void Write(Cafe::Io::BinaryWriter& writer) const
+		void
+		Write(Cafe::Io::BinaryWriter<Cafe::Io::SeekableStream<Cafe::Io::OutputStream>>& writer) const
 		{
 			const auto osTypeTrimmedSpan = gsl::as_bytes(OsType.GetTrimmedSpan());
 			const auto osType =
@@ -469,7 +491,8 @@ namespace YumeBot::Tlv
 	{
 		static constexpr std::uint16_t Version = 0;
 
-		void Write(Cafe::Io::BinaryWriter& writer) const
+		void
+		Write(Cafe::Io::BinaryWriter<Cafe::Io::SeekableStream<Cafe::Io::OutputStream>>& writer) const
 		{
 			writer.Write(Version);
 
@@ -492,7 +515,8 @@ namespace YumeBot::Tlv
 	template <>
 	struct TlvT<0x128>
 	{
-		void Write(Cafe::Io::BinaryWriter& writer) const
+		void
+		Write(Cafe::Io::BinaryWriter<Cafe::Io::SeekableStream<Cafe::Io::OutputStream>>& writer) const
 		{
 			const auto deviceTypeTrimmedSpan = gsl::as_bytes(DeviceType.GetTrimmedSpan());
 			const auto deviceType = deviceTypeTrimmedSpan.subspan(
@@ -531,12 +555,34 @@ namespace YumeBot::Tlv
 		UsingStringView Brand;
 	};
 
+	template <>
+	struct TlvT<0x130>
+	{
+		static TlvT<0x130>
+		Read(Cafe::Io::BinaryReader<Cafe::Io::SeekableStream<Cafe::Io::InputStream>>& reader,
+		     std::size_t bodySize)
+		{
+			TlvT<0x130> result;
+
+			reader.GetStream()->Skip(2);
+			reader.Read(result.InitTime);
+			reader.GetStream()->ReadBytes(
+			    gsl::as_writeable_bytes(gsl::make_span(result.ClientIp.Content)));
+
+			return result;
+		}
+
+		std::uint32_t InitTime;
+		IpV4Addr ClientIp;
+	};
+
 	template <>
 	struct TlvT<0x141>
 	{
 		static constexpr std::uint16_t Version = 1;
 
-		void Write(Cafe::Io::BinaryWriter& writer) const
+		void
+		Write(Cafe::Io::BinaryWriter<Cafe::Io::SeekableStream<Cafe::Io::OutputStream>>& writer) const
 		{
 			const auto operatorName = gsl::as_bytes(OperatorName.GetTrimmedSpan());
 			const auto apn = gsl::as_bytes(Apn.GetTrimmedSpan());
@@ -568,7 +614,8 @@ namespace YumeBot::Tlv
 	{
 		static constexpr std::uint16_t Version = 0;
 
-		void Write(Cafe::Io::BinaryWriter& writer) const
+		void
+		Write(Cafe::Io::BinaryWriter<Cafe::Io::SeekableStream<Cafe::Io::OutputStream>>& writer) const
 		{
 			const auto id = gsl::as_bytes(Id.GetTrimmedSpan());
 			const auto idSize = id.size();
@@ -586,7 +633,8 @@ namespace YumeBot::Tlv
 	template <>
 	struct TlvT<0x143>
 	{
-		void Write(Cafe::Io::BinaryWriter& writer) const
+		void
+		Write(Cafe::Io::BinaryWriter<Cafe::Io::SeekableStream<Cafe::Io::OutputStream>>& writer) const
 		{
 			writer.GetStream()->WriteBytes(B);
 		}
@@ -597,12 +645,14 @@ namespace YumeBot::Tlv
 	template <>
 	struct TlvT<0x144>
 	{
-		void Write(Cafe::Io::BinaryWriter& writer) const
+		void
+		Write(Cafe::Io::BinaryWriter<Cafe::Io::SeekableStream<Cafe::Io::OutputStream>>& writer) const
 		{
 			const std::uint16_t tlvCount = !_109.empty() + !_124.empty() + !_128.empty() + !_148.empty() +
 			                               !_151.empty() + !_153.empty() + !_16e.empty();
 			Cafe::Io::MemoryStream unencryptedDataStream;
-			Cafe::Io::BinaryWriter unencryptedDataWriter{ &unencryptedDataStream, std::endian::big };
+			Cafe::Io::BinaryWriter<Cafe::Io::SeekableStream<Cafe::Io::OutputStream>>
+			    unencryptedDataWriter{ &unencryptedDataStream, std::endian::big };
 			unencryptedDataWriter.Write(tlvCount);
 			unencryptedDataStream.WriteBytes(_109);
 			unencryptedDataStream.WriteBytes(_124);
@@ -633,7 +683,8 @@ namespace YumeBot::Tlv
 	template <>
 	struct TlvT<0x145>
 	{
-		void Write(Cafe::Io::BinaryWriter& writer) const
+		void
+		Write(Cafe::Io::BinaryWriter<Cafe::Io::SeekableStream<Cafe::Io::OutputStream>>& writer) const
 		{
 			writer.GetStream()->WriteBytes(Guid);
 		}
@@ -644,7 +695,8 @@ namespace YumeBot::Tlv
 	template <>
 	struct TlvT<0x147>
 	{
-		void Write(Cafe::Io::BinaryWriter& writer) const
+		void
+		Write(Cafe::Io::BinaryWriter<Cafe::Io::SeekableStream<Cafe::Io::OutputStream>>& writer) const
 		{
 			const auto appVerTrimmedSpan = gsl::as_bytes(AppVer.GetTrimmedSpan());
 			const auto appVer =
@@ -667,7 +719,8 @@ namespace YumeBot::Tlv
 	template <>
 	struct TlvT<0x148>
 	{
-		void Write(Cafe::Io::BinaryWriter& writer) const
+		void
+		Write(Cafe::Io::BinaryWriter<Cafe::Io::SeekableStream<Cafe::Io::OutputStream>>& writer) const
 		{
 			const auto appNameTrimmedSpan = gsl::as_bytes(AppName.GetTrimmedSpan());
 			const auto appName =
@@ -706,7 +759,8 @@ namespace YumeBot::Tlv
 	template <>
 	struct TlvT<0x153>
 	{
-		void Write(Cafe::Io::BinaryWriter& writer) const
+		void
+		Write(Cafe::Io::BinaryWriter<Cafe::Io::SeekableStream<Cafe::Io::OutputStream>>& writer) const
 		{
 			writer.Write(IsRoot);
 		}
@@ -717,7 +771,8 @@ namespace YumeBot::Tlv
 	template <>
 	struct TlvT<0x154>
 	{
-		void Write(Cafe::Io::BinaryWriter& writer) const
+		void
+		Write(Cafe::Io::BinaryWriter<Cafe::Io::SeekableStream<Cafe::Io::OutputStream>>& writer) const
 		{
 			writer.Write(SsoSeq);
 		}
@@ -728,7 +783,8 @@ namespace YumeBot::Tlv
 	template <>
 	struct TlvT<0x166>
 	{
-		void Write(Cafe::Io::BinaryWriter& writer) const
+		void
+		Write(Cafe::Io::BinaryWriter<Cafe::Io::SeekableStream<Cafe::Io::OutputStream>>& writer) const
 		{
 			writer.Write(ImgType);
 		}
@@ -739,7 +795,8 @@ namespace YumeBot::Tlv
 	template <>
 	struct TlvT<0x16A>
 	{
-		void Write(Cafe::Io::BinaryWriter& writer) const
+		void
+		Write(Cafe::Io::BinaryWriter<Cafe::Io::SeekableStream<Cafe::Io::OutputStream>>& writer) const
 		{
 			writer.GetStream()->WriteBytes(NoPicSig);
 		}
@@ -750,7 +807,8 @@ namespace YumeBot::Tlv
 	template <>
 	struct TlvT<0x16B>
 	{
-		void Write(Cafe::Io::BinaryWriter& writer) const
+		void
+		Write(Cafe::Io::BinaryWriter<Cafe::Io::SeekableStream<Cafe::Io::OutputStream>>& writer) const
 		{
 			const auto domainSize = Domains.size();
 			assert(domainSize <= std::numeric_limits<std::uint16_t>::max());
@@ -772,7 +830,8 @@ namespace YumeBot::Tlv
 	template <>
 	struct TlvT<0x16E>
 	{
-		void Write(Cafe::Io::BinaryWriter& writer) const
+		void
+		Write(Cafe::Io::BinaryWriter<Cafe::Io::SeekableStream<Cafe::Io::OutputStream>>& writer) const
 		{
 			writer.GetStream()->WriteBytes(gsl::as_bytes(DeviceName.GetTrimmedSpan()));
 		}
@@ -783,7 +842,8 @@ namespace YumeBot::Tlv
 	template <>
 	struct TlvT<0x172>
 	{
-		void Write(Cafe::Io::BinaryWriter& writer) const
+		void
+		Write(Cafe::Io::BinaryWriter<Cafe::Io::SeekableStream<Cafe::Io::OutputStream>>& writer) const
 		{
 			writer.GetStream()->WriteBytes(RollbackSig);
 		}
@@ -794,7 +854,8 @@ namespace YumeBot::Tlv
 	template <>
 	struct TlvT<0x174>
 	{
-		void Write(Cafe::Io::BinaryWriter& writer) const
+		void
+		Write(Cafe::Io::BinaryWriter<Cafe::Io::SeekableStream<Cafe::Io::OutputStream>>& writer) const
 		{
 			writer.GetStream()->WriteBytes(SecSig);
 		}
@@ -805,7 +866,8 @@ namespace YumeBot::Tlv
 	template <>
 	struct TlvT<0x177>
 	{
-		void Write(Cafe::Io::BinaryWriter& writer) const
+		void
+		Write(Cafe::Io::BinaryWriter<Cafe::Io::SeekableStream<Cafe::Io::OutputStream>>& writer) const
 		{
 			const auto buildVersion = gsl::as_bytes(BuildVersion.GetTrimmedSpan());
 			const auto buildVersionSize = buildVersion.size();
@@ -825,7 +887,8 @@ namespace YumeBot::Tlv
 	template <>
 	struct TlvT<0x17A>
 	{
-		void Write(Cafe::Io::BinaryWriter& writer) const
+		void
+		Write(Cafe::Io::BinaryWriter<Cafe::Io::SeekableStream<Cafe::Io::OutputStream>>& writer) const
 		{
 			writer.Write(SmsAppId);
 		}
@@ -836,7 +899,8 @@ namespace YumeBot::Tlv
 	template <>
 	struct TlvT<0x17C>
 	{
-		void Write(Cafe::Io::BinaryWriter& writer) const
+		void
+		Write(Cafe::Io::BinaryWriter<Cafe::Io::SeekableStream<Cafe::Io::OutputStream>>& writer) const
 		{
 			const auto smsCodeSize = SmsCode.size();
 			assert(smsCodeSize <= std::numeric_limits<std::uint16_t>::max());
@@ -851,7 +915,8 @@ namespace YumeBot::Tlv
 	template <>
 	struct TlvT<0x183>
 	{
-		void Write(Cafe::Io::BinaryWriter& writer) const
+		void
+		Write(Cafe::Io::BinaryWriter<Cafe::Io::SeekableStream<Cafe::Io::OutputStream>>& writer) const
 		{
 			writer.Write(MSalt);
 		}
@@ -862,7 +927,8 @@ namespace YumeBot::Tlv
 	template <>
 	struct TlvT<0x184>
 	{
-		void Write(Cafe::Io::BinaryWriter& writer) const
+		void
+		Write(Cafe::Io::BinaryWriter<Cafe::Io::SeekableStream<Cafe::Io::OutputStream>>& writer) const
 		{
 			const auto mPassword = gsl::as_bytes(MPassword.GetTrimmedSpan());
 
@@ -870,7 +936,9 @@ namespace YumeBot::Tlv
 			Cryptography::Md5::Calculate(mPassword, gsl::make_span(body).subspan(0, 16));
 
 			Cafe::Io::ExternalMemoryOutputStream stream{ gsl::make_span(body) };
-			Cafe::Io::BinaryWriter{ &stream, std::endian::big }.Write(MSalt);
+			Cafe::Io::BinaryWriter<Cafe::Io::SeekableStream<Cafe::Io::OutputStream>>{ &stream,
+				                                                                        std::endian::big }
+			    .Write(MSalt);
 
 			std::byte md5Body[16];
 			Cryptography::Md5::Calculate(gsl::make_span(body), gsl::make_span(md5Body));
@@ -885,7 +953,8 @@ namespace YumeBot::Tlv
 	template <>
 	struct TlvT<0x185>
 	{
-		void Write(Cafe::Io::BinaryWriter& writer) const
+		void
+		Write(Cafe::Io::BinaryWriter<Cafe::Io::SeekableStream<Cafe::Io::OutputStream>>& writer) const
 		{
 			writer.Write(std::uint8_t{ 1 });
 			writer.Write(Flag);
@@ -897,31 +966,33 @@ namespace YumeBot::Tlv
 	template <>
 	struct TlvT<0x187>
 	{
-		void Write(Cafe::Io::BinaryWriter& writer) const
+		void
+		Write(Cafe::Io::BinaryWriter<Cafe::Io::SeekableStream<Cafe::Io::OutputStream>>& writer) const
 		{
-			const auto mac = gsl::as_bytes(Mac.GetTrimmedSpan());
-			writer.GetStream()->WriteBytes(mac);
+			writer.GetStream()->WriteBytes(Mac);
 		}
 
-		UsingStringView Mac;
+		gsl::span<const std::byte, 16> Mac;
 	};
 
 	template <>
 	struct TlvT<0x188>
 	{
-		void Write(Cafe::Io::BinaryWriter& writer) const
+		void
+		Write(Cafe::Io::BinaryWriter<Cafe::Io::SeekableStream<Cafe::Io::OutputStream>>& writer) const
 		{
-			const auto androidID = gsl::as_bytes(AndroidID.GetTrimmedSpan());
-			writer.GetStream()->WriteBytes(androidID);
+			writer.GetStream()->WriteBytes(AndroidID);
 		}
 
-		UsingStringView AndroidID;
+		gsl::span<const std::byte, 16> AndroidID;
 	};
 
 	template <>
 	struct TlvT<0x305>
 	{
-		static TlvT<0x305> Read(Cafe::Io::BinaryReader& reader, std::size_t bodySize)
+		static TlvT<0x305>
+		Read(Cafe::Io::BinaryReader<Cafe::Io::SeekableStream<Cafe::Io::InputStream>>& reader,
+		     std::size_t bodySize)
 		{
 			TlvT<0x305> tlv{ std::vector<std::byte>(bodySize) };
 			reader.GetStream()->ReadBytes(gsl::make_span(tlv.SessionKey));

+ 14 - 15
YumeBot/Wup.cpp

@@ -20,13 +20,13 @@ bool OldUniAttribute::Remove(UsingString const& name)
 	return !!m_Data.erase(name);
 }
 
-void OldUniAttribute::Encode(Cafe::Io::OutputStream* stream) const
+void OldUniAttribute::Encode(Cafe::Io::SeekableStream<Cafe::Io::OutputStream>* stream) const
 {
 	JceOutputStream output{ stream };
 	output.Write(0, m_Data);
 }
 
-void OldUniAttribute::Decode(Cafe::Io::InputStream* stream)
+void OldUniAttribute::Decode(Cafe::Io::SeekableStream<Cafe::Io::InputStream>* stream)
 {
 	JceInputStream input{ stream };
 	m_Data.clear();
@@ -40,35 +40,33 @@ UniPacket::UniPacket() : m_OldRespIRet{}
 {
 }
 
-void UniPacket::Encode(Cafe::Io::OutputStream* stream)
+void UniPacket::Encode(Cafe::Io::SeekableStream<Cafe::Io::OutputStream>* stream)
 {
 	MemoryStream tmpBuffer;
 	m_UniAttribute.Encode(&tmpBuffer);
 	const auto buffer = tmpBuffer.GetInternalStorage();
 	m_RequestPacket.GetsBuffer().assign(buffer.data(), buffer.data() + buffer.size());
 
-	Cafe::Io::BinaryWriter writer{ stream, std::endian::little };
+	Cafe::Io::BinaryWriter<Cafe::Io::SeekableStream<Cafe::Io::OutputStream>> writer{
+		stream, std::endian::little
+	};
 
-	const auto underlyingStream = dynamic_cast<SeekableStreamBase*>(stream);
-	assert(underlyingStream);
-	const auto sizePos = underlyingStream->GetPosition();
+	const auto sizePos = stream->GetPosition();
 	// 占位 4 字节以便之后返回写入长度信息
 	writer.Write(std::uint32_t{});
 
 	JceOutputStream os{ stream };
 	os.Write(0, m_RequestPacket);
-	const auto endPos = underlyingStream->GetPosition();
+	const auto endPos = stream->GetPosition();
 	const auto length = endPos - sizePos;
-	underlyingStream->SeekFromBegin(sizePos);
+	stream->SeekFromBegin(sizePos);
 	writer.Write(static_cast<std::int32_t>(length));
-	underlyingStream->SeekFromBegin(endPos);
+	stream->SeekFromBegin(endPos);
 }
 
-void UniPacket::Decode(Cafe::Io::InputStream* stream)
+void UniPacket::Decode(Cafe::Io::SeekableStream<Cafe::Io::InputStream>* stream)
 {
-	const auto underlyingStream = dynamic_cast<SeekableStreamBase*>(stream);
-	assert(underlyingStream);
-	underlyingStream->Seek(SeekOrigin::Current, 4);
+	stream->Seek(SeekOrigin::Current, 4);
 	JceInputStream is{ stream };
 	if (!is.Read(0, m_RequestPacket))
 	{
@@ -76,7 +74,8 @@ void UniPacket::Decode(Cafe::Io::InputStream* stream)
 	}
 
 	const auto& buffer = m_RequestPacket.GetsBuffer();
-	ExternalMemoryInputStream bufferStream{ gsl::as_bytes(gsl::make_span(buffer.data(), buffer.size())) };
+	ExternalMemoryInputStream bufferStream{ gsl::as_bytes(
+		  gsl::make_span(buffer.data(), buffer.size())) };
 	m_UniAttribute.Decode(&bufferStream);
 }
 

+ 4 - 4
YumeBot/Wup.h

@@ -164,8 +164,8 @@ namespace YumeBot::Jce::Wup
 
 		bool Remove(UsingString const& name);
 
-		void Encode(Cafe::Io::OutputStream* stream) const;
-		void Decode(Cafe::Io::InputStream* stream);
+		void Encode(Cafe::Io::SeekableStream<Cafe::Io::OutputStream>* stream) const;
+		void Decode(Cafe::Io::SeekableStream<Cafe::Io::InputStream>* stream);
 
 	private:
 		std::unordered_map<UsingString, std::unordered_map<UsingString, std::vector<std::byte>>> m_Data;
@@ -176,8 +176,8 @@ namespace YumeBot::Jce::Wup
 	public:
 		UniPacket();
 
-		void Encode(Cafe::Io::OutputStream* stream);
-		void Decode(Cafe::Io::InputStream* stream);
+		void Encode(Cafe::Io::SeekableStream<Cafe::Io::OutputStream>* stream);
+		void Decode(Cafe::Io::SeekableStream<Cafe::Io::InputStream>* stream);
 
 		UniPacket CreateResponse();
 		void CreateOldRespEncode(JceOutputStream& os);

+ 1 - 1
conanfile.txt

@@ -1,7 +1,7 @@
 [requires]
 Cafe/0.1@Chino/Cafe
 Catch2/2.9.2@catchorg/stable
-OpenSSL/1.1.1d@conan/stable
+OpenSSL/1.1.1@conan/stable
 
 [generators]
 cmake