This commit is contained in:
2024-01-24 10:45:25 +01:00
commit bcdbe48523
267 changed files with 39510 additions and 0 deletions

View File

@@ -0,0 +1,182 @@
#include <std_include.hpp>
#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 size_t bytes, const char* data)
{
return this->write_bytes(bytes, reinterpret_cast<const unsigned char*>(data));
}
bool bit_buffer::write_bytes(const size_t bytes, const unsigned char* data)
{
return this->write(static_cast<unsigned>(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 = reinterpret_cast<unsigned char*>(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<char*>(this->buffer_.data());
const auto* input_bytes = reinterpret_cast<const unsigned char*>(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_;
}
}

View File

@@ -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(size_t bytes, const char* data);
bool write_bytes(size_t 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;
};
}

View File

@@ -0,0 +1,313 @@
#include <std_include.hpp>
#include "byte_buffer.hpp"
namespace demonware
{
bool byte_buffer::read_byte(unsigned char* output)
{
if (!this->read_data_type(3)) return false;
return this->read(1, output);
}
bool byte_buffer::read_bool(bool* output)
{
if (!this->read_data_type(1)) 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<char*>(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<char*>(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<char*>(this->buffer_.data()) + this->current_byte_;
*length = static_cast<int>(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);
if (type != expected)
{
//throw std::runtime_error("Data type mismatch!");
}
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_byte(char data)
{
this->write_data_type(3);
return this->write(1, &data);
}
bool byte_buffer::write_bool(bool data)
{
this->write_data_type(1);
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<int>(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_byte(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 size_t 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 size_t bytes, const void* data)
{
this->buffer_.append(static_cast<const char*>(data), bytes);
this->current_byte_ += bytes;
return true;
}
bool byte_buffer::write(const std::string& data)
{
return this->write(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_;
}
}

View File

@@ -0,0 +1,69 @@
#pragma once
namespace demonware
{
class byte_buffer final
{
public:
byte_buffer() = default;
explicit byte_buffer(std::string buffer) : buffer_(std::move(buffer))
{
}
bool read_byte(unsigned char* output);
bool read_bool(bool* 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_byte(char data);
bool write_bool(bool 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(size_t bytes, void* output);
bool write(size_t 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;
};
}

View File

@@ -0,0 +1,449 @@
#pragma once
#include "i_server.hpp"
#include "game/structs.hpp"
namespace demonware
{
class bdFileData final : public i_serializable
{
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 i_serializable
{
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 bdGroupCount final : public i_serializable
{
public:
uint32_t group_id;
uint32_t group_count;
bdGroupCount()
{
this->group_id = 0;
this->group_count = 0;
}
void serialize(byte_buffer* buffer) override
{
buffer->write_uint32(this->group_id);
buffer->write_uint32(this->group_count);
}
void deserialize(byte_buffer* buffer) override
{
buffer->read_uint32(&this->group_id);
buffer->read_uint32(&this->group_count);
}
};
class bdTimeStamp final : public i_serializable
{
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 i_serializable
{
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);
}
};
class bdSessionID final : public i_serializable
{
public:
uint64_t session_id;
void serialize(byte_buffer* buffer) override
{
buffer->write_blob(LPSTR(&this->session_id), sizeof this->session_id);
}
void deserialize(byte_buffer* buffer) override
{
int size{};
char* data{};
buffer->read_blob(&data, &size);
if (data && uint32_t(size) >= sizeof(this->session_id))
{
this->session_id = *reinterpret_cast<uint64_t*>(data);
}
}
};
class bdMatchmakingInfo : public i_serializable
{
public:
bdSessionID session_id{};
std::string host_addr{};
uint32_t game_type{};
uint32_t max_players{};
uint32_t num_players{};
bool symmetric = false;
void serialize(byte_buffer* buffer) override
{
buffer->write_blob(this->host_addr);
this->session_id.serialize(buffer);
buffer->write_uint32(this->game_type);
buffer->write_uint32(this->max_players);
buffer->write_uint32(this->num_players);
}
void deserialize(byte_buffer* buffer) override
{
buffer->read_blob(&this->host_addr);
if (this->symmetric) this->session_id.deserialize(buffer);
buffer->read_uint32(&this->game_type);
buffer->read_uint32(&this->max_players);
if (this->symmetric) buffer->read_uint32(&this->num_players);
}
};
class MatchMakingInfo final : public bdMatchmakingInfo
{
public:
int32_t playlist_number{};
int32_t playlist_version{};
int32_t netcode_version{};
int32_t map_packs{};
int32_t slots_needed_on_team{};
int32_t skill{};
uint32_t country_code{};
uint32_t asn{};
float latitude{};
float longitude{};
int32_t max_reserved_slots{};
int32_t used_reserved_slots{};
std::string game_security_key{}; // 16 bytes.
std::string platform_session_id{}; // 16 bytes.
uint32_t data_centres{};
uint32_t coop_state{};
void serialize(byte_buffer* buffer) override
{
bdMatchmakingInfo::serialize(buffer);
buffer->write_int32(this->playlist_number);
buffer->write_int32(this->playlist_version);
buffer->write_int32(this->netcode_version);
buffer->write_int32(this->map_packs);
buffer->write_int32(this->slots_needed_on_team);
buffer->write_int32(this->skill);
buffer->write_uint32(this->country_code);
buffer->write_uint32(this->asn);
buffer->write_float(this->latitude);
buffer->write_float(this->longitude);
buffer->write_int32(this->max_reserved_slots);
buffer->write_int32(this->used_reserved_slots);
buffer->write_blob(this->game_security_key);
buffer->write_blob(this->platform_session_id);
buffer->write_uint32(this->data_centres);
buffer->write_uint32(this->coop_state);
}
void deserialize(byte_buffer* buffer) override
{
bdMatchmakingInfo::deserialize(buffer);
buffer->read_int32(&this->playlist_number);
buffer->read_int32(&this->playlist_version);
buffer->read_int32(&this->netcode_version);
buffer->read_int32(&this->map_packs);
buffer->read_int32(&this->slots_needed_on_team);
buffer->read_int32(&this->skill);
buffer->read_uint32(&this->country_code);
buffer->read_uint32(&this->asn);
buffer->read_float(&this->latitude);
buffer->read_float(&this->longitude);
buffer->read_int32(&this->max_reserved_slots);
buffer->read_int32(&this->used_reserved_slots);
buffer->read_blob(&this->game_security_key);
buffer->read_blob(&this->platform_session_id);
buffer->read_uint32(&this->data_centres);
buffer->read_uint32(&this->coop_state);
}
};
class bdPerformanceValue final : public i_serializable
{
public:
uint64_t user_id;
int64_t performance;
void serialize(byte_buffer* buffer) override
{
buffer->write_uint64(this->user_id);
buffer->write_int64(this->performance);
}
void deserialize(byte_buffer* buffer) override
{
buffer->read_uint64(&this->user_id);
buffer->read_int64(&this->performance);
}
};
struct bdSockAddr final
{
bdSockAddr() : in_un(), m_family(AF_INET)
{
}
union
{
struct
{
char m_b1;
char m_b2;
char m_b3;
char m_b4;
} m_caddr;
unsigned int m_iaddr;
struct
{
unsigned __int16 m_w1;
unsigned __int16 m_w2;
unsigned __int16 m_w3;
unsigned __int16 m_w4;
unsigned __int16 m_w5;
unsigned __int16 m_w6;
unsigned __int16 m_w7;
unsigned __int16 m_w8;
} m_caddr6;
char m_iaddr6[16];
char m_sockaddr_storage[128];
} in_un;
unsigned __int16 m_family;
};
struct bdInetAddr final : i_serializable
{
bdSockAddr m_addr;
bool is_valid() const
{
return (this->m_addr.m_family == AF_INET /*|| this->m_addr.m_family == AF_INET6*/);
}
void serialize(byte_buffer* buffer) override
{
const auto data_types = buffer->is_using_data_types();
buffer->set_use_data_types(false);
if (this->m_addr.m_family == AF_INET)
{
buffer->write(4, &this->m_addr.in_un.m_caddr);
}
buffer->set_use_data_types(data_types);
}
void deserialize(byte_buffer* buffer) override
{
const auto data_types = buffer->is_using_data_types();
buffer->set_use_data_types(false);
if (this->m_addr.m_family == AF_INET)
{
buffer->read(4, &this->m_addr.in_un.m_caddr);
}
buffer->set_use_data_types(data_types);
}
};
struct bdAddr final : i_serializable
{
bdInetAddr m_address;
unsigned __int16 m_port{};
void serialize(byte_buffer* buffer) override
{
const bool data_types = buffer->is_using_data_types();
buffer->set_use_data_types(false);
this->m_address.serialize(buffer);
buffer->write_uint16(this->m_port);
buffer->set_use_data_types(data_types);
}
void deserialize(byte_buffer* buffer) override
{
const auto data_types = buffer->is_using_data_types();
buffer->set_use_data_types(false);
this->m_address.deserialize(buffer);
buffer->read_uint16(&this->m_port);
buffer->set_use_data_types(data_types);
}
};
struct bdCommonAddr : i_serializable
{
bdAddr m_local_addrs[5];
bdAddr m_public_addr;
game::bdNATType m_nat_type;
unsigned int m_hash;
bool m_is_loopback;
void serialize(byte_buffer* buffer) override
{
const auto data_types = buffer->is_using_data_types();
buffer->set_use_data_types(false);
auto valid = true;
for (uint32_t i = 0; i < 5 && i < ARRAYSIZE(this->m_local_addrs) && valid; ++i)
{
this->m_local_addrs[i].serialize(buffer);
valid = this->m_local_addrs[i].m_address.is_valid();
}
if (valid)
{
this->m_public_addr.serialize(buffer);
buffer->write_byte(this->m_nat_type);
}
buffer->set_use_data_types(data_types);
}
void deserialize(byte_buffer* buffer) override
{
const auto data_types = buffer->is_using_data_types();
buffer->set_use_data_types(false);
auto valid = true;
for (uint32_t i = 0; i < ARRAYSIZE(this->m_local_addrs) && valid; ++i)
{
bdAddr addr;
addr.deserialize(buffer);
this->m_local_addrs[i] = addr;
valid = this->m_local_addrs[i].m_address.is_valid();
}
if (valid)
{
this->m_public_addr.deserialize(buffer);
buffer->read_byte(reinterpret_cast<uint8_t*>(&this->m_nat_type));
}
buffer->set_use_data_types(data_types);
}
};
}

View File

@@ -0,0 +1,201 @@
#pragma once
#include "bit_buffer.hpp"
#include "byte_buffer.hpp"
namespace demonware
{
class reply
{
public:
virtual ~reply() = default;
virtual std::string get_data() = 0;
};
class raw_reply : public reply
{
public:
raw_reply() = default;
explicit raw_reply(std::string data) : buffer_(std::move(data))
{
}
virtual std::string get_data() override
{
return this->buffer_;
}
protected:
std::string buffer_;
};
class typed_reply : public raw_reply
{
public:
typed_reply(uint8_t _type) : type_(_type)
{
}
protected:
uint8_t get_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());
}
virtual std::string get_data() override;
};
class unencrypted_reply final : public typed_reply
{
public:
unencrypted_reply(uint8_t _type, bit_buffer* bbuffer) : typed_reply(_type)
{
this->buffer_.append(bbuffer->get_buffer());
}
unencrypted_reply(uint8_t _type, byte_buffer* bbuffer) : typed_reply(_type)
{
this->buffer_.append(bbuffer->get_buffer());
}
virtual std::string get_data() override;
};
class remote_reply;
class service_reply;
class i_server
{
public:
virtual ~i_server() = default;
virtual int send(const char* buf, int len) = 0;
virtual int recv(char* buf, int len) = 0;
virtual void send_reply(reply* reply) = 0;
virtual std::shared_ptr<remote_reply> create_message(uint8_t type)
{
auto reply = std::make_shared<remote_reply>(this, type);
return reply;
}
virtual std::shared_ptr<service_reply> create_reply(uint8_t type,
uint32_t error = 0 /*Game::bdLobbyErrorCode::BD_NO_ERROR*/)
{
auto reply = std::make_shared<service_reply>(this, type, error);
return reply;
}
};
class remote_reply final
{
public:
remote_reply(i_server* server, uint8_t _type) : type_(_type), server_(server)
{
}
template <typename BufferType>
void send(BufferType* buffer, const bool encrypted)
{
std::unique_ptr<typed_reply> reply;
if (encrypted) reply = std::make_unique<encrypted_reply>(this->type_, buffer);
else reply = std::make_unique<unencrypted_reply>(this->type_, buffer);
this->server_->send_reply(reply.get());
}
uint8_t get_type() const { return this->type_; }
private:
uint8_t type_;
i_server* server_;
};
class i_serializable
{
public:
virtual ~i_serializable() = default;
virtual void serialize(byte_buffer* /*buffer*/)
{
}
virtual void deserialize(byte_buffer* /*buffer*/)
{
}
};
class service_reply final
{
public:
service_reply(i_server* _server, uint8_t _type, uint32_t _error) : type_(_type), error_(_error),
reply_(_server, 1)
{
}
uint64_t send()
{
static uint64_t id = 0x8000000000000001;
const auto transaction_id = ++id;
byte_buffer buffer;
buffer.write_uint64(transaction_id);
buffer.write_uint32(this->error_);
buffer.write_byte(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<i_serializable>& object)
{
this->objects_.push_back(object);
}
void add(i_serializable* object)
{
this->add(std::shared_ptr<i_serializable>(object));
}
private:
uint8_t type_;
uint32_t error_;
remote_reply reply_;
std::vector<std::shared_ptr<i_serializable>> objects_;
};
}

View File

@@ -0,0 +1,82 @@
#pragma once
#include "i_server.hpp"
namespace demonware
{
class i_service
{
public:
virtual ~i_service() = default;
i_service() = default;
// Copying or moving a service object won't work
// as the callbacks are bound to the initial object pointer
// Therefore, you should never declare copy/move
// constructors when inheriting from IService!
i_service(i_service&&) = delete;
i_service(const i_service&) = delete;
i_service& operator=(const i_service&) = delete;
typedef std::function<void(i_server*, byte_buffer*)> callback;
virtual uint16_t getType() = 0;
virtual void call_service(i_server* server, const std::string& data)
{
std::lock_guard _(this->mutex_);
byte_buffer buffer(data);
buffer.read_byte(&this->sub_type_);
#ifdef DEBUG
printf("DW: Handling subservice of type %d\n", this->sub_type_);
#endif
const auto callback = this->callbacks_.find(this->sub_type_);
if (callback != this->callbacks_.end())
{
callback->second(server, &buffer);
}
else
{
#ifdef DEBUG
printf("DW: Missing subservice %d for type %d\n", this->sub_type_, this->getType());
#endif
}
}
protected:
std::map<uint8_t, callback> callbacks_{};
template <typename Class, typename T, typename... Args>
void register_service(const uint8_t type, T (Class::*callback)(Args ...) const)
{
this->callbacks_[type] = [this, callback](Args ... args) -> T
{
return (reinterpret_cast<Class*>(this)->*callback)(args...);
};
}
template <typename Class, typename T, typename... Args>
void register_service(const uint8_t type, T (Class::*callback)(Args ...))
{
this->callbacks_[type] = [this, callback](Args ... args) -> T
{
return (reinterpret_cast<Class*>(this)->*callback)(args...);
};
}
uint8_t get_sub_type() const { return this->sub_type_; }
private:
std::mutex mutex_;
uint8_t sub_type_{};
};
template <uint16_t Type>
class i_generic_service : public i_service
{
public:
uint16_t getType() override { return Type; }
};
}

View File

@@ -0,0 +1,206 @@
#include <std_include.hpp>
#include "component/demonware.hpp"
#include "game/demonware/service_server.hpp"
#include <utils/cryptography.hpp>
namespace demonware
{
std::string unencrypted_reply::get_data()
{
byte_buffer result;
result.set_use_data_types(false);
result.write_int32(static_cast<int>(this->buffer_.size()) + 2);
result.write_bool(false);
result.write_byte(this->get_type());
result.write(this->buffer_);
return result.get_buffer();
}
std::string encrypted_reply::get_data()
{
byte_buffer result;
result.set_use_data_types(false);
byte_buffer enc_buffer;
enc_buffer.set_use_data_types(false);
enc_buffer.write_int32(0xDEADBEEF);
enc_buffer.write_byte(this->get_type());
enc_buffer.write(this->buffer_);
auto data = enc_buffer.get_buffer();
auto size = enc_buffer.size();
size = ~7 & (size + 7); // 8 byte align
data.resize(size);
result.write_int32(static_cast<int>(size) + 5);
result.write_byte(true);
auto seed = 0x13371337;
result.write_int32(seed);
const auto iv = utils::cryptography::tiger::compute(std::string(reinterpret_cast<char*>(&seed), 4));
const std::string key(reinterpret_cast<char*>(get_key(true)), 24);
result.write(utils::cryptography::des3::encrypt(data, iv, key));
return result.get_buffer();
}
service_server::service_server(std::string _name) : name_(std::move(_name))
{
this->address_ = utils::cryptography::jenkins_one_at_a_time::compute(this->name_);
}
unsigned long service_server::get_address() const
{
return this->address_;
}
int service_server::send(const char* buf, const int len)
{
if (len <= 3) return -1;
std::lock_guard<std::recursive_mutex> _(this->mutex_);
this->incoming_queue_.push(std::string(buf, len));
return len;
}
int service_server::recv(char* buf, int len)
{
if (len > 0 && !this->outgoing_queue_.empty())
{
std::lock_guard<std::recursive_mutex> _(this->mutex_);
len = std::min(len, static_cast<int>(this->outgoing_queue_.size()));
for (auto i = 0; i < len; ++i)
{
buf[i] = this->outgoing_queue_.front();
this->outgoing_queue_.pop();
}
return len;
}
return SOCKET_ERROR;
}
void service_server::send_reply(reply* data)
{
if (!data) return;
std::lock_guard<std::recursive_mutex> _(this->mutex_);
this->reply_sent_ = true;
const auto buffer = data->get_data();
for (const auto& byte : buffer)
{
this->outgoing_queue_.push(byte);
}
}
void service_server::call_handler(const uint8_t type, const std::string& data)
{
if (this->services_.find(type) != this->services_.end())
{
this->services_[type]->call_service(this, data);
}
else
{
#ifdef DEBUG
printf("DW: Missing handler of type %d\n", type);
#endif
}
}
void service_server::run_frame()
{
if (!this->incoming_queue_.empty())
{
std::lock_guard<std::recursive_mutex> _(this->mutex_);
const auto packet = this->incoming_queue_.front();
this->incoming_queue_.pop();
this->parse_packet(packet);
}
}
void service_server::parse_packet(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 == 200) // Connection id
{
byte_buffer bbufer;
bbufer.write_uint64(0x00000000000000FD);
auto reply = this->create_message(4);
reply->send(&bbufer, false);
return;
}
if (buffer.size() < size_t(size)) return;
byte_buffer p_buffer;
p_buffer.set_use_data_types(false);
p_buffer.get_buffer().resize(size);
buffer.read(size, p_buffer.get_buffer().data());
bool enc;
p_buffer.read_bool(&enc);
if (enc)
{
int iv;
p_buffer.read_int32(&iv);
auto iv_hash = utils::cryptography::tiger::compute(std::string(reinterpret_cast<char*>(&iv), 4));
const std::string key(reinterpret_cast<char*>(get_key(false)), 24);
p_buffer = byte_buffer{utils::cryptography::des3::decrypt(p_buffer.get_remaining(), iv_hash, key)};
p_buffer.set_use_data_types(false);
int checksum;
p_buffer.read_int32(&checksum);
}
uint8_t type;
p_buffer.read_byte(&type);
#ifdef DEBUG
printf("DW: Handling message of type %d (encrypted: %d)\n", type, enc);
#endif
this->reply_sent_ = false;
this->call_handler(type, p_buffer.get_remaining());
if (!this->reply_sent_ && type != 7)
{
this->create_reply(type)->send();
}
}
}
catch (...)
{
}
}
}

View File

@@ -0,0 +1,43 @@
#pragma once
#include "i_service.hpp"
namespace demonware
{
class service_server final : public i_server
{
public:
explicit service_server(std::string name);
template <typename T>
void register_service()
{
static_assert(std::is_base_of<i_service, T>::value, "Service must inherit from IService");
auto service = std::make_unique<T>();
const uint16_t type = service->getType();
this->services_[type] = std::move(service);
}
unsigned long get_address() const;
int send(const char* buf, int len) override;
int recv(char* buf, int len) override;
void send_reply(reply* data) override;
void call_handler(uint8_t type, const std::string& data);
void run_frame();
private:
std::string name_;
std::recursive_mutex mutex_;
std::queue<char> outgoing_queue_;
std::queue<std::string> incoming_queue_;
std::map<uint16_t, std::unique_ptr<i_service>> services_;
unsigned long address_ = 0;
bool reply_sent_ = false;
void parse_packet(const std::string& packet);
};
}

View File

@@ -0,0 +1,18 @@
#include <std_include.hpp>
#include "bdAnticheat.hpp"
#include "../data_types.hpp"
namespace demonware
{
bdAnticheat::bdAnticheat()
{
this->register_service(4, &bdAnticheat::report_console_details);
}
void bdAnticheat::report_console_details(i_server* server, [[maybe_unused]] byte_buffer* buffer) const
{
// TODO: Read data as soon as needed
auto reply = server->create_reply(this->get_sub_type());
reply->send();
}
}

View File

@@ -0,0 +1,14 @@
#pragma once
#include "../i_service.hpp"
namespace demonware
{
class bdAnticheat final : public i_generic_service<38>
{
public:
bdAnticheat();
private:
void report_console_details(i_server* server, byte_buffer* buffer) const;
};
}

View File

@@ -0,0 +1,24 @@
#include <std_include.hpp>
#include "bdBandwidthTest.hpp"
#include "../data_types.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
};
void bdBandwidthTest::call_service(i_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);
}
}

View File

@@ -0,0 +1,11 @@
#pragma once
#include "../i_service.hpp"
namespace demonware
{
class bdBandwidthTest final : public i_generic_service<18>
{
public:
void call_service(i_server* server, const std::string& data) override;
};
}

View File

@@ -0,0 +1,29 @@
#include <std_include.hpp>
#include "bdDML.hpp"
#include "../data_types.hpp"
namespace demonware
{
bdDML::bdDML()
{
this->register_service(2, &bdDML::get_user_raw_data);
}
void bdDML::get_user_raw_data(i_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->get_sub_type());
reply->add(result);
reply->send();
}
}

View File

@@ -0,0 +1,14 @@
#pragma once
#include "../i_service.hpp"
namespace demonware
{
class bdDML final : public i_generic_service<27>
{
public:
bdDML();
private:
void get_user_raw_data(i_server* server, byte_buffer* buffer) const;
};
}

View File

@@ -0,0 +1,63 @@
#include <std_include.hpp>
#include "bdDediAuth.hpp"
#include "game/game.hpp"
#include "steam/steam.hpp"
#include <utils/cryptography.hpp>
namespace demonware
{
void bdDediAuth::call_service(i_server* server, const std::string& data)
{
bit_buffer buffer(data);
bool more_data;
buffer.set_use_data_types(false);
buffer.read_bool(&more_data);
buffer.set_use_data_types(true);
uint32_t seed, title_id, ticket_size;
buffer.read_uint32(&seed);
buffer.read_uint32(&title_id);
uint8_t ticket[1024];
buffer.read_bytes(std::min(ticket_size, static_cast<uint32_t>(sizeof(ticket))), ticket);
game::bdAuthTicket auth_ticket{};
std::memset(&auth_ticket, 0xA, sizeof auth_ticket);
auth_ticket.m_magicNumber = 0x0EFBDADDE;
auth_ticket.m_type = 0;
auth_ticket.m_titleID = title_id;
auth_ticket.m_userID = steam::SteamUser()->GetSteamID().bits;
auth_ticket.m_licenseID = 4;
auto key = utils::cryptography::tiger::compute(SERVER_CD_KEY);
strcpy_s(auth_ticket.m_username, "iw6-mod server");
std::memcpy(auth_ticket.m_sessionKey, key.data(), 24);
auth_ticket.m_timeIssued = static_cast<uint32_t>(time(nullptr));
uint8_t lsg_ticket[128];
ZeroMemory(&lsg_ticket, sizeof lsg_ticket);
std::memcpy(lsg_ticket, key.data(), 24);
const auto iv = utils::cryptography::tiger::compute(std::string(reinterpret_cast<char*>(&seed), 4));
const std::string enc_key(reinterpret_cast<char*>(&ticket[32]), 24);
auto enc_ticket = utils::cryptography::des3::encrypt(
std::string(reinterpret_cast<char*>(&auth_ticket), sizeof(auth_ticket)), iv, enc_key);
bit_buffer response;
response.set_use_data_types(false);
response.write_bool(false);
response.write_uint32(700);
response.write_uint32(seed);
response.write_bytes(enc_ticket.size(), enc_ticket.data());
response.write_bytes(sizeof(lsg_ticket), lsg_ticket);
auto reply = server->create_message(29);
reply->send(&response, false);
}
}

View File

@@ -0,0 +1,11 @@
#pragma once
#include "../i_service.hpp"
namespace demonware
{
class bdDediAuth final : public i_generic_service<12>
{
public:
void call_service(i_server* server, const std::string& data) override;
};
}

View File

@@ -0,0 +1,73 @@
#include <std_include.hpp>
#include "bdDediRSAAuth.hpp"
#include "game/game.hpp"
#include "steam/steam.hpp"
#include <utils/cryptography.hpp>
namespace demonware
{
void bdDediRSAAuth::call_service(i_server* server, const std::string& data)
{
bit_buffer buffer(data);
bool more_data;
buffer.set_use_data_types(false);
buffer.read_bool(&more_data);
buffer.set_use_data_types(true);
uint32_t seed, title_id, ticket_size;
buffer.read_uint32(&seed);
buffer.read_uint32(&title_id);
unsigned char rsa_key[140];
buffer.read_bytes(sizeof(rsa_key), rsa_key);
uint8_t ticket[1024];
buffer.read_bytes(std::min(ticket_size, static_cast<uint32_t>(sizeof(ticket))), ticket);
game::bdAuthTicket auth_ticket{};
std::memset(&auth_ticket, 0xA, sizeof auth_ticket);
auth_ticket.m_magicNumber = 0x0EFBDADDE;
auth_ticket.m_type = 0;
auth_ticket.m_titleID = title_id;
auth_ticket.m_userID = steam::SteamUser()->GetSteamID().bits;
auto key = utils::cryptography::tiger::compute(SERVER_CD_KEY);
strcpy_s(auth_ticket.m_username, "iw6-mod server");
std::memcpy(auth_ticket.m_sessionKey, key.data(), 24);
auth_ticket.m_timeIssued = static_cast<uint32_t>(time(nullptr));
uint8_t lsg_ticket[128];
ZeroMemory(&lsg_ticket, sizeof lsg_ticket);
std::memcpy(lsg_ticket, key.data(), 24);
const auto iv = utils::cryptography::tiger::compute(std::string(reinterpret_cast<char*>(&seed), 4));
const std::string enc_key(reinterpret_cast<char*>(&ticket[32]), 24);
auto enc_ticket = utils::cryptography::des3::encrypt(
std::string(reinterpret_cast<char*>(&auth_ticket), sizeof(auth_ticket)), iv, enc_key);
register_hash(&sha1_desc);
register_prng(&yarrow_desc);
auto encrypted_key = utils::cryptography::rsa::encrypt(std::string(SERVER_CD_KEY, 24),
std::string("DW-RSAENC", 10),
std::string(PCHAR(rsa_key), sizeof(rsa_key)));
bit_buffer response;
response.set_use_data_types(false);
response.write_bool(false);
response.write_uint32(700);
response.write_uint32(seed);
response.write_bytes(enc_ticket.size(), enc_ticket.data());
response.write_bytes(sizeof(lsg_ticket), lsg_ticket);
response.write_bytes(encrypted_key.size(), encrypted_key.data());
auto reply = server->create_message(29);
reply->send(&response, false);
}
}

View File

@@ -0,0 +1,11 @@
#pragma once
#include "../i_service.hpp"
namespace demonware
{
class bdDediRSAAuth final : public i_generic_service<26>
{
public:
void call_service(i_server* server, const std::string& data) override;
};
}

View File

@@ -0,0 +1,49 @@
#include <std_include.hpp>
#include "bdGroup.hpp"
#include "../data_types.hpp"
namespace demonware
{
bdGroup::bdGroup()
{
this->register_service(1, &bdGroup::set_groups);
this->register_service(4, &bdGroup::get_groups);
}
void bdGroup::set_groups(i_server* server, byte_buffer* /*buffer*/) const
{
//uint32_t groupCount;
// TODO: Implement array reading
auto reply = server->create_reply(this->get_sub_type());
reply->send();
}
void bdGroup::get_groups(i_server* server, byte_buffer* buffer)
{
uint32_t group_count;
buffer->read_array_header(8, &group_count);
auto reply = server->create_reply(this->get_sub_type());
buffer->set_use_data_types(false);
for (uint32_t i = 0; i < group_count; ++i)
{
auto* count = new bdGroupCount;
buffer->read_uint32(&count->group_id);
if (count->group_id < ARRAYSIZE(this->groups))
{
this->groups[count->group_id] = 999;
count->group_count = this->groups[count->group_id];
}
reply->add(count);
}
buffer->set_use_data_types(true);
reply->send();
}
}

View File

@@ -0,0 +1,17 @@
#pragma once
#include "../i_service.hpp"
namespace demonware
{
class bdGroup final : public i_generic_service<28>
{
public:
bdGroup();
private:
void set_groups(i_server* server, byte_buffer* buffer) const;
void get_groups(i_server* server, byte_buffer* buffer);
uint32_t groups[512]{};
};
}

View File

@@ -0,0 +1,26 @@
#include <std_include.hpp>
#include "bdLSGHello.hpp"
#include "component/demonware.hpp"
namespace demonware
{
void bdLSGHello::call_service(i_server* server, const std::string& data)
{
bit_buffer buffer(data);
bool more_data;
buffer.set_use_data_types(false);
buffer.read_bool(&more_data);
buffer.set_use_data_types(true);
uint32_t seed, title_id;
buffer.read_uint32(&title_id);
buffer.read_uint32(&seed);
uint8_t ticket[128];
buffer.read_bytes(sizeof(ticket), ticket);
set_key(true, ticket);
set_key(false, ticket);
}
}

View File

@@ -0,0 +1,11 @@
#pragma once
#include "../i_service.hpp"
namespace demonware
{
class bdLSGHello final : public i_generic_service<7>
{
public:
void call_service(i_server* server, const std::string& data) override;
};
}

View File

@@ -0,0 +1,107 @@
#include <std_include.hpp>
#include "bdMatchMaking.hpp"
#include "../data_types.hpp"
#include "steam/steam.hpp"
namespace demonware
{
std::map<uint64_t, std::shared_ptr<MatchMakingInfo>> sessions;
void UpdateSession(const std::string& data)
{
byte_buffer buffer(data);
auto mmInfo = std::make_shared<MatchMakingInfo>();
mmInfo->symmetric = true;
mmInfo->deserialize(&buffer);
mmInfo->symmetric = false;
sessions[mmInfo->session_id.session_id] = mmInfo;
}
void DeleteSession(const std::string& data)
{
byte_buffer buffer(data);
bdSessionID id;
id.deserialize(&buffer);
const auto session = sessions.find(id.session_id);
if (session != sessions.end())
{
sessions.erase(session);
}
}
bdMatchMaking::bdMatchMaking()
{
this->register_service(1, &bdMatchMaking::create_session);
this->register_service(2, &bdMatchMaking::update_session);
this->register_service(3, &bdMatchMaking::delete_session);
this->register_service(10, &bdMatchMaking::get_performance);
this->register_service(16, &bdMatchMaking::find_sessions_two_pass);
}
void bdMatchMaking::create_session(i_server* server, byte_buffer* /*buffer*/) const
{
auto* id = new bdSessionID;
id->session_id = steam::SteamUser()->GetSteamID().bits;
auto reply = server->create_reply(this->get_sub_type());
reply->add(id);
reply->send();
}
void bdMatchMaking::update_session(i_server* server, byte_buffer* buffer) const
{
MatchMakingInfo mmInfo;
mmInfo.session_id.deserialize(buffer);
mmInfo.deserialize(buffer);
byte_buffer out_data;
mmInfo.symmetric = true;
mmInfo.serialize(&out_data);
byte_buffer addr_buf(mmInfo.host_addr);
bdCommonAddr addr;
addr.deserialize(&addr_buf);
auto reply = server->create_reply(this->get_sub_type());
reply->send();
}
void bdMatchMaking::delete_session(i_server* server, byte_buffer* buffer) const
{
bdSessionID id;
id.deserialize(buffer);
byte_buffer out_data;
id.serialize(&out_data);
auto reply = server->create_reply(this->get_sub_type());
reply->send();
}
void bdMatchMaking::get_performance(i_server* server, byte_buffer* /*buffer*/) const
{
auto* result = new bdPerformanceValue;
result->user_id = steam::SteamUser()->GetSteamID().bits;
result->performance = 10;
auto reply = server->create_reply(this->get_sub_type());
reply->add(result);
reply->send();
}
void bdMatchMaking::find_sessions_two_pass(i_server* server, byte_buffer* /*buffer*/) const
{
auto reply = server->create_reply(this->get_sub_type());
for (auto& session : sessions)
{
reply->add(session.second);
}
reply->send();
}
}

View File

@@ -0,0 +1,18 @@
#pragma once
#include "../i_service.hpp"
namespace demonware
{
class bdMatchMaking final : public i_generic_service<21>
{
public:
bdMatchMaking();
private:
void create_session(i_server* server, byte_buffer* buffer) const;
void update_session(i_server* server, byte_buffer* buffer) const;
void delete_session(i_server* server, byte_buffer* buffer) const;
void get_performance(i_server* server, byte_buffer* buffer) const;
void find_sessions_two_pass(i_server* server, byte_buffer* buffer) const;
};
}

View File

@@ -0,0 +1,136 @@
#include <std_include.hpp>
#include "bdRelayService.hpp"
#include "../data_types.hpp"
#include "steam/steam.hpp"
namespace demonware
{
class DebugObjectUCD : public i_serializable
{
public:
uint64_t user_id;
std::string platform;
std::vector<uint64_t> user_ids;
std::vector<std::string> user_files;
void serialize(byte_buffer* buffer) override
{
buffer->write_uint64(this->user_id);
buffer->write_string(this->platform);
buffer->write_array_header(10, INT(this->user_ids.size()), sizeof(uint64_t));
buffer->set_use_data_types(false);
for (size_t i = 0; i < this->user_ids.size(); i++)
{
buffer->write_uint64(this->user_ids[i]);
}
buffer->set_use_data_types(true);
auto stringSize = this->user_files.size();
for (size_t i = 0; i < this->user_files.size(); i++)
{
stringSize += this->user_files[i].length();
}
buffer->write_array_header(16, INT(this->user_files.size()), INT(stringSize / this->user_files.size()));
buffer->set_use_data_types(false);
for (size_t i = 0; i < this->user_files.size(); i++)
{
buffer->write_string(this->user_files[i]);
}
buffer->set_use_data_types(true);
}
void deserialize(byte_buffer* /*buffer*/) override
{
}
};
class DebugObjectUNO : public i_serializable
{
public:
std::vector<uint64_t> user_ids;
std::vector<std::string> user_files;
void serialize(byte_buffer* buffer) override
{
buffer->write_array_header(10, INT(this->user_ids.size()), sizeof(uint64_t));
buffer->set_use_data_types(false);
for (size_t i = 0; i < this->user_ids.size(); i++)
{
buffer->write_uint64(this->user_ids[i]);
}
buffer->set_use_data_types(true);
auto stringSize = this->user_files.size();
for (size_t i = 0; i < this->user_files.size(); i++)
{
stringSize += this->user_files[i].length();
}
buffer->write_array_header(16, INT(this->user_files.size()), INT(stringSize / this->user_files.size()));
buffer->set_use_data_types(false);
for (size_t i = 0; i < this->user_files.size(); i++)
{
buffer->write_string(this->user_files[i]);
}
buffer->set_use_data_types(true);
}
void deserialize(byte_buffer* /*buffer*/) override
{
}
};
bdRelayService::bdRelayService()
{
this->register_service(3, &bdRelayService::get_credentials);
this->register_service(4, &bdRelayService::get_credentials_from_ticket);
}
void bdRelayService::get_credentials(i_server* server, byte_buffer* buffer) const
{
uint32_t unk1;
uint64_t user_id;
std::string platform;
// User info.
buffer->read_uint32(&unk1);
buffer->read_uint64(&user_id);
buffer->read_string(&platform);
auto* result = new DebugObjectUCD;
result->user_id = steam::SteamUser()->GetSteamID().bits;
result->user_ids.push_back(steam::SteamUser()->GetSteamID().bits);
result->user_ids.push_back(0x00659CD6);
result->user_files.push_back("pc");
result->user_files.push_back("ucd");
auto reply = server->create_reply(this->get_sub_type());
reply->add(result);
reply->send();
}
void bdRelayService::get_credentials_from_ticket(i_server* server, byte_buffer* buffer) const
{
std::string ticket;
buffer->read_string(&ticket);
auto* result = new DebugObjectUNO;
// Create fake info.
result->user_ids.push_back(0x1A586A45744DA396);
result->user_ids.push_back(steam::SteamUser()->GetSteamID().bits);
result->user_ids.push_back(16326195462233142067);
result->user_files.push_back("youtube");
result->user_files.push_back("steam");
result->user_files.push_back("uno");
auto reply = server->create_reply(this->get_sub_type());
reply->add(result);
reply->send();
}
}

View File

@@ -0,0 +1,15 @@
#pragma once
#include "../i_service.hpp"
namespace demonware
{
class bdRelayService final : public i_generic_service<86>
{
public:
bdRelayService();
private:
void get_credentials(i_server* server, byte_buffer* buffer) const;
void get_credentials_from_ticket(i_server* server, byte_buffer* buffer) const;
};
}

View File

@@ -0,0 +1,63 @@
#include <std_include.hpp>
#include "bdSteamAuth.hpp"
#include "game/structs.hpp"
#include "steam/steam.hpp"
#include <utils/cryptography.hpp>
namespace demonware
{
void bdSteamAuth::call_service(i_server* server, const std::string& data)
{
bit_buffer buffer(data);
bool more_data;
buffer.set_use_data_types(false);
buffer.read_bool(&more_data);
buffer.set_use_data_types(true);
uint32_t seed, title_id, ticket_size;
buffer.read_uint32(&seed);
buffer.read_uint32(&title_id);
buffer.read_uint32(&ticket_size);
uint8_t ticket[1024];
buffer.read_bytes(std::min(ticket_size, static_cast<uint32_t>(sizeof(ticket))), ticket);
game::bdAuthTicket auth_ticket{};
std::memset(&auth_ticket, 0xA, sizeof auth_ticket);
auth_ticket.m_magicNumber = 0x0EFBDADDE;
auth_ticket.m_type = 0;
auth_ticket.m_titleID = title_id;
auth_ticket.m_userID = steam::SteamUser()->GetSteamID().bits;
auto key = utils::cryptography::tiger::compute("iw6-mod");
strcpy_s(auth_ticket.m_username, "iw6-mod user");
std::memcpy(auth_ticket.m_sessionKey, key.data(), 24);
auth_ticket.m_timeIssued = static_cast<uint32_t>(time(nullptr));
uint8_t lsg_ticket[128];
ZeroMemory(&lsg_ticket, sizeof lsg_ticket);
std::memcpy(lsg_ticket, key.data(), 24);
const auto iv = utils::cryptography::tiger::compute(std::string(reinterpret_cast<char*>(&seed), 4));
const std::string enc_key(reinterpret_cast<char*>(&ticket[32]), 24);
auto enc_ticket = utils::cryptography::des3::encrypt(
std::string(reinterpret_cast<char*>(&auth_ticket), sizeof(auth_ticket)), iv, enc_key);
bit_buffer response;
response.set_use_data_types(false);
response.write_bool(false);
response.write_uint32(700);
response.write_uint32(seed);
response.write_bytes(enc_ticket.size(), enc_ticket.data());
response.write_bytes(sizeof(lsg_ticket), lsg_ticket);
auto reply = server->create_message(29);
reply->send(&response, false);
}
}

View File

@@ -0,0 +1,11 @@
#pragma once
#include "../i_service.hpp"
namespace demonware
{
class bdSteamAuth final : public i_generic_service<28>
{
public:
void call_service(i_server* server, const std::string& data) override;
};
}

View File

@@ -0,0 +1,365 @@
#include <std_include.hpp>
#include "bdStorage.hpp"
#include "../data_types.hpp"
#include <utils/compression.hpp>
#include <utils/cryptography.hpp>
#include <utils/nt.hpp>
#include <utils/io.hpp>
#include <utils/string.hpp>
#include "component/filesystem.hpp"
namespace demonware
{
namespace
{
std::string get_motd_text()
{
return "This is not a copy & pasted client";
}
}
bdStorage::bdStorage()
{
this->register_service(1, &bdStorage::set_legacy_user_file);
this->register_service(3, &bdStorage::get_legacy_user_file);
this->register_service(5, &bdStorage::list_legacy_user_files);
this->register_service(6, &bdStorage::list_publisher_files);
this->register_service(7, &bdStorage::get_publisher_file);
this->register_service(8, &bdStorage::update_legacy_user_file);
this->register_service(10, &bdStorage::set_user_file);
this->register_service(11, &bdStorage::delete_user_file);
this->register_service(12, &bdStorage::get_user_file);
this->map_publisher_resource_variant("motd-.*\\.txt", get_motd_text);
this->map_publisher_resource("newsfeed-.*\\.txt", "dw/newsfeed.txt", DW_NEWSFEED);
this->map_publisher_resource("mm\\.cfg", "dw/mm.cfg", DW_MM_CONFIG);
this->map_publisher_resource("playlists(_.+)?\\.aggr", "dw/playlists_tu14.aggr", DW_PLAYLISTS);
this->map_publisher_resource("social_[Tt][Uu][0-9]+\\.cfg", "dw/social_tu14.cfg", DW_SOCIAL_CONFIG);
this->map_publisher_resource("entitlement_config_[Tt][Uu][0-9]+\\.info", "dw/entitlement_config_tu14.info", DW_ENTITLEMENT_CONFIG);
publisher_resources_.emplace_back(std::regex{"heatmap\\.raw"}, generate_heat_map());
}
void bdStorage::map_publisher_resource(const std::string& expression, const std::string& path, const int id)
{
auto data = filesystem::exists(path)
? filesystem::read_file(path)
: 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) const
{
for (const auto& resource : this->publisher_resources_)
{
if (std::regex_match(name, resource.first))
{
if (std::holds_alternative<std::string>(resource.second))
{
buffer = std::get<std::string>(resource.second);
}
else
{
buffer = std::get<callback>(resource.second)();
}
return true;
}
}
#ifdef DEBUG
printf("[DW]: [bdStorage]: missing publisher file: %s\n", name.data());
#endif
return false;
}
std::string bdStorage::get_user_file_path(const std::string& name)
{
return "players2/user/" + name;
}
std::string bdStorage::generate_heat_map()
{
uint8_t map[256][256];
for (auto y = 0; y < 256; ++y)
{
for (auto x = 0; x < 256; ++x)
{
auto data = std::rand() % std::numeric_limits<std::uint8_t>::max();
if (data % 15 == 0)
{
data = 0xF | ((data & 0x7) << 4) | 0x10;
}
else
{
data = 0;
}
map[y][x] = static_cast<std::uint8_t>(data);
}
}
return utils::compression::zlib::compress(std::string(LPSTR(map), sizeof(map)));
}
void bdStorage::set_legacy_user_file(i_server* server, byte_buffer* buffer) const
{
bool priv;
std::string filename, data;
buffer->read_string(&filename);
buffer->read_bool(&priv);
buffer->read_blob(&data);
const auto id = *reinterpret_cast<const uint64_t*>(utils::cryptography::sha1::compute(filename).data());
std::string id_string = utils::string::va("%llX", id);
#ifdef DEBUG
printf("DW: Storing user file '%s' as %s\n", filename.data(), id_string.data());
#endif
const auto path = get_user_file_path(id_string);
utils::io::write_file(path, data);
auto* info = new bdFileInfo;
info->file_id = id;
info->filename = filename;
info->create_time = uint32_t(time(nullptr));
info->modified_time = info->create_time;
info->file_size = uint32_t(data.size());
info->owner_id = 0;
info->priv = priv;
auto reply = server->create_reply(this->get_sub_type());
reply->add(info);
reply->send();
}
void bdStorage::update_legacy_user_file(i_server* server, byte_buffer* buffer) const
{
uint64_t id;
std::string data;
buffer->read_uint64(&id);
buffer->read_blob(&data);
std::string id_string = utils::string::va("%llX", id);
#ifdef DEBUG
printf("DW: Updating user file %s\n", id_string.data());
#endif
const auto path = get_user_file_path(id_string);
utils::io::write_file(path, data);
auto* info = new bdFileInfo;
info->file_id = id;
info->filename = "<>";
info->create_time = uint32_t(time(nullptr));
info->modified_time = info->create_time;
info->file_size = uint32_t(data.size());
info->owner_id = 0;
info->priv = false;
auto reply = server->create_reply(this->get_sub_type());
reply->add(info);
reply->send();
}
void bdStorage::get_legacy_user_file(i_server* server, byte_buffer* buffer) const
{
std::string filename, data;
buffer->read_string(&filename);
const auto id = *reinterpret_cast<const uint64_t*>(utils::cryptography::sha1::compute(filename).data());
std::string id_string = utils::string::va("%llX", id);
#ifdef DEBUG
printf("DW: Loading user file: %s (%s)\n", filename.data(), id_string.data());
#endif
const auto path = get_user_file_path(id_string);
if (utils::io::read_file(path, &data))
{
auto reply = server->create_reply(this->get_sub_type());
reply->add(new bdFileData(data));
reply->send();
}
else
{
server->create_reply(this->get_sub_type(), game::BD_NO_FILE)->send();
}
}
void bdStorage::list_legacy_user_files(i_server* server, byte_buffer* buffer) const
{
uint64_t unk;
uint32_t date;
uint16_t num_results, offset;
std::string filename, data;
buffer->read_uint64(&unk);
buffer->read_uint32(&date);
buffer->read_uint16(&num_results);
buffer->read_uint16(&offset);
buffer->read_string(&filename);
auto reply = server->create_reply(this->get_sub_type());
const auto path = get_user_file_path(filename);
if (utils::io::read_file(path, &data))
{
auto* info = new bdFileInfo;
info->file_id = *reinterpret_cast<const uint64_t*>(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::list_publisher_files(i_server* server, byte_buffer* buffer)
{
uint32_t date;
uint16_t num_results, offset;
std::string filename, data;
buffer->read_uint32(&date);
buffer->read_uint16(&num_results);
buffer->read_uint16(&offset);
buffer->read_string(&filename);
auto reply = server->create_reply(this->get_sub_type());
if (this->load_publisher_resource(filename, data))
{
auto* info = new bdFileInfo;
info->file_id = *reinterpret_cast<const uint64_t*>(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(i_server* server, byte_buffer* buffer)
{
std::string filename;
buffer->read_string(&filename);
#ifdef DEBUG
printf("[DW]: [bdStorage]: loading publisher file: %s\n", filename.data());
#endif
std::string data;
if (this->load_publisher_resource(filename, data))
{
#ifdef DEBUG
printf("[DW]: [bdStorage]: sending publisher file: %s, size: %lld\n", filename.data(), data.size());
#endif
auto reply = server->create_reply(this->get_sub_type());
reply->add(new bdFileData(data));
reply->send();
}
else
{
server->create_reply(this->get_sub_type(), game::BD_NO_FILE)->send();
}
}
void bdStorage::delete_user_file(i_server* server, byte_buffer* buffer) const
{
uint64_t owner;
std::string game, filename;
buffer->read_string(&game);
buffer->read_string(&filename);
buffer->read_uint64(&owner);
// Really remove the file?
auto reply = server->create_reply(this->get_sub_type());
reply->send();
}
void bdStorage::set_user_file(i_server* server, byte_buffer* buffer) const
{
bool priv;
uint64_t owner;
std::string game, filename, data;
buffer->read_string(&game);
buffer->read_string(&filename);
buffer->read_bool(&priv);
buffer->read_blob(&data);
buffer->read_uint64(&owner);
const auto path = get_user_file_path(filename);
utils::io::write_file(path, data);
auto* info = new bdFileInfo;
info->file_id = *reinterpret_cast<const uint64_t*>(utils::cryptography::sha1::compute(filename).data());
info->filename = filename;
info->create_time = uint32_t(time(nullptr));
info->modified_time = info->create_time;
info->file_size = uint32_t(data.size());
info->owner_id = owner;
info->priv = priv;
auto reply = server->create_reply(this->get_sub_type());
reply->add(info);
reply->send();
}
void bdStorage::get_user_file(i_server* server, byte_buffer* buffer) const
{
uint64_t owner{};
std::string game, filename, platform, data;
buffer->read_string(&game);
buffer->read_string(&filename);
buffer->read_uint64(&owner);
buffer->read_string(&platform);
const auto path = get_user_file_path(filename);
if (utils::io::read_file(path, &data))
{
auto reply = server->create_reply(this->get_sub_type());
reply->add(new bdFileData(data));
reply->send();
}
else
{
server->create_reply(this->get_sub_type(), game::BD_NO_FILE)->send();
}
}
}

View File

@@ -0,0 +1,33 @@
#pragma once
#include "../i_service.hpp"
namespace demonware
{
class bdStorage final : public i_generic_service<10>
{
public:
bdStorage();
private:
using callback = std::function<std::string()>;
using resource_variant = std::variant<std::string, callback>;
std::vector<std::pair<std::regex, resource_variant>> publisher_resources_;
void set_legacy_user_file(i_server* server, byte_buffer* buffer) const;
void update_legacy_user_file(i_server* server, byte_buffer* buffer) const;
void get_legacy_user_file(i_server* server, byte_buffer* buffer) const;
void list_legacy_user_files(i_server* server, byte_buffer* buffer) const;
void list_publisher_files(i_server* server, byte_buffer* buffer);
void get_publisher_file(i_server* server, byte_buffer* buffer);
void delete_user_file(i_server* server, byte_buffer* buffer) const;
void set_user_file(i_server* server, byte_buffer* buffer) const;
void get_user_file(i_server* server, byte_buffer* buffer) const;
void map_publisher_resource(const std::string& expression, const std::string& path, 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) const;
static std::string get_user_file_path(const std::string& name);
static std::string generate_heat_map();
};
}

View File

@@ -0,0 +1,21 @@
#include <std_include.hpp>
#include "bdTitleUtilities.hpp"
#include "../data_types.hpp"
namespace demonware
{
bdTitleUtilities::bdTitleUtilities()
{
this->register_service(6, &bdTitleUtilities::get_server_time);
}
void bdTitleUtilities::get_server_time(i_server* server, byte_buffer* /*buffer*/) const
{
const auto time_result = new bdTimeStamp;
time_result->unix_time = uint32_t(time(nullptr));
auto reply = server->create_reply(this->get_sub_type());
reply->add(time_result);
reply->send();
}
}

View File

@@ -0,0 +1,14 @@
#pragma once
#include "../i_service.hpp"
namespace demonware
{
class bdTitleUtilities final : public i_generic_service<12>
{
public:
bdTitleUtilities();
private:
void get_server_time(i_server* server, byte_buffer* buffer) const;
};
}

View File

@@ -0,0 +1,78 @@
#include <std_include.hpp>
#include <utility>
#include "component/demonware.hpp"
#include "game/demonware/stun_server.hpp"
#include "byte_buffer.hpp"
#include <utils/cryptography.hpp>
namespace demonware
{
stun_server::stun_server(std::string _name) : name_(std::move(_name))
{
this->address_ = utils::cryptography::jenkins_one_at_a_time::compute(this->name_);
}
unsigned long stun_server::get_address() const
{
return this->address_;
}
void stun_server::ip_discovery(SOCKET s, const sockaddr* to, const int tolen) const
{
const uint32_t ip = 0x0100007f;
byte_buffer buffer;
buffer.set_use_data_types(false);
buffer.write_byte(31); // type
buffer.write_byte(2); // version
buffer.write_byte(0); // version
buffer.write_uint32(ip); // external ip
buffer.write_uint16(3074); // port
send_datagram_packet(s, buffer.get_buffer(), to, tolen);
}
void stun_server::nat_discovery(SOCKET s, const sockaddr* to, const int tolen) const
{
const uint32_t ip = 0x0100007f;
byte_buffer buffer;
buffer.set_use_data_types(false);
buffer.write_byte(21); // type
buffer.write_byte(2); // version
buffer.write_byte(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
send_datagram_packet(s, buffer.get_buffer(), to, tolen);
}
int stun_server::send(const SOCKET s, const char* buf, const int len, const sockaddr* to, const int tolen) const
{
uint8_t type, version, padding;
byte_buffer buffer(std::string(buf, len));
buffer.set_use_data_types(false);
buffer.read_byte(&type);
buffer.read_byte(&version);
buffer.read_byte(&padding);
switch (type)
{
case 30:
this->ip_discovery(s, to, tolen);
break;
case 20:
this->nat_discovery(s, to, tolen);
break;
default:
break;
}
return len;
}
}

View File

@@ -0,0 +1,21 @@
#pragma once
namespace demonware
{
class stun_server final
{
public:
explicit stun_server(std::string name);
unsigned long get_address() const;
int send(SOCKET s, const char* buf, int len, const sockaddr* to, int tolen) const;
private:
std::string name_;
unsigned long address_;
void ip_discovery(SOCKET s, const sockaddr* to, int tolen) const;
void nat_discovery(SOCKET s, const sockaddr* to, int tolen) const;
};
}