Forráskód Böngészése

添加 Jce 部分

akemimadoka 7 éve
szülő
commit
19f55d42dc
7 módosított fájl, 548 hozzáadás és 3 törlés
  1. 4 0
      QQBot.Test/CryptographyTest.cpp
  2. 27 0
      QQBot/Jce.cpp
  3. 284 0
      QQBot/Jce.h
  4. 2 0
      QQBot/QQBot.vcxproj
  5. 6 0
      QQBot/QQBot.vcxproj.filters
  6. 101 0
      QQBot/TlvCodeDef.h
  7. 124 3
      QQBot/Utility.h

+ 4 - 0
QQBot.Test/CryptographyTest.cpp

@@ -16,6 +16,10 @@ TEST_CASE("Cryptography", "[Utility][Cryptography]")
 
 		char result[CalculateOutputSize(std::size(text))]{};
 		const auto formattedKey = FormatKey(Utility::ToByteSpan(key));
+
+		// 0x30303030 == { '0', '0', '0', '0' }
+		REQUIRE(std::all_of(std::cbegin(formattedKey), std::cend(formattedKey), [](std::uint32_t value) constexpr { return value == 0x30303030; }));
+
 		const auto resultSize = Encrypt(Utility::ToByteSpan(text), Utility::ToByteSpan(result), formattedKey);
 		char decryptResult[std::size(result)]{};
 		const auto decryptResultSize = Decrypt(Utility::ToByteSpan(result), Utility::ToByteSpan(decryptResult), formattedKey);

+ 27 - 0
QQBot/Jce.cpp

@@ -0,0 +1,27 @@
+#include "Jce.h"
+
+using namespace NatsuLib;
+using namespace QQBot;
+using namespace Jce;
+
+JceStruct::~JceStruct()
+{
+}
+
+JceInputStream::JceInputStream(natRefPointer<natBinaryReader> reader)
+	: m_Reader{ std::move(reader) }
+{
+}
+
+JceInputStream::~JceInputStream()
+{
+}
+
+JceOutputStream::JceOutputStream(natRefPointer<natBinaryWriter> writer)
+	: m_Writer{ std::move(writer) }
+{
+}
+
+JceOutputStream::~JceOutputStream()
+{
+}

+ 284 - 0
QQBot/Jce.h

@@ -1,7 +1,291 @@
 #pragma once
 #include <natBinary.h>
+#include <optional>
+#include "Utility.h"
 
 namespace QQBot::Jce
 {
+	DeclareException(InvalidData, NatsuLib::natException, u8"Invalid data");
+
+	class JceStruct
+		: public NatsuLib::natRefObj
+	{
+	public:
+#define JCE_FIELD_TYPE(OP)\
+	OP(Byte, 0x00, std::uint8_t)\
+	OP(Short, 0x01, std::int16_t)\
+	OP(Int, 0x02, std::int32_t)\
+	OP(Long, 0x03, std::int64_t)\
+	OP(Float, 0x04, float)\
+	OP(Double, 0x05, double)\
+	OP(String1, 0x06, std::string)\
+	OP(String4, 0x07, std::string)\
+	OP(Map, 0x08, std::unordered_map)\
+	OP(List, 0x09, std::vector)\
+	OP(StructBegin, 0x0A, NatsuLib::natRefPointer)\
+	OP(StructEnd, 0x0B, void)\
+	OP(ZeroTag, 0x0C, (std::integral_constant<std::size_t, 0>))\
+	OP(SimpleList, 0x0D, std::vector<std::uint8_t>)
+
+		enum class TypeEnum : std::uint8_t
+		{
+#define ENUM_OP(name, code, type) name = code,
+			JCE_FIELD_TYPE(ENUM_OP)
+#undef ENUM_OP
+		};
+
+		static constexpr std::size_t MaxStringLength = 0x06400000;
+
+		~JceStruct();
+
+	private:
+
+	};
+
+	template <typename T>
+	struct TlvDeserializer;
+
+	template <typename T>
+	struct TlvSerializer;
+
+	struct NoOp
+	{
+		template <typename T>
+		struct Apply
+			: Utility::ResultType<T>
+		{
+		};
+	};
+
+	struct IsOptional
+	{
+		template <typename T>
+		struct Apply
+			: Utility::ResultType<std::optional<T>>
+		{
+		};
+	};
+
+	template <typename... Args>
+	struct TemplateArgs
+	{
+		template <template <typename...> class Template>
+		struct Apply
+			: Utility::ResultType<Template<Args...>>
+		{
+		};
+	};
+
+	template <JceStruct::TypeEnum Type, typename AttributeSet>
+	struct FieldTypeBuilder;
+
+#define FIELD_TYPE_BUILDER_OP(name, code, type) \
+	template <typename... Attributes>\
+	struct FieldTypeBuilder<JceStruct::TypeEnum::name, std::tuple<Attributes...>>\
+		: Utility::ResultType<decltype(Utility::RecursiveApply<type, Attributes...>())>\
+	{\
+	};
+
+	JCE_FIELD_TYPE(FIELD_TYPE_BUILDER_OP)
+
+#undef FIELD_TYPE_BUILDER_OP
+
+	class JceInputStream
+		: NatsuLib::noncopyable
+	{
+	public:
+		struct HeadData
+		{
+			std::uint32_t Tag;
+			std::uint8_t Type;
+		};
+
+		explicit JceInputStream(NatsuLib::natRefPointer<NatsuLib::natBinaryReader> reader);
+		~JceInputStream();
+
+		HeadData ReadHead() const
+		{
+			const auto byte = m_Reader->ReadPod<std::uint8_t>();
+			const auto type = static_cast<std::uint8_t>(byte & 0x0F);
+			auto tag = static_cast<std::uint32_t>((byte & 0xF0) >> 4);
+			if (tag == 0x0F)
+			{
+				tag = m_Reader->ReadPod<std::uint8_t>();
+			}
+
+			return { tag, type };
+		}
+
+		HeadData PeekHead() const
+		{
+			const auto underlyingStream = m_Reader->GetUnderlyingStream();
+			const auto pos = underlyingStream->GetPosition();
+			const auto head = ReadHead();
+			underlyingStream->SetPositionFromBegin(pos);
+			return head;
+		}
+
+		void Skip(nLen len) const
+		{
+			m_Reader->Skip(len);
+		}
+
+		template <JceStruct::TypeEnum Type, typename T>
+		bool Read(std::size_t tag, T& value, std::nullptr_t = nullptr)
+		{
+			return Reader<Type, T>::DoRead(*this, tag, value);
+		}
+
+		template <JceStruct::TypeEnum Type, typename T, typename U>
+		void Read(std::size_t tag, T& value, U&& defaultValue)
+		{
+			if (!Reader<Type, T>::DoRead(*this, tag, value))
+			{
+				value = std::forward<U>(defaultValue);
+			}
+		}
+
+	private:
+		NatsuLib::natRefPointer<NatsuLib::natBinaryReader> m_Reader;
+
+		template <JceStruct::TypeEnum Type, typename T, typename = void>
+		struct Reader;
+
+		template <JceStruct::TypeEnum Type, typename T>
+		struct Reader<Type, T, std::enable_if_t<std::is_pod_v<T>>>
+		{
+			static bool DoRead(JceInputStream& self, std::size_t tag, T& value)
+			{
+				const auto head = self.ReadHead();
+				if (static_cast<JceStruct::TypeEnum>(head.Type) != Type)
+				{
+					nat_Throw(InvalidData);
+				}
+
+				self.m_Reader->ReadPod(value);
+				return true;
+			}
+		};
+	};
+
+	class JceOutputStream
+		: NatsuLib::noncopyable
+	{
+	public:
+		explicit JceOutputStream(NatsuLib::natRefPointer<NatsuLib::natBinaryWriter> writer);
+		~JceOutputStream();
+
+		template <JceStruct::TypeEnum Type, typename T>
+		void Write(std::size_t tag, T const& value)
+		{
+			Writer<Type, T>::DoWrite(*this, tag, value);
+		}
+
+		template <JceStruct::TypeEnum Type, typename T>
+		void Write(std::size_t tag, std::optional<T> const& value)
+		{
+			if (value.has_value())
+			{
+				Write(tag, value.value());
+			}
+		}
+
+	private:
+		NatsuLib::natRefPointer<NatsuLib::natBinaryWriter> m_Writer;
+
+		template <JceStruct::TypeEnum Type, typename T, typename = void>
+		struct Writer;
+
+		template <JceStruct::TypeEnum Type, typename T>
+		struct Writer<Type, T, std::enable_if_t<std::is_pod_v<T>>>
+		{
+			static void DoWrite(JceOutputStream& self, std::size_t tag, T const& value)
+			{
+				
+			}
+		};
+	};
+
+	enum class Code
+	{
+#define TLV_CODE(name, code, ...) name = code,
+#include "TlvCodeDef.h"
+	};
+
+#define NO_OP NoOp
+
+#define IS_OPTIONAL(defaultValue) IsOptional
+
+#define TEMPLATE_ARGUMENT(...) TemplateArgs<__VA_ARGS__>
+
+#define FIELD(name, tag, type, attribute) \
+	public:\
+		typename FieldTypeBuilder<JceStruct::TypeEnum::type, std::tuple<attribute>>::Type::Type m_##name;\
+		\
+	public:\
+		const auto& Get##name() const noexcept\
+		{\
+			return m_##name;\
+		}\
+		\
+		auto& Get##name() noexcept\
+		{\
+			return m_##name;\
+		}\
+		\
+		template <typename T>\
+		void Set##name(T&& arg)\
+		{\
+			m_##name = std::forward<T>(arg);\
+		}\
+		\
+		static constexpr std::size_t Get##name##Tag() noexcept\
+		{\
+			return tag;\
+		}
+
+#define TLV_CODE(name, code, ...) \
+	class name\
+		: public NatsuLib::natRefObjImpl<name, JceStruct>\
+	{\
+		__VA_ARGS__\
+	};
+
+#include "TlvCodeDef.h"
+
+#define NO_OP nullptr
+
+#define IS_OPTIONAL(defaultValue) defaultValue
+
+#define FIELD(name, tag, type, attribute) stream.Read<JceStruct::TypeEnum::type>(tag, ret->Get##name(),\
+	Utility::ReturnFirst<Utility::ConcatTrait<Utility::BindTrait<std::is_same, std::nullptr_t>::template Result, std::negation>::template Result, std::nullptr_t>(attribute));
+
+#define TLV_CODE(name, code, ...) \
+	template <>\
+	struct TlvDeserializer<name>\
+	{\
+		static NatsuLib::natRefPointer<name> Deserialize(JceInputStream& stream)\
+		{\
+			auto ret = NatsuLib::make_ref<name>();\
+			__VA_ARGS__\
+			return ret;\
+		}\
+	};
+
+#include "TlvCodeDef.h"
+
+#define FIELD(name, tag, type, attribute) stream->Write<JceStruct::TypeEnum::type>(tag, value->Get##name());
+
+#define TLV_CODE(name, code, ...) \
+	template <>\
+	struct TlvSerializer<name>\
+	{\
+		static void Serialize(JceOutputStream& stream, NatsuLib::natRefPointer<name> const& value)\
+		{\
+			__VA_ARGS__\
+		}\
+	};
+
+#include "TlvCodeDef.h"
 
 }

+ 2 - 0
QQBot/QQBot.vcxproj

@@ -22,10 +22,12 @@
     <ClInclude Include="Cryptography.h" />
     <ClInclude Include="Jce.h" />
     <ClInclude Include="QQBot.h" />
+    <ClInclude Include="TlvCodeDef.h" />
     <ClInclude Include="Utility.h" />
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="Cryptography.cpp" />
+    <ClCompile Include="Jce.cpp" />
     <ClCompile Include="QQBot.cpp" />
   </ItemGroup>
   <PropertyGroup Label="Globals">

+ 6 - 0
QQBot/QQBot.vcxproj.filters

@@ -27,6 +27,9 @@
     <ClInclude Include="Jce.h">
       <Filter>头文件</Filter>
     </ClInclude>
+    <ClInclude Include="TlvCodeDef.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="QQBot.cpp">
@@ -35,5 +38,8 @@
     <ClCompile Include="Cryptography.cpp">
       <Filter>源文件</Filter>
     </ClCompile>
+    <ClCompile Include="Jce.cpp">
+      <Filter>源文件</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>

+ 101 - 0
QQBot/TlvCodeDef.h

@@ -0,0 +1,101 @@
+#ifndef TLV_CODE
+#define TLV_CODE(name, code, ...)
+#endif
+
+#ifndef ATTRIBUTE_SET
+#define ATTRIBUTE_SET(...) __VA_ARGS__
+#endif
+
+#ifndef NO_OP
+#define NO_OP
+#endif
+
+#ifndef IS_OPTIONAL
+#define IS_OPTIONAL(defaultValue) NO_OP
+#endif
+
+#ifndef TEMPLATE_ARGUMENT
+#define TEMPLATE_ARGUMENT(...) NO_OP
+#endif
+
+#ifndef FIELD
+#define FIELD(name, tag, type, attribute)
+#endif
+
+#ifndef BYTE
+#define BYTE(name, tag, attribute) FIELD(name, tag, Byte, attribute)
+#endif
+
+#ifndef SHORT
+#define SHORT(name, tag, attribute) FIELD(name, tag, Short, attribute)
+#endif
+
+#ifndef INT
+#define INT(name, tag, attribute) FIELD(name, tag, Int, attribute)
+#endif
+
+#ifndef LONG
+#define LONG(name, tag, attribute) FIELD(name, tag, Long, attribute)
+#endif
+
+#ifndef FLOAT
+#define FLOAT(name, tag, attribute) FIELD(name, tag, Float, attribute)
+#endif
+
+#ifndef DOUBLE
+#define DOUBLE(name, tag, attribute) FIELD(name, tag, Double, attribute)
+#endif
+
+#ifndef STRING1
+#define STRING1(name, tag, attribute) FIELD(name, tag, String1, attribute)
+#endif
+
+#ifndef STRING4
+#define STRING4(name, tag, attribute) FIELD(name, tag, String4, attribute)
+#endif
+
+#ifndef MAP
+#define MAP(name, tag, attribute) FIELD(name, tag, Map, attribute)
+#endif
+
+#ifndef LIST
+#define LIST(name, tag, attribute) FIELD(name, tag, List, attribute)
+#endif
+
+#ifndef STRUCT
+#define STRUCT(name, tag, attribute) FIELD(name, tag, StructBegin, attribute)
+#endif
+
+#ifndef ZERO_TAG
+#define ZERO_TAG(name, tag, attribute) FIELD(name, tag, ZeroTag, attribute)
+#endif
+
+#ifndef SIMPLE_LIST
+#define SIMPLE_LIST(name, tag, attribute) FIELD(name, tag, SimpleList, attribute)
+#endif
+
+TLV_CODE(TlvTest, 0, INT(TestInt, 0, ATTRIBUTE_SET()), FLOAT(TestFloat, 1, ATTRIBUTE_SET(IS_OPTIONAL(1.0f))))
+
+#undef SIMPLE_LIST
+#undef ZERO_TAG
+#undef STRUCT
+#undef STRUCT_END
+#undef STRUCT_BEGIN
+#undef LIST
+#undef MAP
+#undef STRING4
+#undef STRING1
+#undef DOUBLE
+#undef FLOAT
+#undef LONG
+#undef INT
+#undef SHORT
+#undef BYTE
+#undef FIELD
+
+#undef TEMPLATE_ARGUMENT
+#undef IS_OPTIONAL
+#undef NO_OP
+#undef ATTRIBUTE_SET
+
+#undef TLV_CODE

+ 124 - 3
QQBot/Utility.h

@@ -5,7 +5,7 @@
 
 namespace QQBot::Utility
 {
-	constexpr std::size_t AlignTo(std::size_t num, std::size_t alignment)
+	constexpr std::size_t AlignTo(std::size_t num, std::size_t alignment) noexcept
 	{
 		return num + alignment - 1 & ~(alignment - 1);
 	}
@@ -23,14 +23,135 @@ namespace QQBot::Utility
 	};
 
 	template <typename T, std::size_t N>
-	typename MayAddConst<T, std::byte>::Type(&ToByteArray(T(&arr)[N]) noexcept)[sizeof(T) * N / sizeof(std::byte)]
+	constexpr typename MayAddConst<T, std::byte>::Type(&ToByteArray(T(&arr)[N]) noexcept)[sizeof(T) * N / sizeof(std::byte)]
 	{
 		return reinterpret_cast<typename MayAddConst<T, std::byte>::Type(&)[sizeof(T) * N / sizeof(std::byte)]>(arr);
 	}
 
 	template <typename T, std::size_t N>
-	gsl::span<typename MayAddConst<T, std::byte>::Type, sizeof(T) * N / sizeof(std::byte)> ToByteSpan(T(&arr)[N]) noexcept
+	constexpr gsl::span<typename MayAddConst<T, std::byte>::Type, sizeof(T) * N / sizeof(std::byte)> ToByteSpan(T(&arr)[N]) noexcept
 	{
 		return ToByteArray(arr);
 	}
+
+	template <typename T>
+	struct ResultType
+	{
+		using Type = T;
+	};
+
+	template <typename TypeSequence, template <typename> class Predicate, typename FallbackType>
+	struct GetFirstOr
+		: ResultType<FallbackType>
+	{
+	};
+
+	template <template <typename...> class TypeSequenceTemplate, typename FirstArg, typename... RestArgs, template <typename> class Predicate, typename FallbackType>
+	struct GetFirstOr<TypeSequenceTemplate<FirstArg, RestArgs...>, Predicate, FallbackType>
+		: std::conditional_t<Predicate<FirstArg>::value, ResultType<FirstArg>, GetFirstOr<TypeSequenceTemplate<RestArgs...>, Predicate, FallbackType>>
+	{
+	};
+
+	template <typename T>
+	constexpr bool IsTemplate() noexcept
+	{
+		return false;
+	}
+
+	template <template <typename...> class Template>
+	constexpr bool IsTemplate() noexcept
+	{
+		return true;
+	}
+
+	template <typename From, template <typename...> class ToTemplate>
+	struct ApplyToTrait
+	{
+		using Type = ToTemplate<From>;
+	};
+
+	template <template <typename...> class FromTemplate, typename... Args, template <typename...> class ToTemplate>
+	struct ApplyToTrait<FromTemplate<Args...>, ToTemplate>
+	{
+		using Type = ToTemplate<Args...>;
+	};
+
+	template <typename From, template <typename...> class ToTemplate>
+	using ApplyTo = typename ApplyToTrait<From, ToTemplate>::Type;
+
+	template <typename T, template <typename...> class Template>
+	struct IsTemplateOf
+		: std::false_type
+	{
+	};
+
+	template <template <typename...> class Template, typename... Args>
+	struct IsTemplateOf<Template<Args...>, Template>
+		: std::true_type
+	{
+	};
+
+	template <typename T>
+	constexpr auto RecursiveApply()
+	{
+		return ResultType<T>{};
+	}
+
+	template <typename T, typename FirstOperation, typename... RestOperations>
+	constexpr auto RecursiveApply()
+	{
+		return RecursiveApply<typename FirstOperation::template Apply<T>::Type, RestOperations...>();
+	}
+
+	template <template <typename...> class TemplateArg>
+	struct TemplatePlaceholder
+	{
+		template <typename... Args>
+		using Template = TemplateArg<Args...>;
+	};
+
+	template <template <typename...> class Template>
+	constexpr auto RecursiveApply()
+	{
+		return TemplatePlaceholder<Template>{};
+	}
+
+	template <template <typename...> class Template, typename FirstOperation, typename... RestOperations>
+	constexpr auto RecursiveApply()
+	{
+		return RecursiveApply<typename FirstOperation::template Apply<Template>::Type, RestOperations...>();
+	}
+
+	template <template <typename> class Predicate, typename FallbackType>
+	constexpr decltype(auto) ReturnFirst()
+	{
+		return FallbackType{};
+	}
+
+	template <template <typename> class Predicate, typename FallbackType, typename FirstArg, typename... Args>
+	constexpr decltype(auto) ReturnFirst(FirstArg&& firstArg, Args&&... args)
+	{
+		if constexpr (Predicate<FirstArg&&>::value)
+		{
+			return std::forward<FirstArg>(firstArg);
+		}
+		else
+		{
+			return ReturnFirst<Predicate, FallbackType>(std::forward<Args>(args)...);
+		}
+	}
+
+	template <template <typename...> class Template, typename... Args>
+	struct BindTrait
+	{
+		template <typename... RestArgs>
+		using Result = Template<Args..., RestArgs...>;
+	};
+
+	template <template <typename...> class Template1, template <typename...> class Template2>
+	struct ConcatTrait
+	{
+		template <typename... Args>
+		using Result = Template2<Template1<Args...>>;
+	};
 }