diff --git a/.gitmodules b/.gitmodules index ab4f4eaa..06ef07b9 100644 --- a/.gitmodules +++ b/.gitmodules @@ -11,3 +11,14 @@ path = deps/zlib url = https://github.com/madler/zlib.git branch = develop +[submodule "deps/libtomcrypt"] + path = deps/libtomcrypt + url = https://github.com/libtom/libtomcrypt.git + branch = develop +[submodule "deps/libtommath"] + path = deps/libtommath + url = https://github.com/libtom/libtommath.git + branch = develop +[submodule "deps/rapidjson"] + path = deps/rapidjson + url = https://github.com/Tencent/rapidjson.git diff --git a/deps/libtomcrypt b/deps/libtomcrypt new file mode 160000 index 00000000..e6be20bf --- /dev/null +++ b/deps/libtomcrypt @@ -0,0 +1 @@ +Subproject commit e6be20bfc7e92c91dc1ef9cb5cc11a326e41f706 diff --git a/deps/libtommath b/deps/libtommath new file mode 160000 index 00000000..4b473685 --- /dev/null +++ b/deps/libtommath @@ -0,0 +1 @@ +Subproject commit 4b47368501321c795d5b54d87a5bab35a21a7940 diff --git a/deps/premake/libtomcrypt.lua b/deps/premake/libtomcrypt.lua new file mode 100644 index 00000000..6c6f28d1 --- /dev/null +++ b/deps/premake/libtomcrypt.lua @@ -0,0 +1,61 @@ +libtomcrypt = { + source = path.join(dependencies.basePath, "libtomcrypt"), +} + +function libtomcrypt.import() + links { + "libtomcrypt" + } + + libtomcrypt.includes() +end + +function libtomcrypt.includes() + includedirs { + path.join(libtomcrypt.source, "src/headers") + } + + defines { + "LTC_NO_FAST", + "LTC_NO_PROTOTYPES", + "LTC_NO_RSA_BLINDING", + } +end + +function libtomcrypt.project() + project "libtomcrypt" + language "C" + + libtomcrypt.includes() + libtommath.import() + + files { + path.join(libtomcrypt.source, "src/**.c"), + } + + removefiles { + path.join(libtomcrypt.source, "src/**/*tab.c"), + path.join(libtomcrypt.source, "src/encauth/ocb3/**.c"), + } + + defines { + "_CRT_SECURE_NO_WARNINGS", + "LTC_SOURCE", + "_LIB", + "USE_LTM" + } + + removedefines { + "_DLL", + "_USRDLL" + } + + linkoptions { + "-IGNORE:4221" + } + + warnings "Off" + kind "StaticLib" +end + +table.insert(dependencies, libtomcrypt) diff --git a/deps/premake/libtommath.lua b/deps/premake/libtommath.lua new file mode 100644 index 00000000..ab4cdde3 --- /dev/null +++ b/deps/premake/libtommath.lua @@ -0,0 +1,52 @@ +libtommath = { + source = path.join(dependencies.basePath, "libtommath"), +} + +function libtommath.import() + links { + "libtommath" + } + + libtommath.includes() +end + +function libtommath.includes() + includedirs { + libtommath.source + } + + defines { + "LTM_DESC", + "__STDC_IEC_559__", + "MP_NO_DEV_URANDOM", + } +end + +function libtommath.project() + project "libtommath" + language "C" + + libtommath.includes() + + files { + path.join(libtommath.source, "*.c"), + } + + defines { + "_LIB" + } + + removedefines { + "_DLL", + "_USRDLL" + } + + linkoptions { + "-IGNORE:4221" + } + + warnings "Off" + kind "StaticLib" +end + +table.insert(dependencies, libtommath) diff --git a/deps/premake/rapidjson.lua b/deps/premake/rapidjson.lua new file mode 100644 index 00000000..d1085120 --- /dev/null +++ b/deps/premake/rapidjson.lua @@ -0,0 +1,19 @@ +rapidjson = { + source = path.join(dependencies.basePath, "rapidjson"), +} + +function rapidjson.import() + rapidjson.includes() +end + +function rapidjson.includes() + includedirs { + path.join(rapidjson.source, "include"), + } +end + +function rapidjson.project() + +end + +table.insert(dependencies, rapidjson) diff --git a/deps/rapidjson b/deps/rapidjson new file mode 160000 index 00000000..06d58b9e --- /dev/null +++ b/deps/rapidjson @@ -0,0 +1 @@ +Subproject commit 06d58b9e848c650114556a23294d0b6440078c61 diff --git a/src/client/component/demonware.cpp b/src/client/component/demonware.cpp new file mode 100644 index 00000000..fb22dded --- /dev/null +++ b/src/client/component/demonware.cpp @@ -0,0 +1,599 @@ +#include +#include "loader/component_loader.hpp" + +#include +#include + +#include "game/game.hpp" +#include "game/demonware/servers/lobby_server.hpp" +#include "game/demonware/servers/auth3_server.hpp" +#include "game/demonware/servers/stun_server.hpp" +#include "game/demonware/servers/umbrella_server.hpp" +#include "game/demonware/server_registry.hpp" + +#define TCP_BLOCKING true +#define UDP_BLOCKING false + +namespace demonware +{ + namespace + { + volatile bool exit_server; + std::thread server_thread; + utils::concurrency::container> blocking_sockets; + utils::concurrency::container> socket_map; + server_registry tcp_servers; + server_registry udp_servers; + + tcp_server* find_server(const SOCKET socket) + { + return socket_map.access([&](const std::unordered_map& map) -> tcp_server* + { + const auto entry = map.find(socket); + if (entry == map.end()) + { + return nullptr; + } + + return entry->second; + }); + } + + bool socket_link(const SOCKET socket, const uint32_t address) + { + auto* server = tcp_servers.find(address); + if (!server) + { + return false; + } + + socket_map.access([&](std::unordered_map& map) + { + map[socket] = server; + }); + + return true; + } + + void socket_unlink(const SOCKET socket) + { + socket_map.access([&](std::unordered_map& map) + { + const auto entry = map.find(socket); + if (entry != map.end()) + { + map.erase(entry); + } + }); + } + + bool is_socket_blocking(const SOCKET socket, const bool def) + { + return blocking_sockets.access([&](std::unordered_map& map) + { + const auto entry = map.find(socket); + if (entry == map.end()) + { + return def; + } + + return entry->second; + }); + } + + void remove_blocking_socket(const SOCKET socket) + { + blocking_sockets.access([&](std::unordered_map& map) + { + const auto entry = map.find(socket); + if (entry != map.end()) + { + map.erase(entry); + } + }); + } + + void add_blocking_socket(const SOCKET socket, const bool block) + { + blocking_sockets.access([&](std::unordered_map& map) + { + map[socket] = block; + }); + } + + void server_main() + { + exit_server = false; + + while (!exit_server) + { + tcp_servers.frame(); + udp_servers.frame(); + std::this_thread::sleep_for(50ms); + } + } + + namespace io + { + int getaddrinfo_stub(const char* name, const char* service, + const addrinfo* hints, addrinfo** res) + { +#ifdef DW_DEBUG + printf("[ network ]: [getaddrinfo]: \"%s\" \"%s\"\n", name, service); +#endif + + base_server* server = tcp_servers.find(name); + if (!server) + { + server = udp_servers.find(name); + } + + if (!server) + { + return getaddrinfo(name, service, hints, res); + } + + const auto address = utils::memory::get_allocator()->allocate(); + const auto ai = utils::memory::get_allocator()->allocate(); + + auto in_addr = reinterpret_cast(address); + in_addr->sin_addr.s_addr = server->get_address(); + in_addr->sin_family = AF_INET; + + ai->ai_family = AF_INET; + ai->ai_socktype = SOCK_STREAM; + ai->ai_addr = address; + ai->ai_addrlen = sizeof(sockaddr); + ai->ai_next = nullptr; + ai->ai_flags = 0; + ai->ai_protocol = 0; + ai->ai_canonname = const_cast(name); + + *res = ai; + + return 0; + } + + void freeaddrinfo_stub(addrinfo* ai) + { + if (!utils::memory::get_allocator()->find(ai)) + { + return freeaddrinfo(ai); + } + + utils::memory::get_allocator()->free(ai->ai_addr); + utils::memory::get_allocator()->free(ai); + } + + int getpeername_stub(const SOCKET s, sockaddr* addr, socklen_t* addrlen) + { + auto* server = find_server(s); + + if (server) + { + auto in_addr = reinterpret_cast(addr); + in_addr->sin_addr.s_addr = server->get_address(); + in_addr->sin_family = AF_INET; + *addrlen = sizeof(sockaddr); + + return 0; + } + + return getpeername(s, addr, addrlen); + } + + int getsockname_stub(const SOCKET s, sockaddr* addr, socklen_t* addrlen) + { + auto* server = find_server(s); + + if (server) + { + auto in_addr = reinterpret_cast(addr); + in_addr->sin_addr.s_addr = server->get_address(); + in_addr->sin_family = AF_INET; + *addrlen = sizeof(sockaddr); + + return 0; + } + + return getsockname(s, addr, addrlen); + } + + hostent* gethostbyname_stub(const char* name) + { +#ifdef DW_DEBUG + printf("[ network ]: [gethostbyname]: \"%s\"\n", name); +#endif + + base_server* server = tcp_servers.find(name); + if (!server) + { + server = udp_servers.find(name); + } + + if (!server) + { +#pragma warning(push) +#pragma warning(disable: 4996) + return gethostbyname(name); +#pragma warning(pop) + } + + static thread_local in_addr address{}; + address.s_addr = server->get_address(); + + static thread_local in_addr* addr_list[2]{}; + addr_list[0] = &address; + addr_list[1] = nullptr; + + static thread_local hostent host{}; + host.h_name = const_cast(name); + host.h_aliases = nullptr; + host.h_addrtype = AF_INET; + host.h_length = sizeof(in_addr); + host.h_addr_list = reinterpret_cast(addr_list); + + return &host; + } + + int connect_stub(const SOCKET s, const struct sockaddr* addr, const int len) + { + if (len == sizeof(sockaddr_in)) + { + const auto* in_addr = reinterpret_cast(addr); + if (socket_link(s, in_addr->sin_addr.s_addr)) return 0; + } + + return connect(s, addr, len); + } + + int closesocket_stub(const SOCKET s) + { + remove_blocking_socket(s); + socket_unlink(s); + + return closesocket(s); + } + + int send_stub(const SOCKET s, const char* buf, const int len, const int flags) + { + auto* server = find_server(s); + + if (server) + { + server->handle_input(buf, len); + return len; + } + + return send(s, buf, len, flags); + } + + int recv_stub(const SOCKET s, char* buf, const int len, const int flags) + { + auto* server = find_server(s); + + if (server) + { + if (server->pending_data()) + { + return static_cast(server->handle_output(buf, len)); + } + else + { + WSASetLastError(WSAEWOULDBLOCK); + return -1; + } + } + + return recv(s, buf, len, flags); + } + + int sendto_stub(const SOCKET s, const char* buf, const int len, const int flags, const sockaddr* to, + const int tolen) + { + const auto* in_addr = reinterpret_cast(to); + auto* server = udp_servers.find(in_addr->sin_addr.s_addr); + + if (server) + { + server->handle_input(buf, len, { s, to, tolen }); + return len; + } + + return sendto(s, buf, len, flags, to, tolen); + } + + int recvfrom_stub(const SOCKET s, char* buf, const int len, const int flags, struct sockaddr* from, + int* fromlen) + { + // Not supported yet + if (is_socket_blocking(s, UDP_BLOCKING)) + { + return recvfrom(s, buf, len, flags, from, fromlen); + } + + size_t result = 0; + udp_servers.for_each([&](udp_server& server) + { + if (server.pending_data(s)) + { + result = server.handle_output( + s, buf, static_cast(len), from, fromlen); + } + }); + + if (result) + { + return static_cast(result); + } + + return recvfrom(s, buf, len, flags, from, fromlen); + } + + int select_stub(const int nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds, + struct timeval* timeout) + { + if (exit_server) + { + return select(nfds, readfds, writefds, exceptfds, timeout); + } + + auto result = 0; + std::vector read_sockets; + std::vector write_sockets; + + socket_map.access([&](std::unordered_map& sockets) + { + for (auto& s : sockets) + { + if (readfds) + { + if (FD_ISSET(s.first, readfds)) + { + if (s.second->pending_data()) + { + read_sockets.push_back(s.first); + FD_CLR(s.first, readfds); + } + } + } + + if (writefds) + { + if (FD_ISSET(s.first, writefds)) + { + write_sockets.push_back(s.first); + FD_CLR(s.first, writefds); + } + } + + if (exceptfds) + { + if (FD_ISSET(s.first, exceptfds)) + { + FD_CLR(s.first, exceptfds); + } + } + } + }); + + if ((!readfds || readfds->fd_count == 0) && (!writefds || writefds->fd_count == 0)) + { + timeout->tv_sec = 0; + timeout->tv_usec = 0; + } + + result = select(nfds, readfds, writefds, exceptfds, timeout); + if (result < 0) result = 0; + + for (const auto& socket : read_sockets) + { + if (readfds) + { + FD_SET(socket, readfds); + result++; + } + } + + for (const auto& socket : write_sockets) + { + if (writefds) + { + FD_SET(socket, writefds); + result++; + } + } + + return result; + } + + int ioctlsocket_stub(const SOCKET s, const long cmd, u_long* argp) + { + if (static_cast(cmd) == (FIONBIO)) + { + add_blocking_socket(s, *argp == 0); + } + + return ioctlsocket(s, cmd, argp); + } + + BOOL internet_get_connected_state_stub(LPDWORD, DWORD) + { + // Allow offline play + return TRUE; + } + } + + void bd_logger_stub() + { + //printf("logged\n"); + } + +#ifdef DW_DEBUG + void a(unsigned int n) + { + printf("bdAuth: Auth task failed with HTTP code [%u]\n", n); + } + + void b(unsigned int n) + { + printf("bdAuth: Decoded client ticket of unexpected size [%u]\n", n); + } + + void c(unsigned int n) + { + printf("bdAuth: Decoded server ticket of unexpected size [%u]\n", n); + } + + void d() + { + printf("bdAuth: Auth ticket magic number mismatch\n"); + } + + void e() + { + printf("bdAuth: Cross Authentication completed\n"); + } + + void f() + { + printf("bdAuth: Auth task reply contains invalid data / format\n"); + } + + void g(unsigned int n) + { + printf("bdAuth: Auth task returned with error code [%u]\n", n); + } + + void h(unsigned int n) + { + printf("bdAuth: Invalid or No Task ID [%u] in Auth reply\n", n); + } + + void i() + { + printf("bdAuth: Received reply from DemonWare Auth server\n"); + } + + void l() + { + printf("bdAuth: Unknown error\n"); + } +#endif + + utils::hook::detour handle_auth_reply_hook; + bool handle_auth_reply_stub(void* a1, void* a2, void* a3) + { + // Skip bdAuth::validateResponseSignature + //utils::hook::set(0x7D4AB0_b, 0xC301B0); + // Skip bdAuth::processPlatformData + //utils::hook::set(0x7D55C0_b, 0xC301B0); + + return handle_auth_reply_hook.invoke(a1, a2, a3); + } + + void request_start_match_stub() + { + //const auto* args = "StartServer"; + //game::UI_RunMenuScript(0, &args); + } + } + + class component final : public component_interface + { + public: + component() + { + udp_servers.create("phoenix.stun.us.demonware.net"); + udp_servers.create("phoenix.stun.eu.demonware.net"); + udp_servers.create("phoenix.stun.jp.demonware.net"); + udp_servers.create("phoenix.stun.au.demonware.net"); + + udp_servers.create("stun.us.demonware.net"); + udp_servers.create("stun.eu.demonware.net"); + udp_servers.create("stun.jp.demonware.net"); + udp_servers.create("stun.au.demonware.net"); + + tcp_servers.create("mwr-pc-steam-auth3.prod.demonware.net"); + tcp_servers.create("mwr-pc-steam-lobby.prod.demonware.net"); + tcp_servers.create("prod.umbrella.demonware.net"); + } + + /*void* load_import(const std::string& library, const std::string& function) override + { + if (library == "WS2_32.dll") + { + if (function == "#3") return io::closesocket_stub; + if (function == "#4") return io::connect_stub; + if (function == "#5") return io::getpeername_stub; + if (function == "#6") return io::getsockname_stub; + if (function == "#10") return io::ioctlsocket_stub; + if (function == "#16") return io::recv_stub; + if (function == "#17") return io::recvfrom_stub; + if (function == "#18") return io::select_stub; + if (function == "#19") return io::send_stub; + if (function == "#20") return io::sendto_stub; + if (function == "#52") return io::gethostbyname_stub; + if (function == "getaddrinfo") return io::getaddrinfo_stub; + if (function == "freeaddrinfo") return io::freeaddrinfo_stub; + } + + if (function == "InternetGetConnectedState") + { + return io::internet_get_connected_state_stub; + } + + return nullptr; + }*/ + + void post_unpack() override + { + server_thread = utils::thread::create_named_thread("Demonware", server_main); + + /*utils::hook::set(0x7C0AD9_b, 0x0); // CURLOPT_SSL_VERIFYPEER + utils::hook::set(0x7C0AC5_b, 0xAF); // CURLOPT_SSL_VERIFYHOST + utils::hook::set(0xA1327C_b, 0x0); // HTTPS -> HTTP + + std::memcpy(reinterpret_cast(0x8D0298_b), + "http://prod.umbrella.demonware.net/v1.0/", sizeof("http://prod.umbrella.demonware.net/v1.0/")); + std::memcpy(reinterpret_cast(0x8D05A8_b), + "http://prod.uno.demonware.net/v1.0/", sizeof("http://prod.uno.demonware.net/v1.0/")); + std::memcpy(reinterpret_cast(0x9EDB08_b), "http://%s:%d/auth/", sizeof("http://%s:%d/auth/")); + + // utils::hook::set(0x19F8C0_b, 0xC3); // SV_SendMatchData, not sure + utils::hook::nop(0x19BB67_b, 5); // LiveStorage_SendMatchDataComplete (crashes at the end of match) + utils::hook::nop(0x19BC3F_b, 5); // LiveStorage_GettingStoreConfigComplete probably (crashes randomly) + utils::hook::nop(0x19BC48_b, 5); // similar to above (crashes in killcam) + utils::hook::set(0x1A3340_b, 0xC3); // Live_CheckForFullDisconnect + + // Remove some while loop that freezes the rendering for a few secs while connecting + utils::hook::nop(0x625555_b, 5); + + handle_auth_reply_hook.create(0x7AC600_b, handle_auth_reply_stub); + + // Skip update check in Live_SyncOnlineDataFlags + utils::hook::set(0x47A6D0_b, 0xC301B0); + // Remove update failed popup + utils::hook::set(0x47B2B0_b, 0xC301B0); + + // xpartygo -> just start the match + utils::hook::jump(0x355B80_b, request_start_match_stub); + + utils::hook::set(0x396AD0_b, 0xC301B0); // DB_IsZoneLoaded("ffotd") + utils::hook::set(0x4DD600_b, 0xC300B0); // dont use ffotd + utils::hook::set(0x4DD5B0_b, 0xC300B0); // dont dl ffotd + */ + } + + void pre_destroy() override + { + exit_server = true; + if (server_thread.joinable()) + { + server_thread.join(); + } + } + }; +} + +REGISTER_COMPONENT(demonware::component) diff --git a/src/client/game/demonware/bit_buffer.cpp b/src/client/game/demonware/bit_buffer.cpp new file mode 100644 index 00000000..2b65be2d --- /dev/null +++ b/src/client/game/demonware/bit_buffer.cpp @@ -0,0 +1,182 @@ +#include +#include "bit_buffer.hpp" + +namespace demonware +{ + bool bit_buffer::read_bytes(const unsigned int bytes, unsigned char* output) + { + return this->read(bytes * 8, output); + } + + bool bit_buffer::read_bool(bool* output) + { + if (!this->read_data_type(1)) + { + return false; + } + + return this->read(1, output); + } + + bool bit_buffer::read_uint32(unsigned int* output) + { + if (!this->read_data_type(8)) + { + return false; + } + + return this->read(32, output); + } + + bool bit_buffer::read_data_type(const char expected) + { + char data_type = 0; + + if (!this->use_data_types_) return true; + if (this->read(5, &data_type)) + { + return (data_type == expected); + } + + return false; + } + + bool bit_buffer::write_bytes(const unsigned int bytes, const char* data) + { + return this->write_bytes(bytes, reinterpret_cast(data)); + } + + bool bit_buffer::write_bytes(const unsigned int bytes, const unsigned char* data) + { + return this->write(bytes * 8, data); + } + + bool bit_buffer::write_bool(bool data) + { + if (this->write_data_type(1)) + { + return this->write(1, &data); + } + + return false; + } + + bool bit_buffer::write_int32(int data) + { + if (this->write_data_type(7)) + { + return this->write(32, &data); + } + + return false; + } + + bool bit_buffer::write_uint32(unsigned int data) + { + if (this->write_data_type(8)) + { + return this->write(32, &data); + } + + return false; + } + + bool bit_buffer::write_data_type(char data) + { + if (!this->use_data_types_) + { + return true; + } + + return this->write(5, &data); + } + + bool bit_buffer::read(unsigned int bits, void* output) + { + if (bits == 0) return false; + if ((this->current_bit_ + bits) > (this->buffer_.size() * 8)) return false; + + int cur_byte = this->current_bit_ >> 3; + auto cur_out = 0; + + const char* bytes = this->buffer_.data(); + const auto output_bytes = static_cast(output); + + while (bits > 0) + { + const int min_bit = (bits < 8) ? bits : 8; + const auto this_byte = bytes[cur_byte++] & 0xFF; + const int remain = this->current_bit_ & 7; + + if ((min_bit + remain) <= 8) + { + output_bytes[cur_out] = BYTE((0xFF >> (8 - min_bit)) & (this_byte >> remain)); + } + else + { + output_bytes[cur_out] = BYTE( + (0xFF >> (8 - min_bit)) & (bytes[cur_byte] << (8 - remain)) | (this_byte >> remain)); + } + + cur_out++; + this->current_bit_ += min_bit; + bits -= min_bit; + } + + return true; + } + + bool bit_buffer::write(const unsigned int bits, const void* data) + { + if (bits == 0) return false; + this->buffer_.resize(this->buffer_.size() + (bits >> 3) + 1); + + int bit = bits; + const auto bytes = const_cast(this->buffer_.data()); + const auto* input_bytes = static_cast(data); + + while (bit > 0) + { + const int bit_pos = this->current_bit_ & 7; + auto rem_bit = 8 - bit_pos; + const auto this_write = (bit < rem_bit) ? bit : rem_bit; + + const BYTE mask = ((0xFF >> rem_bit) | (0xFF << (bit_pos + this_write))); + const int byte_pos = this->current_bit_ >> 3; + + const BYTE temp_byte = (mask & bytes[byte_pos]); + const BYTE this_bit = ((bits - bit) & 7); + const auto this_byte = (bits - bit) >> 3; + + auto this_data = input_bytes[this_byte]; + + const auto next_byte = (((bits - 1) >> 3) > this_byte) ? input_bytes[this_byte + 1] : 0; + + this_data = BYTE((next_byte << (8 - this_bit)) | (this_data >> this_bit)); + + const BYTE out_byte = (~mask & (this_data << bit_pos) | temp_byte); + bytes[byte_pos] = out_byte; + + this->current_bit_ += this_write; + bit -= this_write; + } + + return true; + } + + void bit_buffer::set_use_data_types(const bool use_data_types) + { + this->use_data_types_ = use_data_types; + } + + unsigned int bit_buffer::size() const + { + return this->current_bit_ / 8 + (this->current_bit_ % 8 ? 1 : 0); + } + + std::string& bit_buffer::get_buffer() + { + this->buffer_.resize(this->size()); + return this->buffer_; + } +} diff --git a/src/client/game/demonware/bit_buffer.hpp b/src/client/game/demonware/bit_buffer.hpp new file mode 100644 index 00000000..f2fd5c09 --- /dev/null +++ b/src/client/game/demonware/bit_buffer.hpp @@ -0,0 +1,40 @@ +#pragma once + +namespace demonware +{ + class bit_buffer final + { + public: + bit_buffer() = default; + + explicit bit_buffer(std::string buffer) : buffer_(std::move(buffer)) + { + } + + bool read_bytes(unsigned int bytes, unsigned char* output); + bool read_bool(bool* output); + bool read_uint32(unsigned int* output); + bool read_data_type(char expected); + + bool write_bytes(unsigned int bytes, const char* data); + bool write_bytes(unsigned int bytes, const unsigned char* data); + bool write_bool(bool data); + bool write_int32(int data); + bool write_uint32(unsigned int data); + bool write_data_type(char data); + + bool read(unsigned int bits, void* output); + bool write(unsigned int bits, const void* data); + + void set_use_data_types(bool use_data_types); + + unsigned int size() const; + + std::string& get_buffer(); + + private: + std::string buffer_{}; + unsigned int current_bit_ = 0; + bool use_data_types_ = true; + }; +} diff --git a/src/client/game/demonware/byte_buffer.cpp b/src/client/game/demonware/byte_buffer.cpp new file mode 100644 index 00000000..af5bee9a --- /dev/null +++ b/src/client/game/demonware/byte_buffer.cpp @@ -0,0 +1,320 @@ +#include +#include "byte_buffer.hpp" + +namespace demonware +{ + bool byte_buffer::read_bool(bool* output) + { + if (!this->read_data_type(1)) return false; + return this->read(1, output); + } + + bool byte_buffer::read_byte(char* output) + { + if (!this->read_data_type(2)) return false; + return this->read(1, output); + } + + bool byte_buffer::read_ubyte(unsigned char* output) + { + if (!this->read_data_type(3)) return false; + return this->read(1, output); + } + + bool byte_buffer::read_int16(short* output) + { + if (!this->read_data_type(5)) return false; + return this->read(2, output); + } + + bool byte_buffer::read_uint16(unsigned short* output) + { + if (!this->read_data_type(6)) return false; + return this->read(2, output); + } + + bool byte_buffer::read_int32(int* output) + { + if (!this->read_data_type(7)) return false; + return this->read(4, output); + } + + bool byte_buffer::read_uint32(unsigned int* output) + { + if (!this->read_data_type(8)) return false; + return this->read(4, output); + } + + bool byte_buffer::read_int64(__int64* output) + { + if (!this->read_data_type(9)) return false; + return this->read(8, output); + } + + bool byte_buffer::read_uint64(unsigned __int64* output) + { + if (!this->read_data_type(10)) return false; + return this->read(8, output); + } + + bool byte_buffer::read_float(float* output) + { + if (!this->read_data_type(13)) return false; + return this->read(4, output); + } + + bool byte_buffer::read_string(std::string* output) + { + char* out_data; + if (this->read_string(&out_data)) + { + output->clear(); + output->append(out_data); + return true; + } + + return false; + } + + bool byte_buffer::read_string(char** output) + { + if (!this->read_data_type(16)) return false; + + *output = const_cast(this->buffer_.data()) + this->current_byte_; + this->current_byte_ += strlen(*output) + 1; + + return true; + } + + bool byte_buffer::read_string(char* output, const int length) + { + if (!this->read_data_type(16)) return false; + + strcpy_s(output, length, const_cast(this->buffer_.data()) + this->current_byte_); + this->current_byte_ += strlen(output) + 1; + + return true; + } + + bool byte_buffer::read_blob(std::string* output) + { + char* out_data; + int length; + if (this->read_blob(&out_data, &length)) + { + output->clear(); + output->append(out_data, length); + return true; + } + + return false; + } + + bool byte_buffer::read_blob(char** output, int* length) + { + if (!this->read_data_type(0x13)) + { + return false; + } + + unsigned int size; + this->read_uint32(&size); + + *output = const_cast(this->buffer_.data()) + this->current_byte_; + *length = static_cast(size); + + this->current_byte_ += size; + + return true; + } + + bool byte_buffer::read_data_type(const char expected) + { + if (!this->use_data_types_) return true; + + char type; + this->read(1, &type); + return type == expected; + } + + bool byte_buffer::read_array_header(const unsigned char expected, unsigned int* element_count, + unsigned int* element_size) + { + if (element_count) *element_count = 0; + if (element_size) *element_size = 0; + + if (!this->read_data_type(expected + 100)) return false; + + uint32_t array_size, el_count; + if (!this->read_uint32(&array_size)) return false; + + this->set_use_data_types(false); + this->read_uint32(&el_count); + this->set_use_data_types(true); + + if (element_count) *element_count = el_count; + if (element_size) *element_size = array_size / el_count; + + return true; + } + + bool byte_buffer::write_bool(bool data) + { + this->write_data_type(1); + return this->write(1, &data); + } + + bool byte_buffer::write_byte(char data) + { + this->write_data_type(2); + return this->write(1, &data); + } + + bool byte_buffer::write_ubyte(unsigned char data) + { + this->write_data_type(3); + return this->write(1, &data); + } + + bool byte_buffer::write_int16(short data) + { + this->write_data_type(5); + return this->write(2, &data); + } + + bool byte_buffer::write_uint16(unsigned short data) + { + this->write_data_type(6); + return this->write(2, &data); + } + + bool byte_buffer::write_int32(int data) + { + this->write_data_type(7); + return this->write(4, &data); + } + + bool byte_buffer::write_uint32(unsigned int data) + { + this->write_data_type(8); + return this->write(4, &data); + } + + bool byte_buffer::write_int64(__int64 data) + { + this->write_data_type(9); + return this->write(8, &data); + } + + bool byte_buffer::write_uint64(unsigned __int64 data) + { + this->write_data_type(10); + return this->write(8, &data); + } + + bool byte_buffer::write_data_type(char data) + { + if (!this->use_data_types_) return true; + return this->write(1, &data); + } + + bool byte_buffer::write_float(float data) + { + this->write_data_type(13); + return this->write(4, &data); + } + + bool byte_buffer::write_string(const std::string& data) + { + return this->write_string(data.data()); + } + + bool byte_buffer::write_string(const char* data) + { + this->write_data_type(16); + return this->write(static_cast(strlen(data)) + 1, data); + } + + bool byte_buffer::write_blob(const std::string& data) + { + return this->write_blob(data.data(), INT(data.size())); + } + + bool byte_buffer::write_blob(const char* data, const int length) + { + this->write_data_type(0x13); + this->write_uint32(length); + + return this->write(length, data); + } + + bool byte_buffer::write_array_header(const unsigned char type, const unsigned int element_count, + const unsigned int element_size) + { + const auto using_types = this->is_using_data_types(); + this->set_use_data_types(false); + + auto result = this->write_ubyte(type + 100); + + this->set_use_data_types(true); + result &= this->write_uint32(element_count * element_size); + this->set_use_data_types(false); + + result &= this->write_uint32(element_count); + + this->set_use_data_types(using_types); + return result; + } + + bool byte_buffer::read(const int bytes, void* output) + { + if (bytes + this->current_byte_ > this->buffer_.size()) return false; + + std::memmove(output, this->buffer_.data() + this->current_byte_, bytes); + this->current_byte_ += bytes; + + return true; + } + + bool byte_buffer::write(const int bytes, const void* data) + { + this->buffer_.append(static_cast(data), bytes); + this->current_byte_ += bytes; + return true; + } + + bool byte_buffer::write(const std::string& data) + { + return this->write(static_cast(data.size()), data.data()); + } + + void byte_buffer::set_use_data_types(const bool use_data_types) + { + this->use_data_types_ = use_data_types; + } + + size_t byte_buffer::size() const + { + return this->buffer_.size(); + } + + bool byte_buffer::is_using_data_types() const + { + return use_data_types_; + } + + std::string& byte_buffer::get_buffer() + { + return this->buffer_; + } + + std::string byte_buffer::get_remaining() + { + return std::string(this->buffer_.begin() + this->current_byte_, this->buffer_.end()); + } + + bool byte_buffer::has_more_data() const + { + return this->buffer_.size() > this->current_byte_; + } +} diff --git a/src/client/game/demonware/byte_buffer.hpp b/src/client/game/demonware/byte_buffer.hpp new file mode 100644 index 00000000..228c0aec --- /dev/null +++ b/src/client/game/demonware/byte_buffer.hpp @@ -0,0 +1,71 @@ +#pragma once + +namespace demonware +{ + class byte_buffer final + { + public: + byte_buffer() = default; + + explicit byte_buffer(std::string buffer) : buffer_(std::move(buffer)) + { + } + + bool read_bool(bool* output); + bool read_byte(char* output); + bool read_ubyte(unsigned char* output); + bool read_int16(short* output); + bool read_uint16(unsigned short* output); + bool read_int32(int* output); + bool read_uint32(unsigned int* output); + bool read_int64(__int64* output); + bool read_uint64(unsigned __int64* output); + bool read_float(float* output); + bool read_string(char** output); + bool read_string(char* output, int length); + bool read_string(std::string* output); + bool read_blob(char** output, int* length); + bool read_blob(std::string* output); + bool read_data_type(char expected); + + bool read_array_header(unsigned char expected, unsigned int* element_count, + unsigned int* element_size = nullptr); + + bool write_bool(bool data); + bool write_byte(char data); + bool write_ubyte(unsigned char data); + bool write_int16(short data); + bool write_uint16(unsigned short data); + bool write_int32(int data); + bool write_uint32(unsigned int data); + bool write_int64(__int64 data); + bool write_uint64(unsigned __int64 data); + bool write_data_type(char data); + bool write_float(float data); + bool write_string(const char* data); + bool write_string(const std::string& data); + bool write_blob(const char* data, int length); + bool write_blob(const std::string& data); + + bool write_array_header(unsigned char type, unsigned int element_count, unsigned int element_size); + + bool read(int bytes, void* output); + bool write(int bytes, const void* data); + bool write(const std::string& data); + + void set_use_data_types(bool use_data_types); + size_t size() const; + + bool is_using_data_types() const; + + std::string& get_buffer(); + std::string get_remaining(); + + bool has_more_data() const; + + private: + std::string buffer_; + size_t current_byte_ = 0; + bool use_data_types_ = true; + }; +} diff --git a/src/client/game/demonware/data_types.hpp b/src/client/game/demonware/data_types.hpp new file mode 100644 index 00000000..579e2248 --- /dev/null +++ b/src/client/game/demonware/data_types.hpp @@ -0,0 +1,210 @@ +#pragma once + +#include "byte_buffer.hpp" + +namespace demonware +{ + class bdTaskResult + { + public: + virtual ~bdTaskResult() = default; + + virtual void serialize(byte_buffer*) + { + } + + virtual void deserialize(byte_buffer*) + { + } + }; + + class bdFileData final : public bdTaskResult + { + public: + std::string file_data; + + explicit bdFileData(std::string buffer) : file_data(std::move(buffer)) + { + } + + void serialize(byte_buffer* buffer) override + { + buffer->write_blob(this->file_data); + } + + void deserialize(byte_buffer* buffer) override + { + buffer->read_blob(&this->file_data); + } + }; + + class bdFileInfo final : public bdTaskResult + { + public: + uint64_t file_id; + uint32_t create_time; + uint32_t modified_time; + bool priv; + uint64_t owner_id; + std::string filename; + uint32_t file_size; + + void serialize(byte_buffer* buffer) override + { + buffer->write_uint32(this->file_size); + buffer->write_uint64(this->file_id); + buffer->write_uint32(this->create_time); + buffer->write_uint32(this->modified_time); + buffer->write_bool(this->priv); + buffer->write_uint64(this->owner_id); + buffer->write_string(this->filename); + } + + void deserialize(byte_buffer* buffer) override + { + buffer->read_uint32(&this->file_size); + buffer->read_uint64(&this->file_id); + buffer->read_uint32(&this->create_time); + buffer->read_uint32(&this->modified_time); + buffer->read_bool(&this->priv); + buffer->read_uint64(&this->owner_id); + buffer->read_string(&this->filename); + } + }; + + class bdTimeStamp final : public bdTaskResult + { + public: + uint32_t unix_time; + + void serialize(byte_buffer* buffer) override + { + buffer->write_uint32(this->unix_time); + } + + void deserialize(byte_buffer* buffer) override + { + buffer->read_uint32(&this->unix_time); + } + }; + + class bdDMLInfo : public bdTaskResult + { + public: + std::string country_code; // Char [3] + std::string country; // Char [65] + std::string region; // Char [65] + std::string city; // Char [129] + float latitude; + float longitude; + + void serialize(byte_buffer* buffer) override + { + buffer->write_string(this->country_code); + buffer->write_string(this->country); + buffer->write_string(this->region); + buffer->write_string(this->city); + buffer->write_float(this->latitude); + buffer->write_float(this->longitude); + } + + void deserialize(byte_buffer* buffer) override + { + buffer->read_string(&this->country_code); + buffer->read_string(&this->country); + buffer->read_string(&this->region); + buffer->read_string(&this->city); + buffer->read_float(&this->latitude); + buffer->read_float(&this->longitude); + } + }; + + class bdDMLRawData final : public bdDMLInfo + { + public: + uint32_t asn; // Autonomous System Number. + std::string timezone; + + void serialize(byte_buffer* buffer) override + { + bdDMLInfo::serialize(buffer); + + buffer->write_uint32(this->asn); + buffer->write_string(this->timezone); + } + + void deserialize(byte_buffer* buffer) override + { + bdDMLInfo::deserialize(buffer); + + buffer->read_uint32(&this->asn); + buffer->read_string(&this->timezone); + } + }; + + // made up name + class bdFile final : public bdTaskResult + { + public: + uint64_t owner_id; + std::string platform; + std::string filename; + uint32_t unk; + std::string data; + + void serialize(byte_buffer* buffer) override + { + buffer->write_uint64(this->owner_id); + buffer->write_string(this->platform); + buffer->write_string(this->filename); + buffer->write_uint32(this->unk); + buffer->write_blob(this->data); + } + + void deserialize(byte_buffer* buffer) override + { + buffer->read_uint64(&this->owner_id); + buffer->read_string(&this->platform); + buffer->read_string(&this->filename); + buffer->read_uint32(&this->unk); + buffer->read_blob(&this->data); + } + }; + + class bdFile2 final : public bdTaskResult + { + public: + uint32_t unk1; + uint32_t unk2; + uint32_t unk3; + bool priv; + uint64_t owner_id; + std::string platform; + std::string filename; + std::string data; + + void serialize(byte_buffer* buffer) override + { + buffer->write_uint32(this->unk1); + buffer->write_uint32(this->unk2); + buffer->write_uint32(this->unk3); + buffer->write_bool(this->priv); + buffer->write_uint64(this->owner_id); + buffer->write_string(this->platform); + buffer->write_string(this->filename); + buffer->write_blob(this->data); + } + + void deserialize(byte_buffer* buffer) override + { + buffer->read_uint32(&this->unk1); + buffer->read_uint32(&this->unk2); + buffer->read_uint32(&this->unk3); + buffer->read_bool(&this->priv); + buffer->read_uint64(&this->owner_id); + buffer->read_string(&this->platform); + buffer->read_string(&this->filename); + buffer->read_blob(&this->data); + } + }; +} diff --git a/src/client/game/demonware/keys.cpp b/src/client/game/demonware/keys.cpp new file mode 100644 index 00000000..645ef2d3 --- /dev/null +++ b/src/client/game/demonware/keys.cpp @@ -0,0 +1,130 @@ +#include +#include "keys.hpp" + +#include +#include + +namespace demonware +{ + struct data_t + { + char m_session_key[24]; + char m_response[8]; + char m_hmac_key[20]; + char m_enc_key[16]; + char m_dec_key[16]; + } data{}; + + std::string packet_buffer; + + void calculate_hmacs_s1(const char* data_, const unsigned int data_size, const char* key, + const unsigned int key_size, + char* dst, const unsigned int dst_size) + { + char buffer[64]; + unsigned int pos = 0; + unsigned int out_offset = 0; + char count = 1; + std::string result; + + // buffer add key + std::memcpy(&buffer[pos], key, key_size); + pos += key_size; + + // buffer add count + buffer[pos] = count; + pos++; + + // calculate hmac + result = utils::cryptography::hmac_sha1::compute(std::string(buffer, pos), std::string(data_, data_size)); + + // save output + std::memcpy(dst, result.data(), std::min(20u, (dst_size - out_offset))); + out_offset = 20; + + // second loop + while (true) + { + // if we filled the output buffer, exit + if (out_offset >= dst_size) + break; + + // buffer add last result + pos = 0; + std::memcpy(&buffer[pos], result.data(), 20); + pos += 20; + + // buffer add key + std::memcpy(&buffer[pos], key, key_size); + pos += key_size; + + // buffer add count + count++; + buffer[pos] = count; + pos++; + + // calculate hmac + result = utils::cryptography::hmac_sha1::compute(std::string(buffer, pos), std::string(data_, data_size)); + + // save output + std::memcpy(dst + out_offset, result.data(), std::min(20u, (dst_size - out_offset))); + out_offset += 20; + } + } + + void derive_keys_s1() + { + const auto out_1 = utils::cryptography::sha1::compute(packet_buffer); // out_1 size 20 + + auto data_3 = utils::cryptography::hmac_sha1::compute(data.m_session_key, out_1); + + char out_2[16]; + calculate_hmacs_s1(data_3.data(), 20, "CLIENTCHAL", 10, out_2, 16); + + char out_3[72]; + calculate_hmacs_s1(data_3.data(), 20, "BDDATA", 6, out_3, 72); + + std::memcpy(data.m_response, &out_2[8], 8); + std::memcpy(data.m_hmac_key, &out_3[20], 20); + std::memcpy(data.m_dec_key, &out_3[40], 16); + std::memcpy(data.m_enc_key, &out_3[56], 16); + +#ifdef DW_DEBUG + printf("[DW] Response id: %s\n", utils::string::dump_hex(std::string(&out_2[8], 8)).data()); + printf("[DW] Hash verify: %s\n", utils::string::dump_hex(std::string(&out_3[20], 20)).data()); + printf("[DW] AES dec key: %s\n", utils::string::dump_hex(std::string(&out_3[40], 16)).data()); + printf("[DW] AES enc key: %s\n", utils::string::dump_hex(std::string(&out_3[56], 16)).data()); + printf("[DW] Bravo 6, going dark.\n"); +#endif + } + + void queue_packet_to_hash(const std::string& packet) + { + packet_buffer.append(packet); + } + + void set_session_key(const std::string& key) + { + std::memcpy(data.m_session_key, key.data(), 24); + } + + std::string get_decrypt_key() + { + return std::string(data.m_dec_key, 16); + } + + std::string get_encrypt_key() + { + return std::string(data.m_enc_key, 16); + } + + std::string get_hmac_key() + { + return std::string(data.m_hmac_key, 20); + } + + std::string get_response_id() + { + return std::string(data.m_response, 8); + } +} diff --git a/src/client/game/demonware/keys.hpp b/src/client/game/demonware/keys.hpp new file mode 100644 index 00000000..e2d29c69 --- /dev/null +++ b/src/client/game/demonware/keys.hpp @@ -0,0 +1,12 @@ +#pragma once + +namespace demonware +{ + void derive_keys_s1(); + void queue_packet_to_hash(const std::string& packet); + void set_session_key(const std::string& key); + std::string get_decrypt_key(); + std::string get_encrypt_key(); + std::string get_hmac_key(); + std::string get_response_id(); +} diff --git a/src/client/game/demonware/reply.cpp b/src/client/game/demonware/reply.cpp new file mode 100644 index 00000000..f0670956 --- /dev/null +++ b/src/client/game/demonware/reply.cpp @@ -0,0 +1,87 @@ +#include +#include "keys.hpp" +#include "reply.hpp" +#include "servers/service_server.hpp" + +#include + +namespace demonware +{ + std::string unencrypted_reply::data() + { + byte_buffer result; + result.set_use_data_types(false); + + result.write_int32(static_cast(this->buffer_.size()) + 2); + result.write_bool(false); + result.write_ubyte(this->type()); + result.write(this->buffer_); + + return result.get_buffer(); + } + + std::string encrypted_reply::data() + { + byte_buffer result; + result.set_use_data_types(false); + + byte_buffer enc_buffer; + enc_buffer.set_use_data_types(false); + + enc_buffer.write_uint32(static_cast(this->buffer_.size())); // service data size CHECKTHIS!! + enc_buffer.write_ubyte(this->type()); // TASK_REPLY type + enc_buffer.write(this->buffer_); // service data + + auto aligned_data = enc_buffer.get_buffer(); + auto size = aligned_data.size(); + size = ~15 & (size + 15); // 16 byte align + aligned_data.resize(size); + + // seed + std::string seed("\x5E\xED\x5E\xED\x5E\xED\x5E\xED\x5E\xED\x5E\xED\x5E\xED\x5E\xED", 16); + + // encrypt + const auto enc_data = utils::cryptography::aes::encrypt(aligned_data, seed, demonware::get_encrypt_key()); + + // header : encrypted service data : hash + static auto msg_count = 0; + msg_count++; + + byte_buffer response; + response.set_use_data_types(false); + + response.write_int32(30 + static_cast(enc_data.size())); + response.write_ubyte(static_cast(0xAB)); + response.write_ubyte(static_cast(0x85)); + response.write_int32(msg_count); + response.write(16, seed.data()); + response.write(enc_data); + + // hash entire packet and append end + auto hash_data = utils::cryptography::hmac_sha1::compute(response.get_buffer(), demonware::get_hmac_key()); + hash_data.resize(8); + response.write(8, hash_data.data()); + + return response.get_buffer(); + } + + void remote_reply::send(bit_buffer* buffer, const bool encrypted) + { + std::unique_ptr reply; + + if (encrypted) reply = std::make_unique(this->type_, buffer); + else reply = std::make_unique(this->type_, buffer); + + this->server_->send_reply(reply.get()); + } + + void remote_reply::send(byte_buffer* buffer, const bool encrypted) + { + std::unique_ptr reply; + + if (encrypted) reply = std::make_unique(this->type_, buffer); + else reply = std::make_unique(this->type_, buffer); + + this->server_->send_reply(reply.get()); + } +} diff --git a/src/client/game/demonware/reply.hpp b/src/client/game/demonware/reply.hpp new file mode 100644 index 00000000..ecf5eca4 --- /dev/null +++ b/src/client/game/demonware/reply.hpp @@ -0,0 +1,164 @@ +#pragma once + +#include "bit_buffer.hpp" +#include "byte_buffer.hpp" +#include "data_types.hpp" + +namespace demonware +{ + class reply + { + public: + reply() = default; + + reply(reply&&) = delete; + reply(const reply&) = delete; + reply& operator=(reply&&) = delete; + reply& operator=(const reply&) = delete; + + virtual ~reply() = default; + virtual std::string data() = 0; + }; + + class raw_reply : public reply + { + protected: + std::string buffer_; + + public: + raw_reply() = default; + + explicit raw_reply(std::string data) : buffer_(std::move(data)) + { + } + + std::string data() override + { + return this->buffer_; + } + }; + + class typed_reply : public raw_reply + { + public: + typed_reply(const uint8_t _type) : type_(_type) + { + } + + protected: + uint8_t type() const { return this->type_; } + + private: + uint8_t type_; + }; + + class encrypted_reply final : public typed_reply + { + public: + encrypted_reply(const uint8_t type, bit_buffer* bbuffer) : typed_reply(type) + { + this->buffer_.append(bbuffer->get_buffer()); + } + + encrypted_reply(const uint8_t type, byte_buffer* bbuffer) : typed_reply(type) + { + this->buffer_.append(bbuffer->get_buffer()); + } + + std::string data() override; + }; + + class unencrypted_reply final : public typed_reply + { + public: + unencrypted_reply(const uint8_t _type, bit_buffer* bbuffer) : typed_reply(_type) + { + this->buffer_.append(bbuffer->get_buffer()); + } + + unencrypted_reply(const uint8_t _type, byte_buffer* bbuffer) : typed_reply(_type) + { + this->buffer_.append(bbuffer->get_buffer()); + } + + std::string data() override; + }; + + class service_server; + + class remote_reply final + { + public: + remote_reply(service_server* server, uint8_t _type) : type_(_type), server_(server) + { + } + + void send(bit_buffer* buffer, bool encrypted); + void send(byte_buffer* buffer, bool encrypted); + + uint8_t type() const { return this->type_; } + + private: + uint8_t type_; + service_server* server_; + }; + + class service_reply final + { + public: + service_reply(service_server* _server, const uint8_t _type, const uint32_t _error) + : type_(_type), error_(_error), reply_(_server, 1) + { + } + + uint64_t send() + { + static uint64_t id = 0x0000000000000000; + const auto transaction_id = ++id; + + byte_buffer buffer; + buffer.write_uint64(transaction_id); + buffer.write_uint32(this->error_); + buffer.write_ubyte(this->type_); + + if (!this->error_) + { + buffer.write_uint32(uint32_t(this->objects_.size())); + if (!this->objects_.empty()) + { + buffer.write_uint32(uint32_t(this->objects_.size())); + + for (auto& object : this->objects_) + { + object->serialize(&buffer); + } + + this->objects_.clear(); + } + } + else + { + buffer.write_uint64(transaction_id); + } + + this->reply_.send(&buffer, true); + return transaction_id; + } + + void add(const std::shared_ptr& object) + { + this->objects_.push_back(object); + } + + void add(bdTaskResult* object) + { + this->add(std::shared_ptr(object)); + } + + private: + uint8_t type_; + uint32_t error_; + remote_reply reply_; + std::vector> objects_; + }; +} diff --git a/src/client/game/demonware/server_registry.hpp b/src/client/game/demonware/server_registry.hpp new file mode 100644 index 00000000..725e24c6 --- /dev/null +++ b/src/client/game/demonware/server_registry.hpp @@ -0,0 +1,60 @@ +#pragma once + +#include "servers/base_server.hpp" +#include + +namespace demonware +{ + template + class server_registry + { + static_assert(std::is_base_of::value, "Invalid server registry type"); + + public: + template + void create(Args&&... args) + { + static_assert(std::is_base_of::value, "Invalid server type"); + + auto server = std::make_unique(std::forward(args)...); + const auto address = server->get_address(); + servers_[address] = std::move(server); + } + + void for_each(const std::function& callback) const + { + for (auto& server : servers_) + { + callback(*server.second); + } + } + + T* find(const std::string& name) + { + const auto address = utils::cryptography::jenkins_one_at_a_time::compute(name); + return find(address); + } + + T* find(const uint32_t address) + { + const auto it = servers_.find(address); + if (it == servers_.end()) + { + return nullptr; + } + + return it->second.get(); + } + + void frame() + { + for (auto& server : servers_) + { + server.second->frame(); + } + } + + private: + std::unordered_map> servers_; + }; +} diff --git a/src/client/game/demonware/servers/auth3_server.cpp b/src/client/game/demonware/servers/auth3_server.cpp new file mode 100644 index 00000000..02a6adc5 --- /dev/null +++ b/src/client/game/demonware/servers/auth3_server.cpp @@ -0,0 +1,168 @@ +#include + +#include "auth3_server.hpp" +#include "../keys.hpp" + +#include +#include + +namespace demonware +{ + namespace + { +#pragma pack(push, 1) + struct auth_ticket + { + unsigned int m_magicNumber; + char m_type; + unsigned int m_titleID; + unsigned int m_timeIssued; + unsigned int m_timeExpires; + unsigned __int64 m_licenseID; + unsigned __int64 m_userID; + char m_username[64]; + char m_sessionKey[24]; + char m_usingHashMagicNumber[3]; + char m_hash[4]; + }; +#pragma pack(pop) + } + + void auth3_server::send_reply(reply* data) + { + if (!data) return; + this->send(data->data()); + } + + void auth3_server::handle(const std::string& packet) + { + if (packet.starts_with("POST /auth/")) + { +#ifdef DW_DEBUG + printf("[DW]: [auth]: user requested authentication.\n"); +#endif + return; + } + + unsigned int title_id = 0; + unsigned int iv_seed = 0; + std::string identity{}; + std::string token{}; + + rapidjson::Document j; + j.Parse(packet.data(), packet.size()); + + if (j.HasMember("title_id") && j["title_id"].IsString()) + { + title_id = std::stoul(j["title_id"].GetString()); + } + + if (j.HasMember("iv_seed") && j["iv_seed"].IsString()) + { + iv_seed = std::stoul(j["iv_seed"].GetString()); + } + + if (j.HasMember("identity") && j["identity"].IsString()) + { + identity = j["identity"].GetString(); + } + + if (j.HasMember("extra_data") && j["extra_data"].IsString()) + { + rapidjson::Document extra_data; + auto& ed = j["extra_data"]; + extra_data.Parse(ed.GetString(), ed.GetStringLength()); + + if (extra_data.HasMember("token") && extra_data["token"].IsString()) + { + auto& token_field = extra_data["token"]; + std::string token_b64(token_field.GetString(), token_field.GetStringLength()); + token = utils::cryptography::base64::decode(token_b64); + } + } + +#ifdef DW_DEBUG + printf("[DW]: [auth]: authenticating user %s\n", token.data() + 64); +#endif + + std::string auth_key(reinterpret_cast(token.data() + 32), 24); + std::string session_key( + "\x13\x37\x13\x37\x13\x37\x13\x37\x13\x37\x13\x37\x13\x37\x13\x37\x13\x37\x13\x37\x13\x37\x13\x37", 24); + + // client_ticket + auth_ticket ticket{}; + std::memset(&ticket, 0x0, sizeof ticket); + ticket.m_magicNumber = 0x0EFBDADDE; + ticket.m_type = 0; + ticket.m_titleID = title_id; + ticket.m_timeIssued = static_cast(time(nullptr)); + ticket.m_timeExpires = ticket.m_timeIssued + 30000; + ticket.m_licenseID = 0; + ticket.m_userID = reinterpret_cast(token.data() + 56); + strncpy_s(ticket.m_username, sizeof(ticket.m_username), reinterpret_cast(token.data() + 64), 64); + std::memcpy(ticket.m_sessionKey, session_key.data(), 24); + + const auto iv = utils::cryptography::tiger::compute(std::string(reinterpret_cast(&iv_seed), 4)); + const auto ticket_enc = utils::cryptography::des3::encrypt( + std::string(reinterpret_cast(&ticket), sizeof(ticket)), iv, auth_key); + const auto ticket_b64 = utils::cryptography::base64::encode( + reinterpret_cast(ticket_enc.data()), 128); + + // server_ticket + uint8_t auth_data[128]; + std::memset(&auth_data, 0, sizeof auth_data); + std::memcpy(auth_data, session_key.data(), 24); + const auto auth_data_b64 = utils::cryptography::base64::encode(auth_data, 128); + + demonware::set_session_key(session_key); + + // header time + char date[64]; + const auto now = time(nullptr); + tm gmtm{}; + gmtime_s(&gmtm, &now); + strftime(date, 64, "%a, %d %b %G %T", &gmtm); + + // json content + rapidjson::Document doc; + doc.SetObject(); + + doc.AddMember("auth_task", "29", doc.GetAllocator()); + doc.AddMember("code", "700", doc.GetAllocator()); + + auto seed = std::to_string(iv_seed); + doc.AddMember("iv_seed", rapidjson::StringRef(seed.data(), seed.size()), doc.GetAllocator()); + doc.AddMember("client_ticket", rapidjson::StringRef(ticket_b64.data(), ticket_b64.size()), doc.GetAllocator()); + doc.AddMember("server_ticket", rapidjson::StringRef(auth_data_b64.data(), auth_data_b64.size()), + doc.GetAllocator()); + doc.AddMember("client_id", "", doc.GetAllocator()); + doc.AddMember("account_type", "steam", doc.GetAllocator()); + doc.AddMember("crossplay_enabled", false, doc.GetAllocator()); + doc.AddMember("loginqueue_eanbled", false, doc.GetAllocator()); + + rapidjson::Value value{}; + doc.AddMember("lsg_endpoint", value, doc.GetAllocator()); + + rapidjson::StringBuffer buffer{}; + rapidjson::Writer> + writer(buffer); + doc.Accept(writer); + + // http stuff + std::string result; + result.append("HTTP/1.1 200 OK\r\n"); + result.append("Server: TornadoServer/4.5.3\r\n"); + result.append("Content-Type: application/json\r\n"); + result.append(utils::string::va("Date: %s GMT\r\n", date)); + result.append(utils::string::va("Content-Length: %d\r\n\r\n", buffer.GetLength())); + result.append(buffer.GetString(), buffer.GetLength()); + + raw_reply reply(result); + + this->send_reply(&reply); + +#ifdef DW_DEBUG + printf("[DW]: [auth]: user successfully authenticated.\n"); +#endif + } +} diff --git a/src/client/game/demonware/servers/auth3_server.hpp b/src/client/game/demonware/servers/auth3_server.hpp new file mode 100644 index 00000000..e58e73e7 --- /dev/null +++ b/src/client/game/demonware/servers/auth3_server.hpp @@ -0,0 +1,16 @@ +#pragma once +#include "tcp_server.hpp" +#include "../reply.hpp" + +namespace demonware +{ + class auth3_server : public tcp_server + { + public: + using tcp_server::tcp_server; + + private: + void send_reply(reply* data); + void handle(const std::string& packet) override; + }; +} diff --git a/src/client/game/demonware/servers/base_server.cpp b/src/client/game/demonware/servers/base_server.cpp new file mode 100644 index 00000000..727a36a6 --- /dev/null +++ b/src/client/game/demonware/servers/base_server.cpp @@ -0,0 +1,22 @@ +#include +#include "base_server.hpp" + +#include + +namespace demonware +{ + base_server::base_server(std::string name): name_(std::move(name)) + { + this->address_ = utils::cryptography::jenkins_one_at_a_time::compute(this->name_); + } + + const std::string& base_server::get_name() const + { + return this->name_; + } + + uint32_t base_server::get_address() const + { + return this->address_; + } +} diff --git a/src/client/game/demonware/servers/base_server.hpp b/src/client/game/demonware/servers/base_server.hpp new file mode 100644 index 00000000..1c3e5aa0 --- /dev/null +++ b/src/client/game/demonware/servers/base_server.hpp @@ -0,0 +1,30 @@ +#pragma once + +namespace demonware +{ + class base_server + { + public: + using stream_queue = std::queue; + using data_queue = std::queue; + + base_server(std::string name); + + base_server(base_server&&) = delete; + base_server(const base_server&) = delete; + base_server& operator=(base_server&&) = delete; + base_server& operator=(const base_server&) = delete; + + virtual ~base_server() = default; + + const std::string& get_name() const; + + uint32_t get_address() const; + + virtual void frame() = 0; + + private: + std::string name_; + std::uint32_t address_ = 0; + }; +} diff --git a/src/client/game/demonware/servers/lobby_server.cpp b/src/client/game/demonware/servers/lobby_server.cpp new file mode 100644 index 00000000..98c2dd73 --- /dev/null +++ b/src/client/game/demonware/servers/lobby_server.cpp @@ -0,0 +1,185 @@ +#include +#include "lobby_server.hpp" + +#include "../services.hpp" +#include "../keys.hpp" + +#include + +namespace demonware +{ + lobby_server::lobby_server(std::string name) : tcp_server(std::move(name)) + { + this->register_service(); + this->register_service(); + this->register_service(); + this->register_service(); + this->register_service(); + this->register_service(); + this->register_service(); + this->register_service(); + this->register_service(); + this->register_service(); + this->register_service(); + this->register_service(); + this->register_service(); + this->register_service(); + this->register_service(); + this->register_service(); + this->register_service(); + this->register_service(); + this->register_service(); + this->register_service(); + this->register_service(); + this->register_service(); + this->register_service(); + this->register_service(); + this->register_service(); + this->register_service(); + }; + + void lobby_server::send_reply(reply* data) + { + if (!data) return; + this->send(data->data()); + } + + void lobby_server::handle(const std::string& packet) + { + byte_buffer buffer(packet); + buffer.set_use_data_types(false); + + try + { + while (buffer.has_more_data()) + { + int size; + buffer.read_int32(&size); + + if (size <= 0) + { + const std::string zero("\x00\x00\x00\x00", 4); + raw_reply reply(zero); + this->send_reply(&reply); + return; + } + else if (size == 0xC8) + { +#ifdef DW_DEBUG + printf("[DW]: [lobby]: received client_header_ack.\n"); +#endif + + int c8; + buffer.read_int32(&c8); + std::string packet_1 = buffer.get_remaining(); + demonware::queue_packet_to_hash(packet_1); + + const std::string packet_2( + "\x16\x00\x00\x00\xab\x81\xd2\x00\x00\x00\x13\x37\x13\x37\x13\x37\x13\x37\x13\x37\x13\x37\x13\x37\x13\x37", + 26); + demonware::queue_packet_to_hash(packet_2); + + raw_reply reply(packet_2); + this->send_reply(&reply); +#ifdef DW_DEBUG + printf("[DW]: [lobby]: sending server_header_ack.\n"); +#endif + return; + } + + if (buffer.size() < size_t(size)) return; + + uint8_t check_ab; + buffer.read_ubyte(&check_ab); + if (check_ab == 0xAB) + { + uint8_t type; + buffer.read_ubyte(&type); + + if (type == 0x82) + { +#ifdef DW_DEBUG + printf("[DW]: [lobby]: received client_auth.\n"); +#endif + std::string packet_3(packet.data(), packet.size() - 8); // this 8 are client hash check? + + demonware::queue_packet_to_hash(packet_3); + demonware::derive_keys_s1(); + + char buff[14] = "\x0A\x00\x00\x00\xAB\x83"; + std::memcpy(&buff[6], demonware::get_response_id().data(), 8); + std::string response(buff, 14); + + raw_reply reply(response); + this->send_reply(&reply); + +#ifdef DW_DEBUG + printf("[DW]: [lobby]: sending server_auth_done.\n"); +#endif + return; + } + else if (type == 0x85) + { + uint32_t msg_count; + buffer.read_uint32(&msg_count); + + char seed[16]; + buffer.read(16, &seed); + + std::string enc = buffer.get_remaining(); + + char hash[8]; + std::memcpy(hash, &(enc.data()[enc.size() - 8]), 8); + + std::string dec = utils::cryptography::aes::decrypt( + std::string(enc.data(), enc.size() - 8), std::string(seed, 16), + demonware::get_decrypt_key()); + + byte_buffer serv(dec); + serv.set_use_data_types(false); + + uint32_t serv_size; + serv.read_uint32(&serv_size); + + uint8_t magic; // 0x86 + serv.read_ubyte(&magic); + + uint8_t service_id; + serv.read_ubyte(&service_id); + + this->call_service(service_id, serv.get_remaining()); + + return; + } + } + + printf("[DW]: [lobby]: ERROR! received unk message.\n"); + return; + } + } + catch (...) + { + } + } + + void lobby_server::call_service(const uint8_t id, const std::string& data) + { + const auto& it = this->services_.find(id); + + if (it != this->services_.end()) + { + it->second->exec_task(this, data); + } + else + { + printf("[DW]: [lobby]: missing service '%s'\n", utils::string::va("%d", id)); + + // return no error + byte_buffer buffer(data); + uint8_t task_id; + buffer.read_ubyte(&task_id); + + this->create_reply(task_id)->send(); + } + } +} diff --git a/src/client/game/demonware/servers/lobby_server.hpp b/src/client/game/demonware/servers/lobby_server.hpp new file mode 100644 index 00000000..8f1bda94 --- /dev/null +++ b/src/client/game/demonware/servers/lobby_server.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include "tcp_server.hpp" +#include "service_server.hpp" +#include "../service.hpp" + +namespace demonware +{ + class lobby_server : public tcp_server, service_server + { + public: + lobby_server(std::string name); + + template + void register_service() + { + static_assert(std::is_base_of::value, "service must inherit from service"); + + auto service = std::make_unique(); + const uint8_t id = service->id(); + + this->services_[id] = std::move(service); + } + + void send_reply(reply* data) override; + + private: + std::unordered_map> services_; + + void handle(const std::string& packet) override; + void call_service(uint8_t id, const std::string& data); + }; +} diff --git a/src/client/game/demonware/servers/service_server.hpp b/src/client/game/demonware/servers/service_server.hpp new file mode 100644 index 00000000..dcf40fe7 --- /dev/null +++ b/src/client/game/demonware/servers/service_server.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include "../reply.hpp" + +namespace demonware +{ + class service_server + { + public: + virtual ~service_server() = default; + + virtual std::shared_ptr create_message(uint8_t type) + { + auto reply = std::make_shared(this, type); + return reply; + } + + + virtual std::shared_ptr create_reply(uint8_t type, uint32_t error = 0) + { + auto reply = std::make_shared(this, type, error); + return reply; + } + + virtual void send_reply(reply* data) = 0; + }; +} diff --git a/src/client/game/demonware/servers/stun_server.cpp b/src/client/game/demonware/servers/stun_server.cpp new file mode 100644 index 00000000..2c4ebd32 --- /dev/null +++ b/src/client/game/demonware/servers/stun_server.cpp @@ -0,0 +1,62 @@ +#include +#include "stun_server.hpp" + +#include "../byte_buffer.hpp" + +namespace demonware +{ + void stun_server::handle(const endpoint_data& endpoint, const std::string& packet) + { + uint8_t type, version, padding; + + byte_buffer buffer(packet); + buffer.set_use_data_types(false); + buffer.read_ubyte(&type); + buffer.read_ubyte(&version); + buffer.read_ubyte(&padding); + + switch (type) + { + case 30: + this->ip_discovery(endpoint); + break; + case 20: + this->nat_discovery(endpoint); + break; + default: + break; + } + } + + void stun_server::ip_discovery(const endpoint_data& endpoint) + { + const uint32_t ip = 0x0100007f; + + byte_buffer buffer; + buffer.set_use_data_types(false); + buffer.write_ubyte(31); // type + buffer.write_ubyte(2); // version + buffer.write_ubyte(0); // version + buffer.write_uint32(ip); // external ip + buffer.write_uint16(3074); // port + + this->send(endpoint, buffer.get_buffer()); + } + + void stun_server::nat_discovery(const endpoint_data& endpoint) + { + const uint32_t ip = 0x0100007f; + + byte_buffer buffer; + buffer.set_use_data_types(false); + buffer.write_ubyte(21); // type + buffer.write_ubyte(2); // version + buffer.write_ubyte(0); // version + buffer.write_uint32(ip); // external ip + buffer.write_uint16(3074); // port + buffer.write_uint32(this->get_address()); // server ip + buffer.write_uint16(3074); // server port + + this->send(endpoint, buffer.get_buffer()); + } +} diff --git a/src/client/game/demonware/servers/stun_server.hpp b/src/client/game/demonware/servers/stun_server.hpp new file mode 100644 index 00000000..cb4192e1 --- /dev/null +++ b/src/client/game/demonware/servers/stun_server.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include "udp_server.hpp" + +namespace demonware +{ + class stun_server : public udp_server + { + public: + using udp_server::udp_server; + + private: + void handle(const endpoint_data& endpoint, const std::string& packet) override; + + void ip_discovery(const endpoint_data& endpoint); + void nat_discovery(const endpoint_data& endpoint); + }; +} diff --git a/src/client/game/demonware/servers/tcp_server.cpp b/src/client/game/demonware/servers/tcp_server.cpp new file mode 100644 index 00000000..8e60d20a --- /dev/null +++ b/src/client/game/demonware/servers/tcp_server.cpp @@ -0,0 +1,84 @@ +#include +#include "tcp_server.hpp" + +namespace demonware +{ + void tcp_server::handle_input(const char* buf, size_t size) + { + in_queue_.access([&](data_queue& queue) + { + queue.emplace(buf, size); + }); + } + + size_t tcp_server::handle_output(char* buf, size_t size) + { + if (out_queue_.get_raw().empty()) + { + return 0; + } + + return out_queue_.access([&](stream_queue& queue) + { + for (size_t i = 0; i < size; ++i) + { + if (queue.empty()) + { + return i; + } + + buf[i] = queue.front(); + queue.pop(); + } + + return size; + }); + } + + bool tcp_server::pending_data() + { + return !this->out_queue_.get_raw().empty(); + } + + void tcp_server::frame() + { + if (this->in_queue_.get_raw().empty()) + { + return; + } + + while (true) + { + std::string packet{}; + const auto result = this->in_queue_.access([&](data_queue& queue) + { + if (queue.empty()) + { + return false; + } + + packet = std::move(queue.front()); + queue.pop(); + return true; + }); + + if (!result) + { + break; + } + + this->handle(packet); + } + } + + void tcp_server::send(const std::string& data) + { + out_queue_.access([&](stream_queue& queue) + { + for (const auto& val : data) + { + queue.push(val); + } + }); + } +} diff --git a/src/client/game/demonware/servers/tcp_server.hpp b/src/client/game/demonware/servers/tcp_server.hpp new file mode 100644 index 00000000..3f6967ee --- /dev/null +++ b/src/client/game/demonware/servers/tcp_server.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include "base_server.hpp" +#include + +namespace demonware +{ + class tcp_server : public base_server + { + public: + using base_server::base_server; + + void handle_input(const char* buf, size_t size); + size_t handle_output(char* buf, size_t size); + bool pending_data(); + void frame() override; + + protected: + virtual void handle(const std::string& data) = 0; + + void send(const std::string& data); + + private: + utils::concurrency::container in_queue_; + utils::concurrency::container out_queue_; + }; +} diff --git a/src/client/game/demonware/servers/udp_server.cpp b/src/client/game/demonware/servers/udp_server.cpp new file mode 100644 index 00000000..019c3f84 --- /dev/null +++ b/src/client/game/demonware/servers/udp_server.cpp @@ -0,0 +1,103 @@ +#include +#include "udp_server.hpp" + +namespace demonware +{ + void udp_server::handle_input(const char* buf, size_t size, endpoint_data endpoint) + { + in_queue_.access([&](in_queue& queue) + { + in_packet p; + p.data = std::string{buf, size}; + p.endpoint = std::move(endpoint); + + queue.emplace(std::move(p)); + }); + } + + size_t udp_server::handle_output(SOCKET socket, char* buf, size_t size, sockaddr* address, int* addrlen) + { + return out_queue_.access([&](socket_queue_map& map) -> size_t + { + const auto entry = map.find(socket); + if (entry == map.end()) + { + return 0; + } + + auto& queue = entry->second; + + if (queue.empty()) + { + return 0; + } + + auto data = std::move(queue.front()); + queue.pop(); + + const auto copy_size = std::min(size, data.data.size()); + std::memcpy(buf, data.data.data(), copy_size); + std::memcpy(address, &data.address, sizeof(data.address)); + *addrlen = sizeof(data.address); + + return copy_size; + }); + } + + bool udp_server::pending_data(SOCKET socket) + { + return this->out_queue_.access([&](const socket_queue_map& map) + { + const auto entry = map.find(socket); + if (entry == map.end()) + { + return false; + } + + return !entry->second.empty(); + }); + } + + void udp_server::send(const endpoint_data& endpoint, std::string data) + { + out_queue_.access([&](socket_queue_map& map) + { + out_packet p; + p.data = std::move(data); + p.address = endpoint.address; + + map[endpoint.socket].emplace(std::move(p)); + }); + } + + void udp_server::frame() + { + if (this->in_queue_.get_raw().empty()) + { + return; + } + + while (true) + { + in_packet packet{}; + const auto result = this->in_queue_.access([&](in_queue& queue) + { + if (queue.empty()) + { + return false; + } + + packet = std::move(queue.front()); + queue.pop(); + return true; + }); + + if (!result) + { + break; + } + + this->handle(packet.endpoint, std::move(packet.data)); + } + } +} diff --git a/src/client/game/demonware/servers/udp_server.hpp b/src/client/game/demonware/servers/udp_server.hpp new file mode 100644 index 00000000..340c5980 --- /dev/null +++ b/src/client/game/demonware/servers/udp_server.hpp @@ -0,0 +1,62 @@ +#pragma once + +#include "base_server.hpp" +#include + +namespace demonware +{ + class udp_server : public base_server + { + public: + struct endpoint_data + { + SOCKET socket{}; + sockaddr_in address{}; + + endpoint_data() = default; + + endpoint_data(const SOCKET sock, const sockaddr* addr, const int size) + { + if (size != sizeof(this->address)) + { + throw std::runtime_error("Invalid size"); + } + + this->socket = sock; + std::memcpy(&this->address, addr, sizeof(this->address)); + } + }; + + using base_server::base_server; + + void handle_input(const char* buf, size_t size, endpoint_data endpoint); + size_t handle_output(SOCKET socket, char* buf, size_t size, sockaddr* address, int* addrlen); + bool pending_data(SOCKET socket); + + void frame() override; + + protected: + virtual void handle(const endpoint_data& endpoint, const std::string& data) = 0; + void send(const endpoint_data& endpoint, std::string data); + + private: + struct in_packet + { + std::string data; + endpoint_data endpoint; + }; + + struct out_packet + { + std::string data; + sockaddr_in address; + }; + + using in_queue = std::queue; + using out_queue = std::queue; + using socket_queue_map = std::unordered_map; + + utils::concurrency::container in_queue_; + utils::concurrency::container out_queue_; + }; +} diff --git a/src/client/game/demonware/servers/umbrella_server.cpp b/src/client/game/demonware/servers/umbrella_server.cpp new file mode 100644 index 00000000..cadce9dd --- /dev/null +++ b/src/client/game/demonware/servers/umbrella_server.cpp @@ -0,0 +1,11 @@ +#include + +#include "umbrella_server.hpp" + +namespace demonware +{ + void umbrella_server::handle(const std::string& packet) + { + // TODO: + } +} diff --git a/src/client/game/demonware/servers/umbrella_server.hpp b/src/client/game/demonware/servers/umbrella_server.hpp new file mode 100644 index 00000000..6507270c --- /dev/null +++ b/src/client/game/demonware/servers/umbrella_server.hpp @@ -0,0 +1,14 @@ +#pragma once +#include "tcp_server.hpp" + +namespace demonware +{ + class umbrella_server : public tcp_server + { + public: + using tcp_server::tcp_server; + + private: + void handle(const std::string& packet) override; + }; +} diff --git a/src/client/game/demonware/service.hpp b/src/client/game/demonware/service.hpp new file mode 100644 index 00000000..0e90b84c --- /dev/null +++ b/src/client/game/demonware/service.hpp @@ -0,0 +1,89 @@ +#pragma once +#include +#include "servers/service_server.hpp" + +namespace demonware +{ + class service + { + using callback_t = std::function; + + uint8_t id_; + std::string name_; + std::mutex mutex_; + uint8_t task_id_; + std::map tasks_; + + public: + virtual ~service() = default; + service(service&&) = delete; + service(const service&) = delete; + service& operator=(const service&) = delete; + + service(const uint8_t id, std::string name) : id_(id), name_(std::move(name)), task_id_(0) + { + } + + uint8_t id() const + { + return this->id_; + } + + const std::string& name() const + { + return this->name_; + } + + uint8_t task_id() const + { + return this->task_id_; + } + + virtual void exec_task(service_server* server, const std::string& data) + { + std::lock_guard _(this->mutex_); + + byte_buffer buffer(data); + + buffer.read_ubyte(&this->task_id_); + + const auto& it = this->tasks_.find(this->task_id_); + + if (it != this->tasks_.end()) + { +#ifdef DW_DEBUG + printf("[DW] %s: executing task '%d'\n", name_.data(), this->task_id_); +#endif + + it->second(server, &buffer); + } + else + { + printf("[DW] %s: missing task '%d'\n", name_.data(), this->task_id_); + + // return no error + server->create_reply(this->task_id_)->send(); + } + } + + protected: + + template + void register_task(const uint8_t id, T (Class::* callback)(Args ...) const) + { + this->tasks_[id] = [this, callback](Args ... args) -> T + { + return (reinterpret_cast(this)->*callback)(args...); + }; + } + + template + void register_task(const uint8_t id, T (Class::* callback)(Args ...)) + { + this->tasks_[id] = [this, callback](Args ... args) -> T + { + return (reinterpret_cast(this)->*callback)(args...); + }; + } + }; +} diff --git a/src/client/game/demonware/services.hpp b/src/client/game/demonware/services.hpp new file mode 100644 index 00000000..d8017ecf --- /dev/null +++ b/src/client/game/demonware/services.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include "bit_buffer.hpp" +#include "byte_buffer.hpp" +#include "data_types.hpp" +#include "reply.hpp" +#include "service.hpp" +#include "servers/service_server.hpp" + +#include "services/bdTeams.hpp" // 3 +#include "services/bdStats.hpp" // 4 +//#include "services/bdMessaging.hpp" // 6 +#include "services/bdProfiles.hpp" // 8 +#include "services/bdStorage.hpp" // 10 +#include "services/bdTitleUtilities.hpp" // 12 +#include "services/bdBandwidthTest.hpp" // 18 +#include "services/bdStats2.hpp" // 19 +#include "services/bdCounter.hpp" // 23 +#include "services/bdDML.hpp" // 27 +#include "services/bdGroup.hpp" // 28 +//#include "services/bdCMail.hpp" // 29 +#include "services/bdFacebook.hpp" // 36 +#include "services/bdAnticheat.hpp" // 38 +#include "services/bdContentStreaming.hpp" // 50 +//#include "services/bdTags.hpp" // 52 +#include "services/bdUNK63.hpp" // 63 +#include "services/bdUserGroups.hpp" // 65 +#include "services/bdEventLog.hpp" // 67 +#include "services/bdRichPresence.hpp" // 68 +//#include "services/bdTencent.hpp" // 71 +//#include "services/bdCodOnlineTeams.hpp" // 72 +#include "services/bdMarketplace.hpp" // 80 +#include "services/bdLeague.hpp" // 81 +#include "services/bdLeague2.hpp" // 82 +#include "services/bdStats3.hpp" // 91 +#include "services/bdPublisherVariables.hpp"// 95 +// AccountLinking // 86 +#include "services/bdPresence.hpp" //103 +#include "services/bdMarketingComms.hpp" //104 +#include "services/bdMatchMaking.hpp" //138 +#include "services/bdReward.hpp" //139 diff --git a/src/client/game/demonware/services/bdAnticheat.cpp b/src/client/game/demonware/services/bdAnticheat.cpp new file mode 100644 index 00000000..86f7f9f5 --- /dev/null +++ b/src/client/game/demonware/services/bdAnticheat.cpp @@ -0,0 +1,41 @@ +#include +#include "../services.hpp" + +namespace demonware +{ + bdAnticheat::bdAnticheat() : service(38, "bdAnticheat") + { + this->register_task(2, &bdAnticheat::answerChallenges); + this->register_task(3, &bdAnticheat::reportConsoleID); + this->register_task(4, &bdAnticheat::reportConsoleDetails); + this->register_task(5, &bdAnticheat::answerTOTPChallenge); + } + + void bdAnticheat::answerChallenges(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: Read data as soon as needed + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdAnticheat::reportConsoleID(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: Read data as soon as needed + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdAnticheat::reportConsoleDetails(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: Read data as soon as needed + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdAnticheat::answerTOTPChallenge(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: Read data as soon as needed + auto reply = server->create_reply(this->task_id()); + reply->send(); + } +} diff --git a/src/client/game/demonware/services/bdAnticheat.hpp b/src/client/game/demonware/services/bdAnticheat.hpp new file mode 100644 index 00000000..7fbe0f9c --- /dev/null +++ b/src/client/game/demonware/services/bdAnticheat.hpp @@ -0,0 +1,16 @@ +#pragma once + +namespace demonware +{ + class bdAnticheat final : public service + { + public: + bdAnticheat(); + + private: + void answerChallenges(service_server* server, byte_buffer* buffer) const; + void reportConsoleID(service_server* server, byte_buffer* buffer) const; + void reportConsoleDetails(service_server* server, byte_buffer* buffer) const; + void answerTOTPChallenge(service_server* server, byte_buffer* buffer) const; + }; +} diff --git a/src/client/game/demonware/services/bdBandwidthTest.cpp b/src/client/game/demonware/services/bdBandwidthTest.cpp new file mode 100644 index 00000000..2c41e4d2 --- /dev/null +++ b/src/client/game/demonware/services/bdBandwidthTest.cpp @@ -0,0 +1,27 @@ +#include +#include "../services.hpp" + +namespace demonware +{ + static uint8_t bandwidth_iw6[51] = + { + 0x0F, 0xC1, 0x1C, 0x37, 0xB8, 0xEF, 0x7C, 0xD6, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0xF4, 0x01, 0x00, 0x00, 0xD0, 0x07, + 0x00, 0x00, 0x10, 0x27, 0x00, 0x00, 0x88, 0x13, 0x00, 0x00, 0xF4, 0x01, + 0x00, 0x00, 0x02, 0x0C, 0x88, 0xB3, 0x04, 0x65, 0x89, 0xBF, 0xC3, 0x6A, + 0x27, 0x94, 0xD4, 0x8F + }; + + bdBandwidthTest::bdBandwidthTest() : service(18, "bdBandwidthTest") + { + } + + void bdBandwidthTest::exec_task(service_server* server, const std::string& data) + { + byte_buffer buffer; + buffer.write(sizeof bandwidth_iw6, bandwidth_iw6); + + auto reply = server->create_message(5); + reply->send(&buffer, true); + } +} diff --git a/src/client/game/demonware/services/bdBandwidthTest.hpp b/src/client/game/demonware/services/bdBandwidthTest.hpp new file mode 100644 index 00000000..c4feb8e9 --- /dev/null +++ b/src/client/game/demonware/services/bdBandwidthTest.hpp @@ -0,0 +1,13 @@ +#pragma once + +namespace demonware +{ + class bdBandwidthTest final : public service + { + public: + bdBandwidthTest(); + + private: + void exec_task(service_server* server, const std::string& data) override; + }; +} diff --git a/src/client/game/demonware/services/bdContentStreaming.cpp b/src/client/game/demonware/services/bdContentStreaming.cpp new file mode 100644 index 00000000..efd4db18 --- /dev/null +++ b/src/client/game/demonware/services/bdContentStreaming.cpp @@ -0,0 +1,25 @@ +#include +#include "../services.hpp" + +namespace demonware +{ + bdContentStreaming::bdContentStreaming() : service(50, "bdContentStreaming") + { + this->register_task(2, &bdContentStreaming::unk2); + this->register_task(3, &bdContentStreaming::unk3); + } + + void bdContentStreaming::unk2(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdContentStreaming::unk3(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } +} diff --git a/src/client/game/demonware/services/bdContentStreaming.hpp b/src/client/game/demonware/services/bdContentStreaming.hpp new file mode 100644 index 00000000..68319662 --- /dev/null +++ b/src/client/game/demonware/services/bdContentStreaming.hpp @@ -0,0 +1,14 @@ +#pragma once + +namespace demonware +{ + class bdContentStreaming final : public service + { + public: + bdContentStreaming(); + + private: + void unk2(service_server* server, byte_buffer* buffer) const; + void unk3(service_server* server, byte_buffer* buffer) const; + }; +} diff --git a/src/client/game/demonware/services/bdCounter.cpp b/src/client/game/demonware/services/bdCounter.cpp new file mode 100644 index 00000000..ee190ade --- /dev/null +++ b/src/client/game/demonware/services/bdCounter.cpp @@ -0,0 +1,25 @@ +#include +#include "../services.hpp" + +namespace demonware +{ + bdCounter::bdCounter() : service(23, "bdCounter") + { + this->register_task(1, &bdCounter::incrementCounters); + this->register_task(2, &bdCounter::getCounterTotals); + } + + void bdCounter::incrementCounters(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdCounter::getCounterTotals(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } +} diff --git a/src/client/game/demonware/services/bdCounter.hpp b/src/client/game/demonware/services/bdCounter.hpp new file mode 100644 index 00000000..ef5553fa --- /dev/null +++ b/src/client/game/demonware/services/bdCounter.hpp @@ -0,0 +1,14 @@ +#pragma once + +namespace demonware +{ + class bdCounter final : public service + { + public: + bdCounter(); + + private: + void incrementCounters(service_server* server, byte_buffer* buffer) const; + void getCounterTotals(service_server* server, byte_buffer* buffer) const; + }; +} diff --git a/src/client/game/demonware/services/bdDML.cpp b/src/client/game/demonware/services/bdDML.cpp new file mode 100644 index 00000000..d2a9da4c --- /dev/null +++ b/src/client/game/demonware/services/bdDML.cpp @@ -0,0 +1,52 @@ +#include +#include "../services.hpp" + +namespace demonware +{ + bdDML::bdDML() : service(27, "bdDML") + { + this->register_task(1, &bdDML::recordIP); + this->register_task(2, &bdDML::getUserData); + this->register_task(3, &bdDML::getUserHierarchicalData); + this->register_task(4, &bdDML::getUsersLastLogonData); + } + + void bdDML::recordIP(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdDML::getUserData(service_server* server, byte_buffer* /*buffer*/) const + { + auto result = new bdDMLRawData; + result->country_code = "US"; + result->country_code = "'Murica"; + result->region = "New York"; + result->city = "New York"; + result->latitude = 0; + result->longitude = 0; + + result->asn = 0x2119; + result->timezone = "+01:00"; + + auto reply = server->create_reply(this->task_id()); + reply->add(result); + reply->send(); + } + + void bdDML::getUserHierarchicalData(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdDML::getUsersLastLogonData(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } +} diff --git a/src/client/game/demonware/services/bdDML.hpp b/src/client/game/demonware/services/bdDML.hpp new file mode 100644 index 00000000..ac4c2abb --- /dev/null +++ b/src/client/game/demonware/services/bdDML.hpp @@ -0,0 +1,16 @@ +#pragma once + +namespace demonware +{ + class bdDML final : public service + { + public: + bdDML(); + + private: + void recordIP(service_server* server, byte_buffer* buffer) const; + void getUserData(service_server* server, byte_buffer* buffer) const; + void getUserHierarchicalData(service_server* server, byte_buffer* buffer) const; + void getUsersLastLogonData(service_server* server, byte_buffer* buffer) const; + }; +} diff --git a/src/client/game/demonware/services/bdEventLog.cpp b/src/client/game/demonware/services/bdEventLog.cpp new file mode 100644 index 00000000..8aa4127a --- /dev/null +++ b/src/client/game/demonware/services/bdEventLog.cpp @@ -0,0 +1,49 @@ +#include +#include "../services.hpp" + +namespace demonware +{ + bdEventLog::bdEventLog() : service(67, "bdEventLog") + { + this->register_task(1, &bdEventLog::recordEvent); + this->register_task(2, &bdEventLog::recordEventBin); + this->register_task(3, &bdEventLog::recordEvents); + this->register_task(4, &bdEventLog::recordEventsBin); + this->register_task(6, &bdEventLog::initializeFiltering); + } + + void bdEventLog::recordEvent(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdEventLog::recordEventBin(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdEventLog::recordEvents(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdEventLog::recordEventsBin(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdEventLog::initializeFiltering(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } +} diff --git a/src/client/game/demonware/services/bdEventLog.hpp b/src/client/game/demonware/services/bdEventLog.hpp new file mode 100644 index 00000000..b9083e5d --- /dev/null +++ b/src/client/game/demonware/services/bdEventLog.hpp @@ -0,0 +1,17 @@ +#pragma once + +namespace demonware +{ + class bdEventLog final : public service + { + public: + bdEventLog(); + + private: + void recordEvent(service_server* server, byte_buffer* buffer) const; + void recordEventBin(service_server* server, byte_buffer* buffer) const; + void recordEvents(service_server* server, byte_buffer* buffer) const; + void recordEventsBin(service_server* server, byte_buffer* buffer) const; + void initializeFiltering(service_server* server, byte_buffer* buffer) const; + }; +} diff --git a/src/client/game/demonware/services/bdFacebook.cpp b/src/client/game/demonware/services/bdFacebook.cpp new file mode 100644 index 00000000..ff01d58e --- /dev/null +++ b/src/client/game/demonware/services/bdFacebook.cpp @@ -0,0 +1,113 @@ +#include +#include "../services.hpp" + +namespace demonware +{ + bdFacebook::bdFacebook() : service(36, "bdFacebook") + { + this->register_task(1, &bdFacebook::registerAccount); + this->register_task(2, &bdFacebook::post); + this->register_task(3, &bdFacebook::unregisterAccount); + this->register_task(5, &bdFacebook::isRegistered); + this->register_task(6, &bdFacebook::getInfo); + this->register_task(7, &bdFacebook::getRegisteredAccounts); + this->register_task(8, &bdFacebook::getFriends); + this->register_task(9, &bdFacebook::getProfilePictures); + this->register_task(10, &bdFacebook::uploadPhoto); + this->register_task(11, &bdFacebook::registerToken); + this->register_task(12, &bdFacebook::uploadVideo); + this->register_task(13, &bdFacebook::getFriendsByID); + this->register_task(14, &bdFacebook::setLikeStatus); + } + + void bdFacebook::registerAccount(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdFacebook::post(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdFacebook::unregisterAccount(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdFacebook::isRegistered(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdFacebook::getInfo(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdFacebook::getRegisteredAccounts(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdFacebook::getFriends(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdFacebook::getProfilePictures(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdFacebook::uploadPhoto(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdFacebook::registerToken(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdFacebook::uploadVideo(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdFacebook::getFriendsByID(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdFacebook::setLikeStatus(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } +} diff --git a/src/client/game/demonware/services/bdFacebook.hpp b/src/client/game/demonware/services/bdFacebook.hpp new file mode 100644 index 00000000..419cca1d --- /dev/null +++ b/src/client/game/demonware/services/bdFacebook.hpp @@ -0,0 +1,25 @@ +#pragma once + +namespace demonware +{ + class bdFacebook final : public service + { + public: + bdFacebook(); + + private: + void registerAccount(service_server* server, byte_buffer* buffer) const; + void post(service_server* server, byte_buffer* buffer) const; + void unregisterAccount(service_server* server, byte_buffer* buffer) const; + void isRegistered(service_server* server, byte_buffer* buffer) const; + void getInfo(service_server* server, byte_buffer* buffer) const; + void getRegisteredAccounts(service_server* server, byte_buffer* buffer) const; + void getFriends(service_server* server, byte_buffer* buffer) const; + void getProfilePictures(service_server* server, byte_buffer* buffer) const; + void uploadPhoto(service_server* server, byte_buffer* buffer) const; + void registerToken(service_server* server, byte_buffer* buffer) const; + void uploadVideo(service_server* server, byte_buffer* buffer) const; + void getFriendsByID(service_server* server, byte_buffer* buffer) const; + void setLikeStatus(service_server* server, byte_buffer* buffer) const; + }; +} diff --git a/src/client/game/demonware/services/bdGroup.cpp b/src/client/game/demonware/services/bdGroup.cpp new file mode 100644 index 00000000..195d38f0 --- /dev/null +++ b/src/client/game/demonware/services/bdGroup.cpp @@ -0,0 +1,41 @@ +#include +#include "../services.hpp" + +namespace demonware +{ + bdGroup::bdGroup() : service(28, "bdGroup") + { + this->register_task(1, &bdGroup::setGroups); + this->register_task(2, &bdGroup::setGroupsForEntity); + this->register_task(3, &bdGroup::getEntityGroups); + this->register_task(4, &bdGroup::getGroupCounts); + } + + void bdGroup::setGroups(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdGroup::setGroupsForEntity(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdGroup::getEntityGroups(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdGroup::getGroupCounts(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } +} diff --git a/src/client/game/demonware/services/bdGroup.hpp b/src/client/game/demonware/services/bdGroup.hpp new file mode 100644 index 00000000..8810f257 --- /dev/null +++ b/src/client/game/demonware/services/bdGroup.hpp @@ -0,0 +1,16 @@ +#pragma once + +namespace demonware +{ + class bdGroup final : public service + { + public: + bdGroup(); + + private: + void setGroups(service_server* server, byte_buffer* buffer) const; + void setGroupsForEntity(service_server* server, byte_buffer* buffer) const; + void getEntityGroups(service_server* server, byte_buffer* buffer) const; + void getGroupCounts(service_server* server, byte_buffer* buffer) const; + }; +} diff --git a/src/client/game/demonware/services/bdLeague.cpp b/src/client/game/demonware/services/bdLeague.cpp new file mode 100644 index 00000000..add6d0d7 --- /dev/null +++ b/src/client/game/demonware/services/bdLeague.cpp @@ -0,0 +1,97 @@ +#include +#include "../services.hpp" + +namespace demonware +{ + bdLeague::bdLeague() : service(81, "bdLeague") + { + this->register_task(1, &bdLeague::getTeamID); + this->register_task(2, &bdLeague::getTeamIDsForUser); + this->register_task(3, &bdLeague::getTeamSubdivisions); + this->register_task(4, &bdLeague::setTeamName); + this->register_task(5, &bdLeague::setTeamIcon); + this->register_task(6, &bdLeague::getTeamInfos); + this->register_task(7, &bdLeague::getTeamLeaguesAndSubdivisions); + this->register_task(8, &bdLeague::getTeamMemberInfos); + this->register_task(10, &bdLeague::incrementGamesPlayedCount); + this->register_task(20, &bdLeague::getSubdivisionInfos); + this->register_task(21, &bdLeague::getTeamSubdivisionHistory); + } + + void bdLeague::getTeamID(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdLeague::getTeamIDsForUser(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdLeague::getTeamSubdivisions(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdLeague::setTeamName(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdLeague::setTeamIcon(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdLeague::getTeamInfos(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdLeague::getTeamLeaguesAndSubdivisions(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdLeague::getTeamMemberInfos(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdLeague::incrementGamesPlayedCount(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdLeague::getSubdivisionInfos(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdLeague::getTeamSubdivisionHistory(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } +} \ No newline at end of file diff --git a/src/client/game/demonware/services/bdLeague.hpp b/src/client/game/demonware/services/bdLeague.hpp new file mode 100644 index 00000000..1df36bb8 --- /dev/null +++ b/src/client/game/demonware/services/bdLeague.hpp @@ -0,0 +1,23 @@ +#pragma once + +namespace demonware +{ + class bdLeague final : public service + { + public: + bdLeague(); + + private: + void getTeamID(service_server* server, byte_buffer* buffer) const; + void getTeamIDsForUser(service_server* server, byte_buffer* buffer) const; + void getTeamSubdivisions(service_server* server, byte_buffer* buffer) const; + void setTeamName(service_server* server, byte_buffer* buffer) const; + void setTeamIcon(service_server* server, byte_buffer* buffer) const; + void getTeamInfos(service_server* server, byte_buffer* buffer) const; + void getTeamLeaguesAndSubdivisions(service_server* server, byte_buffer* buffer) const; + void getTeamMemberInfos(service_server* server, byte_buffer* buffer) const; + void incrementGamesPlayedCount(service_server* server, byte_buffer* buffer) const; + void getSubdivisionInfos(service_server* server, byte_buffer* buffer) const; + void getTeamSubdivisionHistory(service_server* server, byte_buffer* buffer) const; + }; +} \ No newline at end of file diff --git a/src/client/game/demonware/services/bdLeague2.cpp b/src/client/game/demonware/services/bdLeague2.cpp new file mode 100644 index 00000000..edb6a36e --- /dev/null +++ b/src/client/game/demonware/services/bdLeague2.cpp @@ -0,0 +1,41 @@ +#include +#include "../services.hpp" + +namespace demonware +{ + bdLeague2::bdLeague2() : service(82, "bdLeague") + { + this->register_task(1, &bdLeague2::writeStats); + this->register_task(2, &bdLeague2::readStatsByTeamID); + this->register_task(3, &bdLeague2::readStatsByRank); + this->register_task(4, &bdLeague2::readStatsByPivot); + } + + void bdLeague2::writeStats(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdLeague2::readStatsByTeamID(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdLeague2::readStatsByRank(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdLeague2::readStatsByPivot(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } +} \ No newline at end of file diff --git a/src/client/game/demonware/services/bdLeague2.hpp b/src/client/game/demonware/services/bdLeague2.hpp new file mode 100644 index 00000000..4737955b --- /dev/null +++ b/src/client/game/demonware/services/bdLeague2.hpp @@ -0,0 +1,16 @@ +#pragma once + +namespace demonware +{ + class bdLeague2 final : public service + { + public: + bdLeague2(); + + private: + void writeStats(service_server* server, byte_buffer* buffer) const; + void readStatsByTeamID(service_server* server, byte_buffer* buffer) const; + void readStatsByRank(service_server* server, byte_buffer* buffer) const; + void readStatsByPivot(service_server* server, byte_buffer* buffer) const; + }; +} \ No newline at end of file diff --git a/src/client/game/demonware/services/bdMarketingComms.cpp b/src/client/game/demonware/services/bdMarketingComms.cpp new file mode 100644 index 00000000..409b278a --- /dev/null +++ b/src/client/game/demonware/services/bdMarketingComms.cpp @@ -0,0 +1,25 @@ +#include +#include "../services.hpp" + +namespace demonware +{ + bdMarketingComms::bdMarketingComms() : service(104, "bdMarketingComms") + { + this->register_task(1, &bdMarketingComms::getMessages); + this->register_task(4, &bdMarketingComms::reportFullMessagesViewed); + } + + void bdMarketingComms::getMessages(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdMarketingComms::reportFullMessagesViewed(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } +} diff --git a/src/client/game/demonware/services/bdMarketingComms.hpp b/src/client/game/demonware/services/bdMarketingComms.hpp new file mode 100644 index 00000000..c973d7ae --- /dev/null +++ b/src/client/game/demonware/services/bdMarketingComms.hpp @@ -0,0 +1,14 @@ +#pragma once + +namespace demonware +{ + class bdMarketingComms final : public service + { + public: + bdMarketingComms(); + + private: + void getMessages(service_server* server, byte_buffer* buffer) const; + void reportFullMessagesViewed(service_server* server, byte_buffer* buffer) const; + }; +} diff --git a/src/client/game/demonware/services/bdMarketplace.cpp b/src/client/game/demonware/services/bdMarketplace.cpp new file mode 100644 index 00000000..3eb3f165 --- /dev/null +++ b/src/client/game/demonware/services/bdMarketplace.cpp @@ -0,0 +1,84 @@ +#include +#include "../services.hpp" + +#include + +namespace demonware +{ + bdMarketplace::bdMarketplace() : service(80, "bdMarketplace") + { + this->register_task(42, &bdMarketplace::startExchangeTransaction); // COD POINTS purchase ? + //this->register_task(43, &bdMarketplace::purchaseOnSteamInitialize); // COD POINTS purchase ? + this->register_task(49, &bdMarketplace::getExpiredInventoryItems); + this->register_task(60, &bdMarketplace::steamProcessDurable); + this->register_task(122, &bdMarketplace::purchaseSkus); + this->register_task(130, &bdMarketplace::getBalance); + this->register_task(165, &bdMarketplace::getInventoryPaginated); + this->register_task(193, &bdMarketplace::putPlayersInventoryItems); + this->register_task(232, &bdMarketplace::getEntitlements); + } + + void bdMarketplace::startExchangeTransaction(service_server* server, byte_buffer* buffer) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdMarketplace::purchaseOnSteamInitialize(service_server* server, byte_buffer* buffer) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdMarketplace::getExpiredInventoryItems(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdMarketplace::steamProcessDurable(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdMarketplace::purchaseSkus(service_server* server, byte_buffer* buffer) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdMarketplace::getBalance(service_server* server, byte_buffer* buffer) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + + reply->send(); + } + + void bdMarketplace::getInventoryPaginated(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdMarketplace::putPlayersInventoryItems(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdMarketplace::getEntitlements(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } +} diff --git a/src/client/game/demonware/services/bdMarketplace.hpp b/src/client/game/demonware/services/bdMarketplace.hpp new file mode 100644 index 00000000..7b2ebf27 --- /dev/null +++ b/src/client/game/demonware/services/bdMarketplace.hpp @@ -0,0 +1,21 @@ +#pragma once + +namespace demonware +{ + class bdMarketplace final : public service + { + public: + bdMarketplace(); + + private: + void startExchangeTransaction(service_server* server, byte_buffer* buffer) const; + void purchaseOnSteamInitialize(service_server* server, byte_buffer* buffer) const; + void getExpiredInventoryItems(service_server* server, byte_buffer* buffer) const; + void steamProcessDurable(service_server* server, byte_buffer* buffer) const; + void purchaseSkus(service_server* server, byte_buffer* buffer) const; + void getBalance(service_server* server, byte_buffer* buffer) const; + void getInventoryPaginated(service_server* server, byte_buffer* buffer) const; + void putPlayersInventoryItems(service_server* server, byte_buffer* buffer) const; + void getEntitlements(service_server* server, byte_buffer* buffer) const; + }; +} diff --git a/src/client/game/demonware/services/bdMatchMaking.cpp b/src/client/game/demonware/services/bdMatchMaking.cpp new file mode 100644 index 00000000..6b59d1eb --- /dev/null +++ b/src/client/game/demonware/services/bdMatchMaking.cpp @@ -0,0 +1,129 @@ +#include +#include "../services.hpp" + +namespace demonware +{ + bdMatchMaking::bdMatchMaking() : service(138, "bdMatchMaking") + { + this->register_task(1, &bdMatchMaking::createSession); + this->register_task(2, &bdMatchMaking::updateSession); + this->register_task(3, &bdMatchMaking::deleteSession); + this->register_task(4, &bdMatchMaking::findSessionFromID); + this->register_task(5, &bdMatchMaking::findSessions); + this->register_task(6, &bdMatchMaking::notifyJoin); + this->register_task(8, &bdMatchMaking::inviteToSession); + this->register_task(9, &bdMatchMaking::submitPerformance); + this->register_task(10, &bdMatchMaking::getPerformanceValues); + this->register_task(11, &bdMatchMaking::getSessionInvites); + this->register_task(12, &bdMatchMaking::updateSessionPlayers); + this->register_task(13, &bdMatchMaking::findSessionsPaged); + this->register_task(14, &bdMatchMaking::findSessionsByEntityIDs); + this->register_task(15, &bdMatchMaking::findSessionsFromIDs); + this->register_task(16, &bdMatchMaking::findSessionsTwoPass); + } + + void bdMatchMaking::createSession(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdMatchMaking::updateSession(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdMatchMaking::deleteSession(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdMatchMaking::findSessionFromID(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdMatchMaking::findSessions(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdMatchMaking::getPerformanceValues(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdMatchMaking::getSessionInvites(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdMatchMaking::updateSessionPlayers(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdMatchMaking::submitPerformance(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdMatchMaking::notifyJoin(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdMatchMaking::inviteToSession(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdMatchMaking::findSessionsPaged(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdMatchMaking::findSessionsByEntityIDs(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdMatchMaking::findSessionsFromIDs(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdMatchMaking::findSessionsTwoPass(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } +} diff --git a/src/client/game/demonware/services/bdMatchMaking.hpp b/src/client/game/demonware/services/bdMatchMaking.hpp new file mode 100644 index 00000000..0cc1a79c --- /dev/null +++ b/src/client/game/demonware/services/bdMatchMaking.hpp @@ -0,0 +1,27 @@ +#pragma once + +namespace demonware +{ + class bdMatchMaking final : public service + { + public: + bdMatchMaking(); + + private: + void createSession(service_server* server, byte_buffer* buffer) const; + void updateSession(service_server* server, byte_buffer* buffer) const; + void deleteSession(service_server* server, byte_buffer* buffer) const; + void findSessionFromID(service_server* server, byte_buffer* buffer) const; + void findSessions(service_server* server, byte_buffer* buffer) const; + void inviteToSession(service_server* server, byte_buffer* buffer) const; + void submitPerformance(service_server* server, byte_buffer* buffer) const; + void notifyJoin(service_server* server, byte_buffer* buffer) const; + void getPerformanceValues(service_server* server, byte_buffer* buffer) const; + void getSessionInvites(service_server* server, byte_buffer* buffer) const; + void updateSessionPlayers(service_server* server, byte_buffer* buffer) const; + void findSessionsPaged(service_server* server, byte_buffer* buffer) const; + void findSessionsByEntityIDs(service_server* server, byte_buffer* buffer) const; + void findSessionsFromIDs(service_server* server, byte_buffer* buffer) const; + void findSessionsTwoPass(service_server* server, byte_buffer* buffer) const; + }; +} diff --git a/src/client/game/demonware/services/bdPresence.cpp b/src/client/game/demonware/services/bdPresence.cpp new file mode 100644 index 00000000..a043a169 --- /dev/null +++ b/src/client/game/demonware/services/bdPresence.cpp @@ -0,0 +1,25 @@ +#include +#include "../services.hpp" + +namespace demonware +{ + bdPresence::bdPresence() : service(103, "bdPresence") + { + this->register_task(1, &bdPresence::unk1); + this->register_task(3, &bdPresence::unk3); + } + + void bdPresence::unk1(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdPresence::unk3(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } +} diff --git a/src/client/game/demonware/services/bdPresence.hpp b/src/client/game/demonware/services/bdPresence.hpp new file mode 100644 index 00000000..df76cae2 --- /dev/null +++ b/src/client/game/demonware/services/bdPresence.hpp @@ -0,0 +1,14 @@ +#pragma once + +namespace demonware +{ + class bdPresence final : public service + { + public: + bdPresence(); + + private: + void unk1(service_server* server, byte_buffer* buffer) const; + void unk3(service_server* server, byte_buffer* buffer) const; + }; +} diff --git a/src/client/game/demonware/services/bdProfiles.cpp b/src/client/game/demonware/services/bdProfiles.cpp new file mode 100644 index 00000000..ac7a466a --- /dev/null +++ b/src/client/game/demonware/services/bdProfiles.cpp @@ -0,0 +1,73 @@ +#include +#include "../services.hpp" + +namespace demonware +{ + bdProfiles::bdProfiles() : service(8, "bdProfiles") + { + this->register_task(1, &bdProfiles::getPublicInfos); + this->register_task(2, &bdProfiles::getPrivateInfo); + this->register_task(3, &bdProfiles::setPublicInfo); + this->register_task(4, &bdProfiles::setPrivateInfo); + this->register_task(5, &bdProfiles::deleteProfile); + this->register_task(6, &bdProfiles::setPrivateInfoByUserID); + this->register_task(7, &bdProfiles::getPrivateInfoByUserID); + this->register_task(8, &bdProfiles::setPublicInfoByUserID); + } + + void bdProfiles::getPublicInfos(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdProfiles::setPublicInfo(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdProfiles::getPrivateInfo(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdProfiles::setPrivateInfo(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdProfiles::deleteProfile(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdProfiles::setPrivateInfoByUserID(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdProfiles::getPrivateInfoByUserID(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdProfiles::setPublicInfoByUserID(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } +} diff --git a/src/client/game/demonware/services/bdProfiles.hpp b/src/client/game/demonware/services/bdProfiles.hpp new file mode 100644 index 00000000..b9d0b8e7 --- /dev/null +++ b/src/client/game/demonware/services/bdProfiles.hpp @@ -0,0 +1,20 @@ +#pragma once + +namespace demonware +{ + class bdProfiles final : public service + { + public: + bdProfiles(); + + private: + void getPublicInfos(service_server* server, byte_buffer* buffer) const; + void getPrivateInfo(service_server* server, byte_buffer* buffer) const; + void setPublicInfo(service_server* server, byte_buffer* buffer) const; + void setPrivateInfo(service_server* server, byte_buffer* buffer) const; + void deleteProfile(service_server* server, byte_buffer* buffer) const; + void setPrivateInfoByUserID(service_server* server, byte_buffer* buffer) const; + void getPrivateInfoByUserID(service_server* server, byte_buffer* buffer) const; + void setPublicInfoByUserID(service_server* server, byte_buffer* buffer) const; + }; +} diff --git a/src/client/game/demonware/services/bdPublisherVariables.cpp b/src/client/game/demonware/services/bdPublisherVariables.cpp new file mode 100644 index 00000000..73df8a26 --- /dev/null +++ b/src/client/game/demonware/services/bdPublisherVariables.cpp @@ -0,0 +1,17 @@ +#include +#include "../services.hpp" + +namespace demonware +{ + bdPublisherVariables::bdPublisherVariables() : service(95, "bdPublisherVariables") + { + this->register_task(1, &bdPublisherVariables::retrievePublisherVariables); + } + + void bdPublisherVariables::retrievePublisherVariables(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } +} diff --git a/src/client/game/demonware/services/bdPublisherVariables.hpp b/src/client/game/demonware/services/bdPublisherVariables.hpp new file mode 100644 index 00000000..9833602d --- /dev/null +++ b/src/client/game/demonware/services/bdPublisherVariables.hpp @@ -0,0 +1,13 @@ +#pragma once + +namespace demonware +{ + class bdPublisherVariables final : public service + { + public: + bdPublisherVariables(); + + private: + void retrievePublisherVariables(service_server* server, byte_buffer* buffer) const; + }; +} diff --git a/src/client/game/demonware/services/bdReward.cpp b/src/client/game/demonware/services/bdReward.cpp new file mode 100644 index 00000000..86fe661b --- /dev/null +++ b/src/client/game/demonware/services/bdReward.cpp @@ -0,0 +1,41 @@ +#include +#include "../services.hpp" + +namespace demonware +{ + bdReward::bdReward() : service(139, "bdReward") + { + this->register_task(1, &bdReward::incrementTime); + this->register_task(2, &bdReward::claimRewardRoll); + this->register_task(3, &bdReward::claimClientAchievements); + this->register_task(4, &bdReward::reportRewardEvents); + } + + void bdReward::incrementTime(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdReward::claimRewardRoll(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdReward::claimClientAchievements(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdReward::reportRewardEvents(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } +} diff --git a/src/client/game/demonware/services/bdReward.hpp b/src/client/game/demonware/services/bdReward.hpp new file mode 100644 index 00000000..a013123f --- /dev/null +++ b/src/client/game/demonware/services/bdReward.hpp @@ -0,0 +1,16 @@ +#pragma once + +namespace demonware +{ + class bdReward final : public service + { + public: + bdReward(); + + private: + void incrementTime(service_server* server, byte_buffer* buffer) const; + void claimRewardRoll(service_server* server, byte_buffer* buffer) const; + void claimClientAchievements(service_server* server, byte_buffer* buffer) const; + void reportRewardEvents(service_server* server, byte_buffer* buffer) const; + }; +} diff --git a/src/client/game/demonware/services/bdRichPresence.cpp b/src/client/game/demonware/services/bdRichPresence.cpp new file mode 100644 index 00000000..adfb0fbb --- /dev/null +++ b/src/client/game/demonware/services/bdRichPresence.cpp @@ -0,0 +1,25 @@ +#include +#include "../services.hpp" + +namespace demonware +{ + bdRichPresence::bdRichPresence() : service(68, "bdRichPresence") + { + this->register_task(1, &bdRichPresence::setInfo); + this->register_task(2, &bdRichPresence::getInfo); + } + + void bdRichPresence::setInfo(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdRichPresence::getInfo(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } +} diff --git a/src/client/game/demonware/services/bdRichPresence.hpp b/src/client/game/demonware/services/bdRichPresence.hpp new file mode 100644 index 00000000..821270cf --- /dev/null +++ b/src/client/game/demonware/services/bdRichPresence.hpp @@ -0,0 +1,14 @@ +#pragma once + +namespace demonware +{ + class bdRichPresence final : public service + { + public: + bdRichPresence(); + + private: + void setInfo(service_server* server, byte_buffer* buffer) const; + void getInfo(service_server* server, byte_buffer* buffer) const; + }; +} diff --git a/src/client/game/demonware/services/bdStats.cpp b/src/client/game/demonware/services/bdStats.cpp new file mode 100644 index 00000000..607545f8 --- /dev/null +++ b/src/client/game/demonware/services/bdStats.cpp @@ -0,0 +1,113 @@ +#include +#include "../services.hpp" + +namespace demonware +{ + bdStats::bdStats() : service(4, "bdStats") + { + this->register_task(1, &bdStats::writeStats); + this->register_task(2, &bdStats::deleteStats); + this->register_task(3, &bdStats::unk3); // leaderboards + this->register_task(4, &bdStats::readStatsByRank); + this->register_task(5, &bdStats::readStatsByPivot); + this->register_task(6, &bdStats::readStatsByRating); + this->register_task(7, &bdStats::readStatsByMultipleRanks); + this->register_task(8, &bdStats::readExternalTitleStats); + this->register_task(10, &bdStats::readExternalTitleNamedStats); + this->register_task(11, &bdStats::readStatsByLeaderboardIDsAndEntityIDs); + this->register_task(12, &bdStats::readStatsByMultipleRatings); + this->register_task(13, &bdStats::readStatsByEntityID); + this->register_task(14, &bdStats::writeServerValidatedStats); + } + + void bdStats::writeStats(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdStats::deleteStats(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdStats::unk3(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdStats::readStatsByRank(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdStats::readStatsByPivot(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdStats::readStatsByRating(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdStats::readStatsByMultipleRanks(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdStats::readExternalTitleStats(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdStats::readExternalTitleNamedStats(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdStats::readStatsByLeaderboardIDsAndEntityIDs(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdStats::readStatsByMultipleRatings(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdStats::readStatsByEntityID(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdStats::writeServerValidatedStats(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } +} diff --git a/src/client/game/demonware/services/bdStats.hpp b/src/client/game/demonware/services/bdStats.hpp new file mode 100644 index 00000000..86165775 --- /dev/null +++ b/src/client/game/demonware/services/bdStats.hpp @@ -0,0 +1,25 @@ +#pragma once + +namespace demonware +{ + class bdStats final : public service + { + public: + bdStats(); + + private: + void writeStats(service_server* server, byte_buffer* buffer) const; + void deleteStats(service_server* server, byte_buffer* buffer) const; + void unk3(service_server* server, byte_buffer* buffer) const; + void readStatsByRank(service_server* server, byte_buffer* buffer) const; + void readStatsByPivot(service_server* server, byte_buffer* buffer) const; + void readStatsByRating(service_server* server, byte_buffer* buffer) const; + void readStatsByMultipleRanks(service_server* server, byte_buffer* buffer) const; + void readExternalTitleStats(service_server* server, byte_buffer* buffer) const; + void readExternalTitleNamedStats(service_server* server, byte_buffer* buffer) const; + void readStatsByLeaderboardIDsAndEntityIDs(service_server* server, byte_buffer* buffer) const; + void readStatsByMultipleRatings(service_server* server, byte_buffer* buffer) const; + void readStatsByEntityID(service_server* server, byte_buffer* buffer) const; + void writeServerValidatedStats(service_server* server, byte_buffer* buffer) const; + }; +} diff --git a/src/client/game/demonware/services/bdStats2.cpp b/src/client/game/demonware/services/bdStats2.cpp new file mode 100644 index 00000000..ee1f7e77 --- /dev/null +++ b/src/client/game/demonware/services/bdStats2.cpp @@ -0,0 +1,25 @@ +#include +#include "../services.hpp" + +namespace demonware +{ + bdStats2::bdStats2() : service(19, "bdStats") + { + this->register_task(1, &bdStats2::startArbitratedSession); + this->register_task(2, &bdStats2::writeArbitratedStats); + } + + void bdStats2::startArbitratedSession(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdStats2::writeArbitratedStats(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } +} \ No newline at end of file diff --git a/src/client/game/demonware/services/bdStats2.hpp b/src/client/game/demonware/services/bdStats2.hpp new file mode 100644 index 00000000..74c08932 --- /dev/null +++ b/src/client/game/demonware/services/bdStats2.hpp @@ -0,0 +1,14 @@ +#pragma once + +namespace demonware +{ + class bdStats2 final : public service + { + public: + bdStats2(); + + private: + void startArbitratedSession(service_server* server, byte_buffer* buffer) const; + void writeArbitratedStats(service_server* server, byte_buffer* buffer) const; + }; +} \ No newline at end of file diff --git a/src/client/game/demonware/services/bdStats3.cpp b/src/client/game/demonware/services/bdStats3.cpp new file mode 100644 index 00000000..0916d488 --- /dev/null +++ b/src/client/game/demonware/services/bdStats3.cpp @@ -0,0 +1,65 @@ +#include +#include "../services.hpp" + +namespace demonware +{ + bdStats3::bdStats3() : service(91, "bdStats") + { + this->register_task(1, &bdStats3::deleteCSFileStats); + this->register_task(3, &bdStats3::readStatsByEntityID); + this->register_task(4, &bdStats3::readStatsByRank); + this->register_task(5, &bdStats3::readStatsByPivot); + this->register_task(6, &bdStats3::readStatsByRating); + this->register_task(7, &bdStats3::readStatsByMultipleRanks); + this->register_task(11, &bdStats3::readStatsByLeaderboardIDsAndEntityIDs); + } + + void bdStats3::deleteCSFileStats(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdStats3::readStatsByEntityID(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdStats3::readStatsByRank(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdStats3::readStatsByPivot(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdStats3::readStatsByRating(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdStats3::readStatsByMultipleRanks(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdStats3::readStatsByLeaderboardIDsAndEntityIDs(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } +} \ No newline at end of file diff --git a/src/client/game/demonware/services/bdStats3.hpp b/src/client/game/demonware/services/bdStats3.hpp new file mode 100644 index 00000000..efeb5831 --- /dev/null +++ b/src/client/game/demonware/services/bdStats3.hpp @@ -0,0 +1,19 @@ +#pragma once + +namespace demonware +{ + class bdStats3 final : public service + { + public: + bdStats3(); + + private: + void deleteCSFileStats(service_server* server, byte_buffer* buffer) const; + void readStatsByEntityID(service_server* server, byte_buffer* buffer) const; + void readStatsByRank(service_server* server, byte_buffer* buffer) const; + void readStatsByPivot(service_server* server, byte_buffer* buffer) const; + void readStatsByRating(service_server* server, byte_buffer* buffer) const; + void readStatsByMultipleRanks(service_server* server, byte_buffer* buffer) const; + void readStatsByLeaderboardIDsAndEntityIDs(service_server* server, byte_buffer* buffer) const; + }; +} \ No newline at end of file diff --git a/src/client/game/demonware/services/bdStorage.cpp b/src/client/game/demonware/services/bdStorage.cpp new file mode 100644 index 00000000..a8ead05b --- /dev/null +++ b/src/client/game/demonware/services/bdStorage.cpp @@ -0,0 +1,246 @@ +#include +#include "../services.hpp" + +#include +#include +#include + +#include "game/game.hpp" + +namespace demonware +{ + bdStorage::bdStorage() : service(10, "bdStorage") + { + this->register_task(20, &bdStorage::list_publisher_files); + this->register_task(21, &bdStorage::get_publisher_file); + this->register_task(24, &bdStorage::set_user_file); + this->register_task(16, &bdStorage::get_user_file); + this->register_task(12, &bdStorage::unk12); + + //this->map_publisher_resource("motd-.*\\.txt", DW_MOTD); + // this->map_publisher_resource("ffotd-.*\\.ff", DW_FASTFILE); + //this->map_publisher_resource("playlists(_.+)?\\.aggr", DW_PLAYLISTS); + } + + void bdStorage::map_publisher_resource(const std::string& expression, const INT id) + { + auto data = utils::nt::load_resource(id); + this->map_publisher_resource_variant(expression, std::move(data)); + } + + void bdStorage::map_publisher_resource_variant(const std::string& expression, resource_variant resource) + { + if (resource.valueless_by_exception()) + { + throw std::runtime_error("Publisher resource variant is empty!"); + } + + this->publisher_resources_.emplace_back(std::regex{expression}, std::move(resource)); + } + + bool bdStorage::load_publisher_resource(const std::string& name, std::string& buffer) + { + for (const auto& resource : this->publisher_resources_) + { + if (std::regex_match(name, resource.first)) + { + if (std::holds_alternative(resource.second)) + { + buffer = std::get(resource.second); + } + else + { + buffer = std::get(resource.second)(); + } + + return true; + } + } + +#ifdef DW_DEBUG + printf("[DW]: [bdStorage]: missing publisher file: %s\n", name.data()); +#endif + + return false; + } + + void bdStorage::list_publisher_files(service_server* server, byte_buffer* buffer) + { + uint32_t date; + uint16_t num_results, offset; + std::string unk, filename, data; + + buffer->read_string(&unk); + buffer->read_uint32(&date); + buffer->read_uint16(&num_results); + buffer->read_uint16(&offset); + buffer->read_string(&filename); + +#ifdef DW_DEBUG + printf("[DW]: [bdStorage]: list publisher files: %s\n", filename.data()); +#endif + + auto reply = server->create_reply(this->task_id()); + + if (this->load_publisher_resource(filename, data)) + { + auto* info = new bdFileInfo; + + info->file_id = *reinterpret_cast(utils::cryptography::sha1::compute(filename).data()); + info->filename = filename; + info->create_time = 0; + info->modified_time = info->create_time; + info->file_size = uint32_t(data.size()); + info->owner_id = 0; + info->priv = false; + + reply->add(info); + } + + reply->send(); + } + + void bdStorage::get_publisher_file(service_server* server, byte_buffer* buffer) + { + std::string unk, filename; + buffer->read_string(&unk); + buffer->read_string(&filename); + +#ifdef DW_DEBUG + printf("[DW]: [bdStorage]: loading publisher file: %s\n", filename.data()); +#endif + + std::string data; + + if (this->load_publisher_resource(filename, data)) + { +#ifdef DW_DEBUG + printf("[DW]: [bdStorage]: sending publisher file: %s, size: %lld\n", filename.data(), data.size()); +#endif + + auto reply = server->create_reply(this->task_id()); + reply->add(new bdFileData(data)); + reply->send(); + } + else + { + server->create_reply(this->task_id(), game::BD_NO_FILE)->send(); + } + } + + std::string bdStorage::get_user_file_path(const std::string& name) + { + return "players2/user/" + name; + } + + void bdStorage::set_user_file(service_server* server, byte_buffer* buffer) const + { + uint64_t owner; + uint32_t numfiles; + std::string game, platform; + + buffer->read_string(&game); + buffer->read_uint64(&owner); + buffer->read_string(&platform); + buffer->read_uint32(&numfiles); + + auto reply = server->create_reply(this->task_id()); + + for (uint32_t i = 0; i < numfiles; i++) + { + std::string filename, data; + uint32_t unk; + bool priv; + + buffer->read_string(&filename); + buffer->read_blob(&data); + buffer->read_uint32(&unk); + buffer->read_bool(&priv); + + const auto path = get_user_file_path(filename); + utils::io::write_file(path, data); + + auto* info = new bdFile2; + + info->unk1 = 0; + info->unk2 = 0; + info->unk3 = 0; + info->priv = false; + info->owner_id = owner; + info->platform = platform; + info->filename = filename; + info->data = data; + +#ifdef DW_DEBUG + printf("[DW]: [bdStorage]: set user file: %s\n", filename.data()); +#endif + + reply->add(info); + } + + reply->send(); + } + + void bdStorage::get_user_file(service_server* server, byte_buffer* buffer) const + { + uint32_t unk32_0; + uint32_t numfiles, count = 0; + uint64_t owner; + std::string game, platform; + + buffer->read_string(&game); + buffer->read_uint32(&unk32_0); + buffer->read_uint64(&owner); + buffer->read_string(&platform); + buffer->read_uint64(&owner); + buffer->read_string(&platform); + buffer->read_uint32(&numfiles); + + auto reply = server->create_reply(this->task_id()); + + for (uint32_t i = 0; i < numfiles; i++) + { + std::string filename, data; + buffer->read_string(&filename); + + const auto path = get_user_file_path(filename); + if (!utils::io::read_file(path, &data)) + { +#ifdef DW_DEBUG + printf("[DW]: [bdStorage]: get user file: missing file: %s, %s, %s\n", game.data(), filename.data(), platform.data()); +#endif + continue; + } + + auto response = new bdFile; + response->owner_id = owner; + response->unk = 0; + response->platform = platform; + response->filename = filename; + response->data = data; + + reply->add(response); + ++count; + +#ifdef DW_DEBUG + printf("[DW]: [bdStorage]: get user file: %s, %s, %s\n", game.data(), filename.data(), platform.data()); +#endif + } + + if (count == numfiles) + { + reply->send(); + } + else + { + server->create_reply(this->task_id(), game::BD_NO_FILE)->send(); + } + } + + void bdStorage::unk12(service_server* server, byte_buffer* buffer) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } +} diff --git a/src/client/game/demonware/services/bdStorage.hpp b/src/client/game/demonware/services/bdStorage.hpp new file mode 100644 index 00000000..88382538 --- /dev/null +++ b/src/client/game/demonware/services/bdStorage.hpp @@ -0,0 +1,27 @@ +#pragma once + +namespace demonware +{ + class bdStorage final : public service + { + public: + bdStorage(); + + private: + using callback = std::function; + using resource_variant = std::variant; + std::vector> publisher_resources_; + + void map_publisher_resource(const std::string& expression, INT id); + void map_publisher_resource_variant(const std::string& expression, resource_variant resource); + bool load_publisher_resource(const std::string& name, std::string& buffer); + + void list_publisher_files(service_server* server, byte_buffer* buffer); + void get_publisher_file(service_server* server, byte_buffer* buffer); + void set_user_file(service_server* server, byte_buffer* buffer) const; + void get_user_file(service_server* server, byte_buffer* buffer) const; + void unk12(service_server* server, byte_buffer* buffer) const; + + static std::string get_user_file_path(const std::string& name); + }; +} diff --git a/src/client/game/demonware/services/bdTeams.cpp b/src/client/game/demonware/services/bdTeams.cpp new file mode 100644 index 00000000..574cd013 --- /dev/null +++ b/src/client/game/demonware/services/bdTeams.cpp @@ -0,0 +1,409 @@ +#include +#include "../services.hpp" + +namespace demonware +{ + bdTeams::bdTeams() : service(3, "bdTeams") + { + this->register_task(30, &bdTeams::createTeam); + this->register_task(31, &bdTeams::updateTeamName); + this->register_task(32, &bdTeams::promoteMember); + this->register_task(33, &bdTeams::kickMember); + this->register_task(34, &bdTeams::leaveTeam); + this->register_task(35, &bdTeams::proposeMembership); + this->register_task(36, &bdTeams::rejectMembership); + this->register_task(37, &bdTeams::acceptMembership); + this->register_task(38, &bdTeams::getPublicProfiles); + this->register_task(39, &bdTeams::getPrivateProfile); + this->register_task(40, &bdTeams::getPublicMemberProfiles); + this->register_task(41, &bdTeams::getPrivateMemberProfiles); + this->register_task(42, &bdTeams::setPublicProfile); + this->register_task(43, &bdTeams::setPrivateProfile); + this->register_task(44, &bdTeams::setPublicMemberProfile); + this->register_task(45, &bdTeams::setPrivateMemberProfile); + this->register_task(46, &bdTeams::getMemberships); + this->register_task(47, &bdTeams::getMembers); + this->register_task(48, &bdTeams::getOutgoingProposals); + this->register_task(49, &bdTeams::withdrawProposal); + this->register_task(50, &bdTeams::demoteMember); + this->register_task(51, &bdTeams::promoteMemberToOwner); + this->register_task(52, &bdTeams::getTeamInfo); + this->register_task(53, &bdTeams::getIncomingProposals); + this->register_task(54, &bdTeams::sendInstantMessage); + this->register_task(56, &bdTeams::getMembershipsUser); + this->register_task(57, &bdTeams::sendInstantMessageToTeam); + this->register_task(58, &bdTeams::searchPublicTeamProfiles); + this->register_task(63, &bdTeams::addApplication); + this->register_task(64, &bdTeams::getApplicationsByTeam); + this->register_task(65, &bdTeams::acceptApplication); + this->register_task(66, &bdTeams::rejectApplication); + this->register_task(68, &bdTeams::autoJoinTeam); + this->register_task(70, &bdTeams::createTeamWithProfiles); + this->register_task(73, &bdTeams::banMember); + this->register_task(74, &bdTeams::unbanMember); + this->register_task(76, &bdTeams::blockApplication); + this->register_task(78, &bdTeams::unblockApplication); + this->register_task(80, &bdTeams::updateTeamType); + this->register_task(82, &bdTeams::setOnline); + this->register_task(83, &bdTeams::getMembershipsWithCounts); + this->register_task(84, &bdTeams::getMembershipsWithCountsUser); + this->register_task(85, &bdTeams::searchTeams); + this->register_task(86, &bdTeams::createTeamWithProfilesAndTeamType); + this->register_task(87, &bdTeams::getMembershipsWithCountsAndTeamTypeUser); + this->register_task(88, &bdTeams::getMembershipsWithCountsAndTeamType); + this->register_task(89, &bdTeams::getTeamInfoWithTeamType); + this->register_task(91, &bdTeams::setTeamAutoJoin); + this->register_task(92, &bdTeams::getTeamAutoJoin); + this->register_task(94, &bdTeams::getMembersAndPrivileges); + } + + void bdTeams::createTeam(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTeams::updateTeamName(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTeams::promoteMember(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTeams::kickMember(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTeams::leaveTeam(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTeams::proposeMembership(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTeams::rejectMembership(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTeams::acceptMembership(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTeams::getPublicProfiles(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTeams::getPrivateProfile(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTeams::getPublicMemberProfiles(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTeams::getPrivateMemberProfiles(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTeams::setPublicProfile(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTeams::setPrivateProfile(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTeams::setPublicMemberProfile(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTeams::setPrivateMemberProfile(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTeams::getMemberships(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTeams::getMembers(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTeams::getOutgoingProposals(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTeams::withdrawProposal(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTeams::demoteMember(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTeams::promoteMemberToOwner(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTeams::getTeamInfo(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTeams::getIncomingProposals(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTeams::sendInstantMessage(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTeams::getMembershipsUser(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTeams::sendInstantMessageToTeam(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTeams::searchPublicTeamProfiles(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTeams::addApplication(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTeams::getApplicationsByTeam(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTeams::acceptApplication(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTeams::rejectApplication(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTeams::autoJoinTeam(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTeams::createTeamWithProfiles(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTeams::banMember(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTeams::unbanMember(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTeams::blockApplication(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTeams::unblockApplication(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTeams::updateTeamType(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTeams::setOnline(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTeams::getMembershipsWithCounts(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTeams::getMembershipsWithCountsUser(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTeams::searchTeams(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTeams::createTeamWithProfilesAndTeamType(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTeams::getMembershipsWithCountsAndTeamTypeUser(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTeams::getMembershipsWithCountsAndTeamType(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTeams::getTeamInfoWithTeamType(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTeams::setTeamAutoJoin(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTeams::getTeamAutoJoin(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdTeams::getMembersAndPrivileges(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } +} diff --git a/src/client/game/demonware/services/bdTeams.hpp b/src/client/game/demonware/services/bdTeams.hpp new file mode 100644 index 00000000..62874e73 --- /dev/null +++ b/src/client/game/demonware/services/bdTeams.hpp @@ -0,0 +1,62 @@ +#pragma once + +namespace demonware +{ + class bdTeams final : public service + { + public: + bdTeams(); + + private: + void createTeam(service_server* server, byte_buffer* buffer) const; + void updateTeamName(service_server* server, byte_buffer* buffer) const; + void promoteMember(service_server* server, byte_buffer* buffer) const; + void kickMember(service_server* server, byte_buffer* buffer) const; + void leaveTeam(service_server* server, byte_buffer* buffer) const; + void proposeMembership(service_server* server, byte_buffer* buffer) const; + void rejectMembership(service_server* server, byte_buffer* buffer) const; + void acceptMembership(service_server* server, byte_buffer* buffer) const; + void getPublicProfiles(service_server* server, byte_buffer* buffer) const; + void getPrivateProfile(service_server* server, byte_buffer* buffer) const; + void getPublicMemberProfiles(service_server* server, byte_buffer* buffer) const; + void getPrivateMemberProfiles(service_server* server, byte_buffer* buffer) const; + void setPublicProfile(service_server* server, byte_buffer* buffer) const; + void setPrivateProfile(service_server* server, byte_buffer* buffer) const; + void setPublicMemberProfile(service_server* server, byte_buffer* buffer) const; + void setPrivateMemberProfile(service_server* server, byte_buffer* buffer) const; + void getMemberships(service_server* server, byte_buffer* buffer) const; + void getMembers(service_server* server, byte_buffer* buffer) const; + void getOutgoingProposals(service_server* server, byte_buffer* buffer) const; + void withdrawProposal(service_server* server, byte_buffer* buffer) const; + void demoteMember(service_server* server, byte_buffer* buffer) const; + void promoteMemberToOwner(service_server* server, byte_buffer* buffer) const; + void getTeamInfo(service_server* server, byte_buffer* buffer) const; + void getIncomingProposals(service_server* server, byte_buffer* buffer) const; + void sendInstantMessage(service_server* server, byte_buffer* buffer) const; + void getMembershipsUser(service_server* server, byte_buffer* buffer) const; + void sendInstantMessageToTeam(service_server* server, byte_buffer* buffer) const; + void searchPublicTeamProfiles(service_server* server, byte_buffer* buffer) const; + void addApplication(service_server* server, byte_buffer* buffer) const; + void getApplicationsByTeam(service_server* server, byte_buffer* buffer) const; + void acceptApplication(service_server* server, byte_buffer* buffer) const; + void rejectApplication(service_server* server, byte_buffer* buffer) const; + void autoJoinTeam(service_server* server, byte_buffer* buffer) const; + void createTeamWithProfiles(service_server* server, byte_buffer* buffer) const; + void banMember(service_server* server, byte_buffer* buffer) const; + void unbanMember(service_server* server, byte_buffer* buffer) const; + void blockApplication(service_server* server, byte_buffer* buffer) const; + void unblockApplication(service_server* server, byte_buffer* buffer) const; + void updateTeamType(service_server* server, byte_buffer* buffer) const; + void setOnline(service_server* server, byte_buffer* buffer) const; + void getMembershipsWithCounts(service_server* server, byte_buffer* buffer) const; + void getMembershipsWithCountsUser(service_server* server, byte_buffer* buffer) const; + void searchTeams(service_server* server, byte_buffer* buffer) const; + void createTeamWithProfilesAndTeamType(service_server* server, byte_buffer* buffer) const; + void getMembershipsWithCountsAndTeamTypeUser(service_server* server, byte_buffer* buffer) const; + void getMembershipsWithCountsAndTeamType(service_server* server, byte_buffer* buffer) const; + void getTeamInfoWithTeamType(service_server* server, byte_buffer* buffer) const; + void setTeamAutoJoin(service_server* server, byte_buffer* buffer) const; + void getTeamAutoJoin(service_server* server, byte_buffer* buffer) const; + void getMembersAndPrivileges(service_server* server, byte_buffer* buffer) const; + }; +} diff --git a/src/client/game/demonware/services/bdTitleUtilities.cpp b/src/client/game/demonware/services/bdTitleUtilities.cpp new file mode 100644 index 00000000..8b1f90e9 --- /dev/null +++ b/src/client/game/demonware/services/bdTitleUtilities.cpp @@ -0,0 +1,20 @@ +#include +#include "../services.hpp" + +namespace demonware +{ + bdTitleUtilities::bdTitleUtilities() : service(12, "bdTitleUtilities") + { + this->register_task(6, &bdTitleUtilities::get_server_time); + } + + void bdTitleUtilities::get_server_time(service_server* server, byte_buffer* /*buffer*/) const + { + auto* const time_result = new bdTimeStamp; + time_result->unix_time = uint32_t(time(nullptr)); + + auto reply = server->create_reply(this->task_id()); + reply->add(time_result); + reply->send(); + } +} diff --git a/src/client/game/demonware/services/bdTitleUtilities.hpp b/src/client/game/demonware/services/bdTitleUtilities.hpp new file mode 100644 index 00000000..3f57105d --- /dev/null +++ b/src/client/game/demonware/services/bdTitleUtilities.hpp @@ -0,0 +1,13 @@ +#pragma once + +namespace demonware +{ + class bdTitleUtilities final : public service + { + public: + bdTitleUtilities(); + + private: + void get_server_time(service_server* server, byte_buffer* buffer) const; + }; +} diff --git a/src/client/game/demonware/services/bdUNK63.cpp b/src/client/game/demonware/services/bdUNK63.cpp new file mode 100644 index 00000000..e6f4945a --- /dev/null +++ b/src/client/game/demonware/services/bdUNK63.cpp @@ -0,0 +1,17 @@ +#include +#include "../services.hpp" + +namespace demonware +{ + bdUNK63::bdUNK63() : service(63, "bdUNK63") + { + //this->register_task(6, "unk6", &bdUNK63::unk6); + } + + void bdUNK63::unk(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } +} diff --git a/src/client/game/demonware/services/bdUNK63.hpp b/src/client/game/demonware/services/bdUNK63.hpp new file mode 100644 index 00000000..85ed78bf --- /dev/null +++ b/src/client/game/demonware/services/bdUNK63.hpp @@ -0,0 +1,13 @@ +#pragma once + +namespace demonware +{ + class bdUNK63 final : public service + { + public: + bdUNK63(); + + private: + void unk(service_server* server, byte_buffer* buffer) const; + }; +} diff --git a/src/client/game/demonware/services/bdUserGroups.cpp b/src/client/game/demonware/services/bdUserGroups.cpp new file mode 100644 index 00000000..206bdcb7 --- /dev/null +++ b/src/client/game/demonware/services/bdUserGroups.cpp @@ -0,0 +1,97 @@ +#include +#include "../services.hpp" + +namespace demonware +{ + bdUserGroups::bdUserGroups() : service(65, "bdUserGroups") + { + this->register_task(1, &bdUserGroups::createGroup); + this->register_task(2, &bdUserGroups::deleteGroup); + this->register_task(3, &bdUserGroups::joinGroup); + this->register_task(4, &bdUserGroups::leaveGroup); + this->register_task(5, &bdUserGroups::getMembershipInfo); + this->register_task(6, &bdUserGroups::changeMemberType); + this->register_task(7, &bdUserGroups::getNumMembers); + this->register_task(8, &bdUserGroups::getMembers); + this->register_task(9, &bdUserGroups::getMemberships); + this->register_task(10, &bdUserGroups::readStatsByRank); + this->register_task(11, &bdUserGroups::getGroupLists); + } + + void bdUserGroups::createGroup(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdUserGroups::deleteGroup(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdUserGroups::joinGroup(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdUserGroups::leaveGroup(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdUserGroups::getMembershipInfo(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdUserGroups::changeMemberType(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdUserGroups::getNumMembers(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdUserGroups::getMembers(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdUserGroups::getMemberships(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdUserGroups::readStatsByRank(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } + + void bdUserGroups::getGroupLists(service_server* server, byte_buffer* /*buffer*/) const + { + // TODO: + auto reply = server->create_reply(this->task_id()); + reply->send(); + } +} \ No newline at end of file diff --git a/src/client/game/demonware/services/bdUserGroups.hpp b/src/client/game/demonware/services/bdUserGroups.hpp new file mode 100644 index 00000000..558abba3 --- /dev/null +++ b/src/client/game/demonware/services/bdUserGroups.hpp @@ -0,0 +1,23 @@ +#pragma once + +namespace demonware +{ + class bdUserGroups final : public service + { + public: + bdUserGroups(); + + private: + void createGroup(service_server* server, byte_buffer* buffer) const; + void deleteGroup(service_server* server, byte_buffer* buffer) const; + void joinGroup(service_server* server, byte_buffer* buffer) const; + void leaveGroup(service_server* server, byte_buffer* buffer) const; + void getMembershipInfo(service_server* server, byte_buffer* buffer) const; + void changeMemberType(service_server* server, byte_buffer* buffer) const; + void getNumMembers(service_server* server, byte_buffer* buffer) const; + void getMembers(service_server* server, byte_buffer* buffer) const; + void getMemberships(service_server* server, byte_buffer* buffer) const; + void readStatsByRank(service_server* server, byte_buffer* buffer) const; + void getGroupLists(service_server* server, byte_buffer* buffer) const; + }; +} \ No newline at end of file diff --git a/src/client/game/structs.hpp b/src/client/game/structs.hpp index 4191e19b..d6738023 100644 --- a/src/client/game/structs.hpp +++ b/src/client/game/structs.hpp @@ -64,4 +64,345 @@ namespace game { unsigned int name; }; // Incomplete + + + enum bdLobbyErrorCode : uint32_t + { + BD_NO_ERROR = 0x0, + BD_TOO_MANY_TASKS = 0x1, + BD_NOT_CONNECTED = 0x2, + BD_SEND_FAILED = 0x3, + BD_HANDLE_TASK_FAILED = 0x4, + BD_START_TASK_FAILED = 0x5, + BD_RESULT_EXCEEDS_BUFFER_SIZE = 0x64, + BD_ACCESS_DENIED = 0x65, + BD_EXCEPTION_IN_DB = 0x66, + BD_MALFORMED_TASK_HEADER = 0x67, + BD_INVALID_ROW = 0x68, + BD_EMPTY_ARG_LIST = 0x69, + BD_PARAM_PARSE_ERROR = 0x6A, + BD_PARAM_MISMATCHED_TYPE = 0x6B, + BD_SERVICE_NOT_AVAILABLE = 0x6C, + BD_CONNECTION_RESET = 0x6D, + BD_INVALID_USER_ID = 0x6E, + BD_LOBBY_PROTOCOL_VERSION_FAILURE = 0x6F, + BD_LOBBY_INTERNAL_FAILURE = 0x70, + BD_LOBBY_PROTOCOL_ERROR = 0x71, + BD_LOBBY_FAILED_TO_DECODE_UTF8 = 0x72, + BD_LOBBY_ASCII_EXPECTED = 0x73, + BD_ASYNCHRONOUS_ERROR = 0xC8, + BD_STREAMING_COMPLETE = 0xC9, + BD_MEMBER_NO_PROPOSAL = 0x12C, + BD_TEAMNAME_ALREADY_EXISTS = 0x12D, + BD_MAX_TEAM_MEMBERSHIPS_LIMITED = 0x12E, + BD_MAX_TEAM_OWNERSHIPS_LIMITED = 0x12F, + BD_NOT_A_TEAM_MEMBER = 0x130, + BD_INVALID_TEAM_ID = 0x131, + BD_INVALID_TEAM_NAME = 0x132, + BD_NOT_A_TEAM_OWNER = 0x133, + BD_NOT_AN_ADMIN_OR_OWNER = 0x134, + BD_MEMBER_PROPOSAL_EXISTS = 0x135, + BD_MEMBER_EXISTS = 0x136, + BD_TEAM_FULL = 0x137, + BD_VULGAR_TEAM_NAME = 0x138, + BD_TEAM_USERID_BANNED = 0x139, + BD_TEAM_EMPTY = 0x13A, + BD_INVALID_TEAM_PROFILE_QUERY_ID = 0x13B, + BD_TEAMNAME_TOO_SHORT = 0x13C, + BD_UNIQUE_PROFILE_DATA_EXISTS_ALREADY = 0x13D, + BD_INVALID_LEADERBOARD_ID = 0x190, + BD_INVALID_STATS_SET = 0x191, + BD_EMPTY_STATS_SET_IGNORED = 0x193, + BD_NO_DIRECT_ACCESS_TO_ARBITRATED_LBS = 0x194, + BD_STATS_WRITE_PERMISSION_DENIED = 0x195, + BD_STATS_WRITE_TYPE_DATA_TYPE_MISMATCH = 0x196, + BD_NO_STATS_FOR_USER = 0x197, + BD_INVALID_ACCESS_TO_UNRANKED_LB = 0x198, + BD_INVALID_EXTERNAL_TITLE_ID = 0x199, + BD_DIFFERENT_LEADERBOARD_SCHEMAS = 0x19A, + BD_TOO_MANY_LEADERBOARDS_REQUESTED = 0x19B, + BD_ENTITLEMENTS_ERROR = 0x19C, + BD_ENTITLEMENTS_INVALID_TITLEID = 0x19D, + BD_ENTITLEMENTS_INVALID_LEADERBOARDID = 0x19E, + BD_ENTITLEMENTS_INVALID_GET_MODE_FOR_TITLE = 0x19F, + BD_ENTITLEMENTS_URL_CONNECTION_ERROR = 0x1A0, + BD_ENTITLEMENTS_CONFIG_ERROR = 0x1A1, + BD_ENTITLEMENTS_NAMED_PARENT_ERROR = 0x1A2, + BD_ENTITLEMENTS_NAMED_KEY_ERROR = 0x1A3, + BD_TOO_MANY_ENTITY_IDS_REQUESTED = 0x1A4, + BD_STATS_READ_FAILED = 0x1A5, + BD_INVALID_TITLE_ID = 0x1F4, + BD_MESSAGING_INVALID_MAIL_ID = 0x258, + BD_SELF_BLOCK_NOT_ALLOWED = 0x259, + BD_GLOBAL_MESSAGE_ACCESS_DENIED = 0x25A, + BD_GLOBAL_MESSAGES_USER_LIMIT_EXCEEDED = 0x25B, + BD_MESSAGING_SENDER_DOES_NOT_EXIST = 0x25C, + BD_AUTH_NO_ERROR = 0x2BC, + BD_AUTH_BAD_REQUEST = 0x2BD, + BD_AUTH_SERVER_CONFIG_ERROR = 0x2BE, + BD_AUTH_BAD_TITLE_ID = 0x2BF, + BD_AUTH_BAD_ACCOUNT = 0x2C0, + BD_AUTH_ILLEGAL_OPERATION = 0x2C1, + BD_AUTH_INCORRECT_LICENSE_CODE = 0x2C2, + BD_AUTH_CREATE_USERNAME_EXISTS = 0x2C3, + BD_AUTH_CREATE_USERNAME_ILLEGAL = 0x2C4, + BD_AUTH_CREATE_USERNAME_VULGAR = 0x2C5, + BD_AUTH_CREATE_MAX_ACC_EXCEEDED = 0x2C6, + BD_AUTH_MIGRATE_NOT_SUPPORTED = 0x2C7, + BD_AUTH_TITLE_DISABLED = 0x2C8, + BD_AUTH_ACCOUNT_EXPIRED = 0x2C9, + BD_AUTH_ACCOUNT_LOCKED = 0x2CA, + BD_AUTH_UNKNOWN_ERROR = 0x2CB, + BD_AUTH_INCORRECT_PASSWORD = 0x2CC, + BD_AUTH_IP_NOT_IN_ALLOWED_RANGE = 0x2CD, + BD_AUTH_WII_TOKEN_VERIFICATION_FAILED = 0x2CE, + BD_AUTH_WII_AUTHENTICATION_FAILED = 0x2CF, + BD_AUTH_IP_KEY_LIMIT_REACHED = 0x2D0, + BD_AUTH_INVALID_GSPID = 0x2D1, + BD_AUTH_INVALID_IP_RANGE_ID = 0x2D2, + BD_AUTH_3DS_TOKEN_VERIFICATION_FAILED = 0x2D1, + BD_AUTH_3DS_AUTHENTICATION_FAILED = 0x2D2, + BD_AUTH_STEAM_APP_ID_MISMATCH = 0x2D3, + BD_AUTH_ABACCOUNTS_APP_ID_MISMATCH = 0x2D4, + BD_AUTH_CODO_USERNAME_NOT_SET = 0x2D5, + BD_AUTH_WIIU_TOKEN_VERIFICATION_FAILED = 0x2D6, + BD_AUTH_WIIU_AUTHENTICATION_FAILED = 0x2D7, + BD_AUTH_CODO_USERNAME_NOT_BASE64 = 0x2D8, + BD_AUTH_CODO_USERNAME_NOT_UTF8 = 0x2D9, + BD_AUTH_TENCENT_TICKET_EXPIRED = 0x2DA, + BD_AUTH_PS3_SERVICE_ID_MISMATCH = 0x2DB, + BD_AUTH_CODOID_NOT_WHITELISTED = 0x2DC, + BD_AUTH_PLATFORM_TOKEN_ERROR = 0x2DD, + BD_AUTH_JSON_FORMAT_ERROR = 0x2DE, + BD_AUTH_REPLY_CONTENT_ERROR = 0x2DF, + BD_AUTH_THIRD_PARTY_TOKEN_EXPIRED = 0x2E0, + BD_AUTH_CONTINUING = 0x2E1, + BD_AUTH_PLATFORM_DEVICE_ID_ERROR = 0x2E4, + BD_NO_PROFILE_INFO_EXISTS = 0x320, + BD_FRIENDSHIP_NOT_REQUSTED = 0x384, + BD_NOT_A_FRIEND = 0x385, + BD_SELF_FRIENDSHIP_NOT_ALLOWED = 0x387, + BD_FRIENDSHIP_EXISTS = 0x388, + BD_PENDING_FRIENDSHIP_EXISTS = 0x389, + BD_USERID_BANNED = 0x38A, + BD_FRIENDS_FULL = 0x38C, + BD_FRIENDS_NO_RICH_PRESENCE = 0x38D, + BD_RICH_PRESENCE_TOO_LARGE = 0x38E, + BD_NO_FILE = 0x3E8, + BD_PERMISSION_DENIED = 0x3E9, + BD_FILESIZE_LIMIT_EXCEEDED = 0x3EA, + BD_FILENAME_MAX_LENGTH_EXCEEDED = 0x3EB, + BD_EXTERNAL_STORAGE_SERVICE_ERROR = 0x3EC, + BD_CHANNEL_DOES_NOT_EXIST = 0x44D, + BD_CHANNEL_ALREADY_SUBSCRIBED = 0x44E, + BD_CHANNEL_NOT_SUBSCRIBED = 0x44F, + BD_CHANNEL_FULL = 0x450, + BD_CHANNEL_SUBSCRIPTIONS_FULL = 0x451, + BD_CHANNEL_NO_SELF_WHISPERING = 0x452, + BD_CHANNEL_ADMIN_REQUIRED = 0x453, + BD_CHANNEL_TARGET_NOT_SUBSCRIBED = 0x454, + BD_CHANNEL_REQUIRES_PASSWORD = 0x455, + BD_CHANNEL_TARGET_IS_SELF = 0x456, + BD_CHANNEL_PUBLIC_BAN_NOT_ALLOWED = 0x457, + BD_CHANNEL_USER_BANNED = 0x458, + BD_CHANNEL_PUBLIC_PASSWORD_NOT_ALLOWED = 0x459, + BD_CHANNEL_PUBLIC_KICK_NOT_ALLOWED = 0x45A, + BD_CHANNEL_MUTED = 0x45B, + BD_EVENT_DESC_TRUNCATED = 0x4B0, + BD_CONTENT_UNLOCK_UNKNOWN_ERROR = 0x514, + BD_UNLOCK_KEY_INVALID = 0x515, + BD_UNLOCK_KEY_ALREADY_USED_UP = 0x516, + BD_SHARED_UNLOCK_LIMIT_REACHED = 0x517, + BD_DIFFERENT_HARDWARE_ID = 0x518, + BD_INVALID_CONTENT_OWNER = 0x519, + BD_CONTENT_UNLOCK_INVALID_USER = 0x51A, + BD_CONTENT_UNLOCK_INVALID_CATEGORY = 0x51B, + BD_KEY_ARCHIVE_INVALID_WRITE_TYPE = 0x5DC, + BD_KEY_ARCHIVE_EXCEEDED_MAX_IDS_PER_REQUEST = 0x5DD, + BD_BANDWIDTH_TEST_TRY_AGAIN = 0x712, + BD_BANDWIDTH_TEST_STILL_IN_PROGRESS = 0x713, + BD_BANDWIDTH_TEST_NOT_PROGRESS = 0x714, + BD_BANDWIDTH_TEST_SOCKET_ERROR = 0x715, + BD_INVALID_SESSION_NONCE = 0x76D, + BD_ARBITRATION_FAILURE = 0x76F, + BD_ARBITRATION_USER_NOT_REGISTERED = 0x771, + BD_ARBITRATION_NOT_CONFIGURED = 0x772, + BD_CONTENTSTREAMING_FILE_NOT_AVAILABLE = 0x7D0, + BD_CONTENTSTREAMING_STORAGE_SPACE_EXCEEDED = 0x7D1, + BD_CONTENTSTREAMING_NUM_FILES_EXCEEDED = 0x7D2, + BD_CONTENTSTREAMING_UPLOAD_BANDWIDTH_EXCEEDED = 0x7D3, + BD_CONTENTSTREAMING_FILENAME_MAX_LENGTH_EXCEEDED = 0x7D4, + BD_CONTENTSTREAMING_MAX_THUMB_DATA_SIZE_EXCEEDED = 0x7D5, + BD_CONTENTSTREAMING_DOWNLOAD_BANDWIDTH_EXCEEDED = 0x7D6, + BD_CONTENTSTREAMING_NOT_ENOUGH_DOWNLOAD_BUFFER_SPACE = 0x7D7, + BD_CONTENTSTREAMING_SERVER_NOT_CONFIGURED = 0x7D8, + BD_CONTENTSTREAMING_INVALID_APPLE_RECEIPT = 0x7DA, + BD_CONTENTSTREAMING_APPLE_STORE_NOT_AVAILABLE = 0x7DB, + BD_CONTENTSTREAMING_APPLE_RECEIPT_FILENAME_MISMATCH = 0x7DC, + BD_CONTENTSTREAMING_HTTP_ERROR = 0x7E4, + BD_CONTENTSTREAMING_FAILED_TO_START_HTTP = 0x7E5, + BD_CONTENTSTREAMING_LOCALE_INVALID = 0x7E6, + BD_CONTENTSTREAMING_LOCALE_MISSING = 0x7E7, + BD_VOTERANK_ERROR_EMPTY_RATING_SUBMISSION = 0x7EE, + BD_VOTERANK_ERROR_MAX_VOTES_EXCEEDED = 0x7EF, + BD_VOTERANK_ERROR_INVALID_RATING = 0x7F0, + BD_MAX_NUM_TAGS_EXCEEDED = 0x82A, + BD_TAGGED_COLLECTION_DOES_NOT_EXIST = 0x82B, + BD_EMPTY_TAG_ARRAY = 0x82C, + BD_INVALID_QUERY_ID = 0x834, + BD_NO_ENTRY_TO_UPDATE = 0x835, + BD_SESSION_INVITE_EXISTS = 0x836, + BD_INVALID_SESSION_ID = 0x837, + BD_ATTACHMENT_TOO_LARGE = 0x838, + BD_INVALID_GROUP_ID = 0xAF0, + BD_MAIL_INVALID_MAIL_ID_ERROR = 0xB55, + BD_UCD_SERVICE_ERROR = 0xC80, + BD_UCD_SERVICE_DISABLED = 0xC81, + BD_UCD_UNINTIALIZED_ERROR = 0xC82, + BD_UCD_ACCOUNT_ALREADY_REGISTERED = 0xC83, + BD_UCD_ACCOUNT_NOT_REGISTERED = 0xC84, + BD_UCD_AUTH_ATTEMPT_FAILED = 0xC85, + BD_UCD_ACCOUNT_LINKING_ERROR = 0xC86, + BD_UCD_ENCRYPTION_ERROR = 0xC87, + BD_UCD_ACCOUNT_DATA_INVALID = 0xC88, + BD_UCD_ACCOUNT_DATA_INVALID_FIRSTNAME = 0xC89, + BD_UCD_ACCOUNT_DATA_INVALID_LASTNAME = 0xC8A, + BD_UCD_ACCOUNT_DATA_INVALID_DOB = 0xC8B, + BD_UCD_ACCOUNT_DATA_INVALID_EMAIL = 0xC8C, + BD_UCD_ACCOUNT_DATA_INVALID_COUNTRY = 0xC8D, + BD_UCD_ACCOUNT_DATA_INVALID_POSTCODE = 0xC8E, + BD_UCD_ACCOUNT_DATA_INVALID_PASSWORD = 0xC8F, + BD_UCD_ACCOUNT_NAME_ALREADY_RESISTERED = 0xC94, + BD_UCD_ACCOUNT_EMAIL_ALREADY_RESISTERED = 0xC95, + BD_UCD_GUEST_ACCOUNT_AUTH_CONFLICT = 0xC96, + BD_TWITCH_SERVICE_ERROR = 0xC1D, + BD_TWITCH_ACCOUNT_ALREADY_LINKED = 0xC1E, + BD_TWITCH_NO_LINKED_ACCOUNT = 0xC1F, + BD_YOUTUBE_SERVICE_ERROR = 0xCE5, + BD_YOUTUBE_SERVICE_COMMUNICATION_ERROR = 0xCE6, + BD_YOUTUBE_USER_DENIED_AUTHORIZATION = 0xCE7, + BD_YOUTUBE_AUTH_MAX_TIME_EXCEEDED = 0xCE8, + BD_YOUTUBE_USER_UNAUTHORIZED = 0xCE9, + BD_YOUTUBE_UPLOAD_MAX_TIME_EXCEEDED = 0xCEA, + BD_YOUTUBE_DUPLICATE_UPLOAD = 0xCEB, + BD_YOUTUBE_FAILED_UPLOAD = 0xCEC, + BD_YOUTUBE_ACCOUNT_ALREADY_REGISTERED = 0xCED, + BD_YOUTUBE_ACCOUNT_NOT_REGISTERED = 0xCEE, + BD_YOUTUBE_CONTENT_SERVER_ERROR = 0xCEF, + BD_YOUTUBE_UPLOAD_DOES_NOT_EXIST = 0xCF0, + BD_YOUTUBE_NO_LINKED_ACCOUNT = 0xCF1, + BD_YOUTUBE_DEVELOPER_TAGS_INVALID = 0xCF2, + BD_TWITTER_AUTH_ATTEMPT_FAILED = 0xDAD, + BD_TWITTER_AUTH_TOKEN_INVALID = 0xDAE, + BD_TWITTER_UPDATE_LIMIT_REACHED = 0xDAF, + BD_TWITTER_UNAVAILABLE = 0xDB0, + BD_TWITTER_ERROR = 0xDB1, + BD_TWITTER_TIMED_OUT = 0xDB2, + BD_TWITTER_DISABLED_FOR_USER = 0xDB3, + BD_TWITTER_ACCOUNT_AMBIGUOUS = 0xDB4, + BD_TWITTER_MAXIMUM_ACCOUNTS_REACHED = 0xDB5, + BD_TWITTER_ACCOUNT_NOT_REGISTERED = 0xDB6, + BD_TWITTER_DUPLICATE_STATUS = 0xDB7, + BD_TWITTER_ACCOUNT_ALREADY_REGISTERED = 0xE1C, + BD_FACEBOOK_AUTH_ATTEMPT_FAILED = 0xE11, + BD_FACEBOOK_AUTH_TOKEN_INVALID = 0xE12, + BD_FACEBOOK_PHOTO_DOES_NOT_EXIST = 0xE13, + BD_FACEBOOK_PHOTO_INVALID = 0xE14, + BD_FACEBOOK_PHOTO_ALBUM_FULL = 0xE15, + BD_FACEBOOK_UNAVAILABLE = 0xE16, + BD_FACEBOOK_ERROR = 0xE17, + BD_FACEBOOK_TIMED_OUT = 0xE18, + BD_FACEBOOK_DISABLED_FOR_USER = 0xE19, + BD_FACEBOOK_ACCOUNT_AMBIGUOUS = 0xE1A, + BD_FACEBOOK_MAXIMUM_ACCOUNTS_REACHED = 0xE1B, + BD_FACEBOOK_INVALID_NUM_PICTURES_REQUESTED = 0xE1C, + BD_FACEBOOK_VIDEO_DOES_NOT_EXIST = 0xE1D, + BD_FACEBOOK_ACCOUNT_ALREADY_REGISTERED = 0xE1E, + BD_APNS_INVALID_PAYLOAD = 0xE74, + BD_APNS_INVALID_TOKEN_LENGTH_ERROR = 0xE76, + BD_MAX_CONSOLEID_LENGTH_EXCEEDED = 0xEE1, + BD_MAX_WHITELIST_LENGTH_EXCEEDED = 0xEE2, + BD_USERGROUP_NAME_ALREADY_EXISTS = 0x1770, + BD_INVALID_USERGROUP_ID = 0x1771, + BD_USER_ALREADY_IN_USERGROUP = 0x1772, + BD_USER_NOT_IN_USERGROUP = 0x1773, + BD_INVALID_USERGROUP_MEMBER_TYPE = 0x1774, + BD_TOO_MANY_MEMBERS_REQUESTED = 0x1775, + BD_USERGROUP_NAME_TOO_SHORT = 0x1776, + BD_RICH_PRESENCE_DATA_TOO_LARGE = 0x1A90, + BD_RICH_PRESENCE_TOO_MANY_USERS = 0x1A91, + BD_PRESENCE_DATA_TOO_LARGE = 0x283C, + BD_PRESENCE_TOO_MANY_USERS = 0x283D, + BD_USER_LOGGED_IN_OTHER_TITLE = 0x283E, + BD_USER_NOT_LOGGED_IN = 0x283F, + BD_SUBSCRIPTION_TOO_MANY_USERS = 0x1B58, + BD_SUBSCRIPTION_TICKET_PARSE_ERROR = 0x1B59, + BD_CODO_ID_INVALID_DATA = 0x1BBC, + BD_INVALID_MESSAGE_FORMAT = 0x1BBD, + BD_TLOG_TOO_MANY_MESSAGES = 0x1BBE, + BD_CODO_ID_NOT_IN_WHITELIST = 0x1BBF, + BD_TLOG_MESSAGE_TRANSFORMATION_ERROR = 0x1BC0, + BD_REWARDS_NOT_ENABLED = 0x1BC1, + BD_MARKETPLACE_ERROR = 0x1F40, + BD_MARKETPLACE_RESOURCE_NOT_FOUND = 0x1F41, + BD_MARKETPLACE_INVALID_CURRENCY = 0x1F42, + BD_MARKETPLACE_INVALID_PARAMETER = 0x1F43, + BD_MARKETPLACE_RESOURCE_CONFLICT = 0x1F44, + BD_MARKETPLACE_STORAGE_ERROR = 0x1F45, + BD_MARKETPLACE_INTEGRITY_ERROR = 0x1F46, + BD_MARKETPLACE_INSUFFICIENT_FUNDS_ERROR = 0x1F47, + BD_MARKETPLACE_MMP_SERVICE_ERROR = 0x1F48, + BD_MARKETPLACE_PRECONDITION_REQUIRED = 0x1F49, + BD_MARKETPLACE_ITEM_MULTIPLE_PURCHASE_ERROR = 0x1F4A, + BD_MARKETPLACE_MISSING_REQUIRED_ENTITLEMENT = 0x1F4B, + BD_MARKETPLACE_VALIDATION_ERROR = 0x1F4C, + BD_MARKETPLACE_TENCENT_PAYMENT_ERROR = 0x1F4D, + BD_MARKETPLACE_SKU_NOT_COUPON_ENABLED_ERROR = 0x1F4E, + BD_LEAGUE_INVALID_TEAM_SIZE = 0x1FA4, + BD_LEAGUE_INVALID_TEAM = 0x1FA5, + BD_LEAGUE_INVALID_SUBDIVISION = 0x1FA6, + BD_LEAGUE_INVALID_LEAGUE = 0x1FA7, + BD_LEAGUE_TOO_MANY_RESULTS_REQUESTED = 0x1FA8, + BD_LEAGUE_METADATA_TOO_LARGE = 0x1FA9, + BD_LEAGUE_TEAM_ICON_TOO_LARGE = 0x1FAA, + BD_LEAGUE_TEAM_NAME_TOO_LONG = 0x1FAB, + BD_LEAGUE_ARRAY_SIZE_MISMATCH = 0x1FAC, + BD_LEAGUE_SUBDIVISION_MISMATCH = 0x2008, + BD_LEAGUE_INVALID_WRITE_TYPE = 0x2009, + BD_LEAGUE_INVALID_STATS_DATA = 0x200A, + BD_LEAGUE_SUBDIVISION_UNRANKED = 0x200B, + BD_LEAGUE_CROSS_TEAM_STATS_WRITE_PREVENTED = 0x200C, + BD_LEAGUE_INVALID_STATS_SEASON = 0x200D, + BD_COMMERCE_ERROR = 0x206C, + BD_COMMERCE_RESOURCE_NOT_FOUND = 0x206D, + BD_COMMERCE_STORAGE_INVALID_PARAMETER = 0x206E, + BD_COMMERCE_APPLICATION_INVALID_PARAMETER = 0x206F, + BD_COMMERCE_RESOURCE_CONFLICT = 0x2070, + BD_COMMERCE_STORAGE_ERROR = 0x2071, + BD_COMMERCE_INTEGRITY_ERROR = 0x2072, + BD_COMMERCE_MMP_SERVICE_ERROR = 0x2073, + BD_COMMERCE_PERMISSION_DENIED = 0x2074, + BD_COMMERCE_INSUFFICIENT_FUNDS_ERROR = 0x2075, + BD_COMMERCE_UNKNOWN_CURRENCY = 0x2076, + BD_COMMERCE_INVALID_RECEIPT = 0x2077, + BD_COMMERCE_RECEIPT_USED = 0x2078, + BD_COMMERCE_TRANSACTION_ALREADY_APPLIED = 0x2079, + BD_COMMERCE_INVALID_CURRENCY_TYPE = 0x207A, + BD_CONNECTION_COUNTER_ERROR = 0x20D0, + BD_LINKED_ACCOUNTS_INVALID_CONTEXT = 0x2198, + BD_LINKED_ACCOUNTS_INVALID_PLATFORM = 0x2199, + BD_LINKED_ACCOUNTS_LINKED_ACCOUNTS_FETCH_ERROR = 0x219A, + BD_LINKED_ACCOUNTS_INVALID_ACCOUNT = 0x219B, + BD_GMSG_INVALID_CATEGORY_ID = 0x27D8, + BD_GMSG_CATEGORY_MEMBERSHIPS_LIMIT = 0x27D9, + BD_GMSG_NONMEMBER_POST_DISALLOWED = 0x27DA, + BD_GMSG_CATEGORY_DISALLOWS_CLIENT_TYPE = 0x27DB, + BD_GMSG_PAYLOAD_TOO_BIG = 0x27DC, + BD_GMSG_MEMBER_POST_DISALLOWED = 0x27DD, + BD_GMSG_OVERLOADED = 0x27DE, + BD_GMSG_USER_PERCATEGORY_POST_RATE_EXCEEDED = 0x27DF, + BD_GMSG_USER_GLOBAL_POST_RATE_EXCEEDED = 0x27E0, + BD_GMSG_GROUP_POST_RATE_EXCEEDED = 0x27E1, + BD_MAX_ERROR_CODE = 0x27E2, + }; } diff --git a/src/client/game/symbols.hpp b/src/client/game/symbols.hpp index 6d62398f..17b94cb6 100644 --- a/src/client/game/symbols.hpp +++ b/src/client/game/symbols.hpp @@ -1,5 +1,7 @@ #pragma once +#include "loader/component_loader.hpp" + #define WEAK __declspec(selectany) namespace game diff --git a/src/client/std_include.cpp b/src/client/std_include.cpp index 4b2b1809..a28d1f38 100644 --- a/src/client/std_include.cpp +++ b/src/client/std_include.cpp @@ -1,3 +1,26 @@ #include #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language=''\"") + +extern "C" +{ + int s_read_arc4random(void*, size_t) + { + return -1; + } + + int s_read_getrandom(void*, size_t) + { + return -1; + } + + int s_read_urandom(void*, size_t) + { + return -1; + } + + int s_read_ltm_rng(void*, size_t) + { + return -1; + } +} diff --git a/src/client/std_include.hpp b/src/client/std_include.hpp index 493109cc..70994304 100644 --- a/src/client/std_include.hpp +++ b/src/client/std_include.hpp @@ -82,6 +82,13 @@ #include #include +#define RAPIDJSON_NOEXCEPT +#define RAPIDJSON_ASSERT(cond) if(cond); else throw std::runtime_error("rapidjson assert fail"); + +#include +#include +#include + #pragma warning(pop) #pragma warning(disable: 4100) diff --git a/src/common/utils/cryptography.cpp b/src/common/utils/cryptography.cpp new file mode 100644 index 00000000..e6ce36a0 --- /dev/null +++ b/src/common/utils/cryptography.cpp @@ -0,0 +1,640 @@ +#include "string.hpp" +#include "cryptography.hpp" +#include "nt.hpp" +#include "finally.hpp" + +#undef max +using namespace std::string_literals; + +/// http://www.opensource.apple.com/source/CommonCrypto/CommonCrypto-55010/Source/libtomcrypt/doc/libTomCryptDoc.pdf + +namespace utils::cryptography +{ + namespace + { + struct __ + { + __() + { + ltc_mp = ltm_desc; + + register_cipher(&aes_desc); + register_cipher(&des3_desc); + + register_prng(&sprng_desc); + register_prng(&fortuna_desc); + register_prng(&yarrow_desc); + + register_hash(&sha1_desc); + register_hash(&sha256_desc); + register_hash(&sha512_desc); + } + } ___; + + [[maybe_unused]] const char* cs(const uint8_t* data) + { + return reinterpret_cast(data); + } + + [[maybe_unused]] char* cs(uint8_t* data) + { + return reinterpret_cast(data); + } + + [[maybe_unused]] const uint8_t* cs(const char* data) + { + return reinterpret_cast(data); + } + + [[maybe_unused]] uint8_t* cs(char* data) + { + return reinterpret_cast(data); + } + + [[maybe_unused]] unsigned long ul(const size_t value) + { + return static_cast(value); + } + + class prng + { + public: + prng(const ltc_prng_descriptor& descriptor, const bool autoseed = true) + : state_(std::make_unique()) + , descriptor_(descriptor) + { + this->id_ = register_prng(&descriptor); + if (this->id_ == -1) + { + throw std::runtime_error("PRNG "s + this->descriptor_.name + " could not be registered!"); + } + + if (autoseed) + { + this->auto_seed(); + } + else + { + this->descriptor_.start(this->state_.get()); + } + } + + ~prng() + { + this->descriptor_.done(this->state_.get()); + } + + prng_state* get_state() const + { + this->descriptor_.ready(this->state_.get()); + return this->state_.get(); + } + + int get_id() const + { + return this->id_; + } + + void add_entropy(const void* data, const size_t length) const + { + this->descriptor_.add_entropy(static_cast(data), ul(length), this->state_.get()); + } + + void read(void* data, const size_t length) const + { + this->descriptor_.read(static_cast(data), ul(length), this->get_state()); + } + + private: + int id_; + std::unique_ptr state_; + const ltc_prng_descriptor& descriptor_; + + void auto_seed() const + { + rng_make_prng(128, this->id_, this->state_.get(), nullptr); + + int i[4]; // uninitialized data + auto* i_ptr = &i; + this->add_entropy(reinterpret_cast(&i), sizeof(i)); + this->add_entropy(reinterpret_cast(&i_ptr), sizeof(i_ptr)); + + auto t = time(nullptr); + this->add_entropy(reinterpret_cast(&t), sizeof(t)); + } + }; + + const prng prng_(fortuna_desc); + } + + ecc::key::key() + { + ZeroMemory(&this->key_storage_, sizeof(this->key_storage_)); + } + + ecc::key::~key() + { + this->free(); + } + + ecc::key::key(key&& obj) noexcept + : key() + { + this->operator=(std::move(obj)); + } + + ecc::key::key(const key& obj) + : key() + { + this->operator=(obj); + } + + ecc::key& ecc::key::operator=(key&& obj) noexcept + { + if (this != &obj) + { + std::memmove(&this->key_storage_, &obj.key_storage_, sizeof(this->key_storage_)); + ZeroMemory(&obj.key_storage_, sizeof(obj.key_storage_)); + } + + return *this; + } + + ecc::key& ecc::key::operator=(const key& obj) + { + if (this != &obj && obj.is_valid()) + { + this->deserialize(obj.serialize(obj.key_storage_.type)); + } + + return *this; + } + + bool ecc::key::is_valid() const + { + return (!memory::is_set(&this->key_storage_, 0, sizeof(this->key_storage_))); + } + + ecc_key& ecc::key::get() + { + return this->key_storage_; + } + + const ecc_key& ecc::key::get() const + { + return this->key_storage_; + } + + std::string ecc::key::get_public_key() const + { + uint8_t buffer[512] = {0}; + unsigned long length = sizeof(buffer); + + if (ecc_ansi_x963_export(&this->key_storage_, buffer, &length) == CRYPT_OK) + { + return std::string(cs(buffer), length); + } + + return {}; + } + + void ecc::key::set(const std::string& pub_key_buffer) + { + this->free(); + + if (ecc_ansi_x963_import(cs(pub_key_buffer.data()), + ul(pub_key_buffer.size()), + &this->key_storage_) != CRYPT_OK) + { + ZeroMemory(&this->key_storage_, sizeof(this->key_storage_)); + } + } + + void ecc::key::deserialize(const std::string& key) + { + this->free(); + + if (ecc_import(cs(key.data()), ul(key.size()), + &this->key_storage_) != CRYPT_OK + ) + { + ZeroMemory(&this->key_storage_, sizeof(this->key_storage_)); + } + } + + std::string ecc::key::serialize(const int type) const + { + uint8_t buffer[4096] = {0}; + unsigned long length = sizeof(buffer); + + if (ecc_export(buffer, &length, type, &this->key_storage_) == CRYPT_OK) + { + return std::string(cs(buffer), length); + } + + return ""; + } + + void ecc::key::free() + { + if (this->is_valid()) + { + ecc_free(&this->key_storage_); + } + + ZeroMemory(&this->key_storage_, sizeof(this->key_storage_)); + } + + bool ecc::key::operator==(key& key) const + { + return (this->is_valid() && key.is_valid() && this->serialize(PK_PUBLIC) == key.serialize(PK_PUBLIC)); + } + + uint64_t ecc::key::get_hash() const + { + const auto hash = sha1::compute(this->get_public_key()); + if (hash.size() >= 8) + { + return *reinterpret_cast(hash.data()); + } + + return 0; + } + + ecc::key ecc::generate_key(const int bits) + { + key key; + ecc_make_key(prng_.get_state(), prng_.get_id(), bits / 8, &key.get()); + + return key; + } + + ecc::key ecc::generate_key(const int bits, const std::string& entropy) + { + key key{}; + const prng yarrow(yarrow_desc, false); + yarrow.add_entropy(entropy.data(), entropy.size()); + + ecc_make_key(yarrow.get_state(), yarrow.get_id(), bits / 8, &key.get()); + + return key; + } + + std::string ecc::sign_message(const key& key, const std::string& message) + { + if (!key.is_valid()) return ""; + + uint8_t buffer[512]; + unsigned long length = sizeof(buffer); + + ecc_sign_hash(cs(message.data()), ul(message.size()), buffer, &length, prng_.get_state(), prng_.get_id(), + &key.get()); + + return std::string(cs(buffer), length); + } + + bool ecc::verify_message(const key& key, const std::string& message, const std::string& signature) + { + if (!key.is_valid()) return false; + + auto result = 0; + return (ecc_verify_hash(cs(signature.data()), + ul(signature.size()), + cs(message.data()), + ul(message.size()), &result, + &key.get()) == CRYPT_OK && result != 0); + } + + bool ecc::encrypt(const key& key, std::string& data) + { + std::string out_data{}; + out_data.resize(std::max(ul(data.size() * 3), ul(0x100))); + + auto out_len = ul(out_data.size()); + auto crypt = [&]() + { + return ecc_encrypt_key(cs(data.data()), ul(data.size()), cs(out_data.data()), &out_len, + prng_.get_state(), prng_.get_id(), find_hash("sha512"), &key.get()); + }; + + auto res = crypt(); + + if (res == CRYPT_BUFFER_OVERFLOW) + { + out_data.resize(out_len); + res = crypt(); + } + + if (res != CRYPT_OK) + { + return false; + } + + out_data.resize(out_len); + data = std::move(out_data); + return true; + } + + bool ecc::decrypt(const key& key, std::string& data) + { + std::string out_data{}; + out_data.resize(std::max(ul(data.size() * 3), ul(0x100))); + + auto out_len = ul(out_data.size()); + auto crypt = [&]() + { + return ecc_decrypt_key(cs(data.data()), ul(data.size()), cs(out_data.data()), &out_len, &key.get()); + }; + + auto res = crypt(); + + if (res == CRYPT_BUFFER_OVERFLOW) + { + out_data.resize(out_len); + res = crypt(); + } + + if (res != CRYPT_OK) + { + return false; + } + + out_data.resize(out_len); + data = std::move(out_data); + return true; + } + + std::string rsa::encrypt(const std::string& data, const std::string& hash, const std::string& key) + { + rsa_key new_key; + rsa_import(cs(key.data()), ul(key.size()), &new_key); + const auto _ = finally([&]() + { + rsa_free(&new_key); + }); + + + std::string out_data{}; + out_data.resize(std::max(ul(data.size() * 3), ul(0x100))); + + auto out_len = ul(out_data.size()); + auto crypt = [&]() + { + return rsa_encrypt_key(cs(data.data()), ul(data.size()), cs(out_data.data()), &out_len, cs(hash.data()), + ul(hash.size()), prng_.get_state(), prng_.get_id(), find_hash("sha512"), &new_key); + }; + + auto res = crypt(); + + if (res == CRYPT_BUFFER_OVERFLOW) + { + out_data.resize(out_len); + res = crypt(); + } + + if (res == CRYPT_OK) + { + out_data.resize(out_len); + return out_data; + } + + return {}; + } + + std::string des3::encrypt(const std::string& data, const std::string& iv, const std::string& key) + { + std::string enc_data; + enc_data.resize(data.size()); + + symmetric_CBC cbc; + const auto des3 = find_cipher("3des"); + + cbc_start(des3, cs(iv.data()), cs(key.data()), static_cast(key.size()), 0, &cbc); + cbc_encrypt(cs(data.data()), cs(enc_data.data()), ul(data.size()), &cbc); + cbc_done(&cbc); + + return enc_data; + } + + std::string des3::decrypt(const std::string& data, const std::string& iv, const std::string& key) + { + std::string dec_data; + dec_data.resize(data.size()); + + symmetric_CBC cbc; + const auto des3 = find_cipher("3des"); + + cbc_start(des3, cs(iv.data()), cs(key.data()), static_cast(key.size()), 0, &cbc); + cbc_decrypt(cs(data.data()), cs(dec_data.data()), ul(data.size()), &cbc); + cbc_done(&cbc); + + return dec_data; + } + + std::string tiger::compute(const std::string& data, const bool hex) + { + return compute(cs(data.data()), data.size(), hex); + } + + std::string tiger::compute(const uint8_t* data, const size_t length, const bool hex) + { + uint8_t buffer[24] = {0}; + + hash_state state; + tiger_init(&state); + tiger_process(&state, data, ul(length)); + tiger_done(&state, buffer); + + std::string hash(cs(buffer), sizeof(buffer)); + if (!hex) return hash; + + return string::dump_hex(hash, ""); + } + + std::string aes::encrypt(const std::string& data, const std::string& iv, const std::string& key) + { + std::string enc_data; + enc_data.resize(data.size()); + + symmetric_CBC cbc; + const auto aes = find_cipher("aes"); + + cbc_start(aes, cs(iv.data()), cs(key.data()), + static_cast(key.size()), 0, &cbc); + cbc_encrypt(cs(data.data()), + cs(enc_data.data()), + ul(data.size()), &cbc); + cbc_done(&cbc); + + return enc_data; + } + + std::string aes::decrypt(const std::string& data, const std::string& iv, const std::string& key) + { + std::string dec_data; + dec_data.resize(data.size()); + + symmetric_CBC cbc; + const auto aes = find_cipher("aes"); + + cbc_start(aes, cs(iv.data()), cs(key.data()), + static_cast(key.size()), 0, &cbc); + cbc_decrypt(cs(data.data()), + cs(dec_data.data()), + ul(data.size()), &cbc); + cbc_done(&cbc); + + return dec_data; + } + + std::string hmac_sha1::compute(const std::string& data, const std::string& key) + { + std::string buffer; + buffer.resize(20); + + hmac_state state; + hmac_init(&state, find_hash("sha1"), cs(key.data()), ul(key.size())); + hmac_process(&state, cs(data.data()), static_cast(data.size())); + + auto out_len = ul(buffer.size()); + hmac_done(&state, cs(buffer.data()), &out_len); + + buffer.resize(out_len); + return buffer; + } + + std::string sha1::compute(const std::string& data, const bool hex) + { + return compute(cs(data.data()), data.size(), hex); + } + + std::string sha1::compute(const uint8_t* data, const size_t length, const bool hex) + { + uint8_t buffer[20] = {0}; + + hash_state state; + sha1_init(&state); + sha1_process(&state, data, ul(length)); + sha1_done(&state, buffer); + + std::string hash(cs(buffer), sizeof(buffer)); + if (!hex) return hash; + + return string::dump_hex(hash, ""); + } + + std::string sha256::compute(const std::string& data, const bool hex) + { + return compute(cs(data.data()), data.size(), hex); + } + + std::string sha256::compute(const uint8_t* data, const size_t length, const bool hex) + { + uint8_t buffer[32] = {0}; + + hash_state state; + sha256_init(&state); + sha256_process(&state, data, ul(length)); + sha256_done(&state, buffer); + + std::string hash(cs(buffer), sizeof(buffer)); + if (!hex) return hash; + + return string::dump_hex(hash, ""); + } + + std::string sha512::compute(const std::string& data, const bool hex) + { + return compute(cs(data.data()), data.size(), hex); + } + + std::string sha512::compute(const uint8_t* data, const size_t length, const bool hex) + { + uint8_t buffer[64] = {0}; + + hash_state state; + sha512_init(&state); + sha512_process(&state, data, ul(length)); + sha512_done(&state, buffer); + + std::string hash(cs(buffer), sizeof(buffer)); + if (!hex) return hash; + + return string::dump_hex(hash, ""); + } + + std::string base64::encode(const uint8_t* data, const size_t len) + { + std::string result; + result.resize((len + 2) * 2); + + auto out_len = ul(result.size()); + if (base64_encode(data, ul(len), result.data(), &out_len) != CRYPT_OK) + { + return {}; + } + + result.resize(out_len); + return result; + } + + std::string base64::encode(const std::string& data) + { + return base64::encode(cs(data.data()), static_cast(data.size())); + } + + std::string base64::decode(const std::string& data) + { + std::string result; + result.resize((data.size() + 2) * 2); + + auto out_len = ul(result.size()); + if (base64_decode(data.data(), ul(data.size()), cs(result.data()), &out_len) != CRYPT_OK) + { + return {}; + } + + result.resize(out_len); + return result; + } + + unsigned int jenkins_one_at_a_time::compute(const std::string& data) + { + return compute(data.data(), data.size()); + } + + unsigned int jenkins_one_at_a_time::compute(const char* key, const size_t len) + { + unsigned int hash, i; + for (hash = i = 0; i < len; ++i) + { + hash += key[i]; + hash += (hash << 10); + hash ^= (hash >> 6); + } + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + return hash; + } + + uint32_t random::get_integer() + { + uint32_t result; + random::get_data(&result, sizeof(result)); + return result; + } + + std::string random::get_challenge() + { + std::string result; + result.resize(sizeof(uint32_t)); + random::get_data(result.data(), result.size()); + return string::dump_hex(result, ""); + } + + void random::get_data(void* data, const size_t size) + { + prng_.read(data, size); + } +} diff --git a/src/common/utils/cryptography.hpp b/src/common/utils/cryptography.hpp new file mode 100644 index 00000000..9538c5eb --- /dev/null +++ b/src/common/utils/cryptography.hpp @@ -0,0 +1,118 @@ +#pragma once + +#include +#include + +namespace utils::cryptography +{ + namespace ecc + { + class key final + { + public: + key(); + ~key(); + + key(key&& obj) noexcept; + key(const key& obj); + key& operator=(key&& obj) noexcept; + key& operator=(const key& obj); + + bool is_valid() const; + + ecc_key& get(); + const ecc_key& get() const; + + std::string get_public_key() const; + + void set(const std::string& pub_key_buffer); + + void deserialize(const std::string& key); + + std::string serialize(int type = PK_PRIVATE) const; + + void free(); + + bool operator==(key& key) const; + + uint64_t get_hash() const; + + private: + ecc_key key_storage_{}; + }; + + key generate_key(int bits); + key generate_key(int bits, const std::string& entropy); + std::string sign_message(const key& key, const std::string& message); + bool verify_message(const key& key, const std::string& message, const std::string& signature); + + bool encrypt(const key& key, std::string& data); + bool decrypt(const key& key, std::string& data); + } + + namespace rsa + { + std::string encrypt(const std::string& data, const std::string& hash, const std::string& key); + } + + namespace des3 + { + std::string encrypt(const std::string& data, const std::string& iv, const std::string& key); + std::string decrypt(const std::string& data, const std::string& iv, const std::string& key); + } + + namespace tiger + { + std::string compute(const std::string& data, bool hex = false); + std::string compute(const uint8_t* data, size_t length, bool hex = false); + } + + namespace aes + { + std::string encrypt(const std::string& data, const std::string& iv, const std::string& key); + std::string decrypt(const std::string& data, const std::string& iv, const std::string& key); + } + + namespace hmac_sha1 + { + std::string compute(const std::string& data, const std::string& key); + } + + namespace sha1 + { + std::string compute(const std::string& data, bool hex = false); + std::string compute(const uint8_t* data, size_t length, bool hex = false); + } + + namespace sha256 + { + std::string compute(const std::string& data, bool hex = false); + std::string compute(const uint8_t* data, size_t length, bool hex = false); + } + + namespace sha512 + { + std::string compute(const std::string& data, bool hex = false); + std::string compute(const uint8_t* data, size_t length, bool hex = false); + } + + namespace base64 + { + std::string encode(const uint8_t* data, size_t len); + std::string encode(const std::string& data); + std::string decode(const std::string& data); + } + + namespace jenkins_one_at_a_time + { + unsigned int compute(const std::string& data); + unsigned int compute(const char* key, size_t len); + }; + + namespace random + { + uint32_t get_integer(); + std::string get_challenge(); + void get_data(void* data, size_t size); + } +}