Add console handling and prepare demonware emulation
This commit is contained in:
parent
8308b72e96
commit
e1333db8a2
@ -91,7 +91,7 @@ workspace "open-iw5"
|
||||
configuration {}
|
||||
|
||||
project "open-iw5"
|
||||
kind "WindowedApp"
|
||||
kind "ConsoleApp"
|
||||
language "C++"
|
||||
|
||||
pchheader "std_include.hpp"
|
||||
|
182
src/game/demonware/bit_buffer.cpp
Normal file
182
src/game/demonware/bit_buffer.cpp
Normal 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_u_int32(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<const unsigned char*>(data));
|
||||
}
|
||||
|
||||
bool bit_buffer::write_bytes(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 = 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_;
|
||||
}
|
||||
}
|
40
src/game/demonware/bit_buffer.hpp
Normal file
40
src/game/demonware/bit_buffer.hpp
Normal 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_u_int32(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;
|
||||
};
|
||||
}
|
298
src/game/demonware/byte_buffer.cpp
Normal file
298
src/game/demonware/byte_buffer.cpp
Normal file
@ -0,0 +1,298 @@
|
||||
#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(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_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(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(reinterpret_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(bool _useDataTypes)
|
||||
{
|
||||
this->use_data_types_ = _useDataTypes;
|
||||
}
|
||||
|
||||
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_;
|
||||
}
|
||||
}
|
66
src/game/demonware/byte_buffer.hpp
Normal file
66
src/game/demonware/byte_buffer.hpp
Normal file
@ -0,0 +1,66 @@
|
||||
#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(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();
|
||||
|
||||
private:
|
||||
std::string buffer_;
|
||||
size_t current_byte_ = 0;
|
||||
bool use_data_types_ = true;
|
||||
};
|
||||
}
|
449
src/game/demonware/data_types.hpp
Normal file
449
src/game/demonware/data_types.hpp
Normal 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(const std::string& buffer) : file_data(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 && 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::native::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);
|
||||
}
|
||||
};
|
||||
}
|
182
src/game/demonware/i_server.hpp
Normal file
182
src/game/demonware/i_server.hpp
Normal file
@ -0,0 +1,182 @@
|
||||
#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_;
|
||||
};
|
||||
}
|
70
src/game/demonware/i_service.hpp
Normal file
70
src/game/demonware/i_service.hpp
Normal file
@ -0,0 +1,70 @@
|
||||
#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_);
|
||||
|
||||
printf("DW: Handling subservice of type %d\n", this->sub_type_);
|
||||
|
||||
const auto callback = this->callbacks.find(this->sub_type_);
|
||||
if (callback != this->callbacks.end())
|
||||
{
|
||||
callback->second(server, &buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("DW: Missing subservice %d for type %d\n", this->sub_type_, this->getType());
|
||||
}
|
||||
}
|
||||
|
||||
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 ...))
|
||||
{
|
||||
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 final : public i_service
|
||||
{
|
||||
public:
|
||||
uint16_t getType() override { return Type; }
|
||||
};
|
||||
}
|
196
src/game/demonware/service_server.cpp
Normal file
196
src/game/demonware/service_server.cpp
Normal file
@ -0,0 +1,196 @@
|
||||
#include <std_include.hpp>
|
||||
#include "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));
|
||||
//result.write(utils::cryptography::des3::encrypt(data, iv, Components::DemonWare::GetKey(true)));
|
||||
|
||||
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));
|
||||
//this->parsePacket(Utils::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 _(this->mutex_);
|
||||
|
||||
this->reply_sent_ = true;
|
||||
const auto buffer = data->get_data();
|
||||
for (auto& byte : buffer)
|
||||
{
|
||||
this->outgoing_queue_.push(byte);
|
||||
}
|
||||
}
|
||||
|
||||
void service_server::call_handler(uint8_t type, const std::string& data)
|
||||
{
|
||||
if (this->services_.find(type) != this->services_.end())
|
||||
{
|
||||
this->services_[type]->call_service(this, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("DW: Missing handler of type %d\n", type);
|
||||
}
|
||||
}
|
||||
|
||||
void service_server::run_frame()
|
||||
{
|
||||
if (!this->incoming_queue_.empty())
|
||||
{
|
||||
std::lock_guard _(this->mutex_);
|
||||
const std::string 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 (!packet.empty())
|
||||
{
|
||||
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));
|
||||
//pBuffer.get_buffer() = utils::cryptography::des3::decrypt(pBuffer.get_buffer(), iv_hash, Components::DemonWare::GetKey(false));
|
||||
|
||||
int checksum;
|
||||
p_buffer.read_int32(&checksum);
|
||||
}
|
||||
|
||||
uint8_t type;
|
||||
p_buffer.read_byte(&type);
|
||||
printf("DW: Handling message of type %d (encrypted: %d)\n", type, enc);
|
||||
|
||||
this->reply_sent_ = false;
|
||||
this->call_handler(type, p_buffer.get_buffer());
|
||||
|
||||
if (!this->reply_sent_ && type != 7)
|
||||
{
|
||||
this->create_reply(type)->send();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
42
src/game/demonware/service_server.hpp
Normal file
42
src/game/demonware/service_server.hpp
Normal file
@ -0,0 +1,42 @@
|
||||
#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);
|
||||
};
|
||||
}
|
70
src/game/demonware/stun_server.cpp
Normal file
70
src/game/demonware/stun_server.cpp
Normal file
@ -0,0 +1,70 @@
|
||||
#include <std_include.hpp>
|
||||
#include <utility>
|
||||
#include "stun_server.hpp"
|
||||
#include "utils/cryptography.hpp"
|
||||
#include "byte_buffer.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, 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
|
||||
|
||||
//Components::DemonWare::SendDatagramPacket(s, buffer, to, tolen);
|
||||
}
|
||||
|
||||
void stun_server::nat_discovery(SOCKET s, const sockaddr* to, 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
|
||||
|
||||
//Components::DemonWare::SendDatagramPacket(s, buffer, to, tolen);
|
||||
}
|
||||
|
||||
int stun_server::send(const SOCKET s, const char* buf, int len, const sockaddr* to, 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;
|
||||
}
|
||||
}
|
21
src/game/demonware/stun_server.hpp
Normal file
21
src/game/demonware/stun_server.hpp
Normal 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;
|
||||
};
|
||||
}
|
@ -5,6 +5,8 @@ namespace game
|
||||
{
|
||||
namespace native
|
||||
{
|
||||
Conbuf_AppendText_t Conbuf_AppendText;
|
||||
|
||||
Sys_ShowConsole_t Sys_ShowConsole;
|
||||
}
|
||||
|
||||
@ -30,6 +32,8 @@ namespace game
|
||||
{
|
||||
mode = _mode;
|
||||
|
||||
native::Conbuf_AppendText = native::Conbuf_AppendText_t(SELECT_VALUE(0x4C84E0, 0x5CF610, 0x53C790));
|
||||
|
||||
native::Sys_ShowConsole = native::Sys_ShowConsole_t(SELECT_VALUE(0x470AF0, 0x5CF590, 0));
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "structs.hpp"
|
||||
#include "launcher/launcher.hpp"
|
||||
|
||||
#define SELECT_VALUE(sp, mp, dedi) (game::is_sp() ? (sp) : (game::is_mp() ? (mp) : (dedi)))
|
||||
@ -8,13 +9,16 @@ namespace game
|
||||
{
|
||||
namespace native
|
||||
{
|
||||
typedef void(*Sys_ShowConsole_t)();
|
||||
typedef void (*Conbuf_AppendText_t)(const char* message);
|
||||
extern Conbuf_AppendText_t Conbuf_AppendText;
|
||||
|
||||
typedef void (*Sys_ShowConsole_t)();
|
||||
extern Sys_ShowConsole_t Sys_ShowConsole;
|
||||
}
|
||||
|
||||
|
||||
bool is_mp();
|
||||
bool is_sp();
|
||||
bool is_dedi();
|
||||
|
||||
void initialize(launcher::mode mode);
|
||||
}
|
||||
}
|
||||
|
15
src/game/structs.hpp
Normal file
15
src/game/structs.hpp
Normal file
@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
namespace game
|
||||
{
|
||||
namespace native
|
||||
{
|
||||
enum bdNATType : uint8_t
|
||||
{
|
||||
BD_NAT_UNKNOWN = 0x0,
|
||||
BD_NAT_OPEN = 0x1,
|
||||
BD_NAT_MODERATE = 0x2,
|
||||
BD_NAT_STRICT = 0x3,
|
||||
};
|
||||
}
|
||||
}
|
@ -59,13 +59,13 @@ void image::set_click_listener(std::function<void()> callback)
|
||||
|
||||
void image::click(const POINT& mouse, const bool down)
|
||||
{
|
||||
if(down)
|
||||
if (down)
|
||||
{
|
||||
this->down_handled_ = this->is_hovered(mouse);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(this->down_handled_ && this->is_hovered(mouse) && this->callback_)
|
||||
if (this->down_handled_ && this->is_hovered(mouse) && this->callback_)
|
||||
{
|
||||
this->callback_();
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ void launcher::draw_text(const HDC hdc)
|
||||
Gdiplus::RectF rect{};
|
||||
graphics.MeasureString(sp.data(), -1, &font, rect, stringformat, &rect);
|
||||
|
||||
Gdiplus::PointF pos{150 - (rect.Width / 2 + 2), 45};
|
||||
const Gdiplus::PointF pos{150 - (rect.Width / 2 + 2), 45};
|
||||
graphics.DrawString(sp.data(), -1, &font, pos, &color);
|
||||
|
||||
rect = {};
|
||||
|
@ -94,7 +94,7 @@ void loader::load_sections(const utils::nt::module& target, const utils::nt::mod
|
||||
|
||||
void loader::load_imports(const utils::nt::module& target, const utils::nt::module& source) const
|
||||
{
|
||||
IMAGE_DATA_DIRECTORY* import_directory = &source.get_optional_header()->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
|
||||
const auto import_directory = &source.get_optional_header()->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
|
||||
|
||||
auto descriptor = PIMAGE_IMPORT_DESCRIPTOR(target.get_ptr() + import_directory->VirtualAddress);
|
||||
|
||||
|
@ -7,6 +7,10 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
virtual void post_start()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void post_load()
|
||||
{
|
||||
}
|
||||
|
@ -14,6 +14,18 @@ void module_loader::register_module(std::unique_ptr<module>&& module_)
|
||||
modules_->push_back(std::move(module_));
|
||||
}
|
||||
|
||||
void module_loader::post_start()
|
||||
{
|
||||
static auto handled = false;
|
||||
if (handled || !modules_) return;
|
||||
handled = true;
|
||||
|
||||
for (const auto& module_ : *modules_)
|
||||
{
|
||||
module_->post_start();
|
||||
}
|
||||
}
|
||||
|
||||
void module_loader::post_load()
|
||||
{
|
||||
static auto handled = false;
|
||||
|
@ -18,6 +18,7 @@ public:
|
||||
|
||||
static void register_module(std::unique_ptr<module>&& module);
|
||||
|
||||
static void post_start();
|
||||
static void post_load();
|
||||
static void pre_destroy();
|
||||
|
||||
|
@ -13,7 +13,7 @@ void exit_hook(const int code)
|
||||
exit(code);
|
||||
}
|
||||
|
||||
int CALLBACK WinMain(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int /*nCmdShow*/)
|
||||
int main()
|
||||
{
|
||||
FARPROC entry_point = nullptr;
|
||||
|
||||
@ -24,6 +24,8 @@ int CALLBACK WinMain(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, LPSTR
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
module_loader::post_start();
|
||||
|
||||
launcher launcher;
|
||||
const auto mode = launcher.run();
|
||||
|
||||
@ -50,7 +52,7 @@ int CALLBACK WinMain(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, LPSTR
|
||||
game::initialize(mode);
|
||||
module_loader::post_load();
|
||||
}
|
||||
catch (std::exception e)
|
||||
catch (std::exception& e)
|
||||
{
|
||||
MessageBoxA(nullptr, e.what(), "ERROR", MB_ICONERROR);
|
||||
return 1;
|
||||
|
@ -1,16 +1,105 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/module_loader.hpp"
|
||||
#include "console.hpp"
|
||||
#include "game/game.hpp"
|
||||
|
||||
class console final : public module
|
||||
{
|
||||
public:
|
||||
console()
|
||||
{
|
||||
ShowWindow(GetConsoleWindow(), SW_HIDE);
|
||||
|
||||
_pipe(this->handles_, 1024, _O_TEXT);
|
||||
_dup2(this->handles_[1], 1);
|
||||
_dup2(this->handles_[1], 2);
|
||||
|
||||
setvbuf(stdout, nullptr, _IONBF, 0);
|
||||
setvbuf(stderr, nullptr, _IONBF, 0);
|
||||
}
|
||||
|
||||
void post_start() override
|
||||
{
|
||||
this->console_runner_ = std::thread(std::bind(&console::runner, this));
|
||||
}
|
||||
|
||||
void pre_destroy() override
|
||||
{
|
||||
this->terminate_runner_ = true;
|
||||
|
||||
printf("\r\n");
|
||||
_flushall();
|
||||
|
||||
_close(this->handles_[0]);
|
||||
_close(this->handles_[1]);
|
||||
|
||||
if (this->console_runner_.joinable())
|
||||
{
|
||||
this->console_runner_.join();
|
||||
}
|
||||
}
|
||||
|
||||
void post_load() override
|
||||
{
|
||||
if (game::is_dedi()) return;
|
||||
|
||||
game::native::Sys_ShowConsole();
|
||||
|
||||
std::lock_guard _(this->mutex_);
|
||||
this->console_initialized_ = true;
|
||||
this->replay_messages();
|
||||
}
|
||||
|
||||
private:
|
||||
bool console_initialized_ = false;
|
||||
bool terminate_runner_ = false;
|
||||
|
||||
std::mutex mutex_;
|
||||
std::thread console_runner_;
|
||||
std::queue<std::string> message_queue_;
|
||||
|
||||
int handles_[2]{};
|
||||
|
||||
void replay_messages()
|
||||
{
|
||||
if (!this->console_initialized_) return;
|
||||
|
||||
while (!this->message_queue_.empty())
|
||||
{
|
||||
this->push_message(this->message_queue_.front());
|
||||
this->message_queue_.pop();
|
||||
}
|
||||
}
|
||||
|
||||
void push_message(const std::string& message)
|
||||
{
|
||||
if (!this->console_initialized_)
|
||||
{
|
||||
std::lock_guard _(this->mutex_);
|
||||
this->message_queue_.push(message);
|
||||
return;
|
||||
}
|
||||
|
||||
game::native::Conbuf_AppendText(message.data());
|
||||
}
|
||||
|
||||
void runner()
|
||||
{
|
||||
char buffer[1024];
|
||||
|
||||
while (!this->terminate_runner_ && this->handles_[0])
|
||||
{
|
||||
const auto len = _read(this->handles_[0], buffer, sizeof(buffer));
|
||||
if (len > 0)
|
||||
{
|
||||
this->push_message(std::string(buffer, len));
|
||||
}
|
||||
else
|
||||
{
|
||||
std::this_thread::sleep_for(10ms);
|
||||
}
|
||||
}
|
||||
|
||||
std::this_thread::yield();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1 +0,0 @@
|
||||
#pragma once
|
@ -1,5 +1,7 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/module_loader.hpp"
|
||||
#include "utils/hook.hpp"
|
||||
#include "game/game.hpp"
|
||||
|
||||
class dw final : public module
|
||||
{
|
||||
@ -8,6 +10,26 @@ public:
|
||||
{
|
||||
// TODO Patch DW
|
||||
}
|
||||
|
||||
void post_load() override
|
||||
{
|
||||
utils::hook(SELECT_VALUE(0x6F40A0, 0x6EE1C0, 0x611310), bd_logger_stub, HOOK_JUMP).install()->quick();
|
||||
}
|
||||
|
||||
private:
|
||||
static void bd_logger_stub(int /*type*/, const char* const /*channelName*/, const char*, const char* const /*file*/,
|
||||
const char* const function, const unsigned int /*line*/, const char* const msg, ...)
|
||||
{
|
||||
char buffer[2048];
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, msg);
|
||||
|
||||
vsnprintf_s(buffer, sizeof(buffer), _TRUNCATE, msg, ap);
|
||||
printf("%s: %s\n", function, buffer);
|
||||
|
||||
va_end(ap);
|
||||
}
|
||||
};
|
||||
|
||||
REGISTER_MODULE(dw)
|
||||
|
@ -11,6 +11,9 @@
|
||||
#include <Windowsx.h>
|
||||
#include <objidl.h>
|
||||
#include <gdiplus.h>
|
||||
#include <WinSock2.h>
|
||||
#include <corecrt_io.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
// min and max is required by gdi, therefore NOMINMAX won't work
|
||||
#ifdef max
|
||||
@ -24,8 +27,10 @@
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <thread>
|
||||
#include <fstream>
|
||||
#include <utility>
|
||||
#include <filesystem>
|
||||
|
||||
#include <zlib.h>
|
||||
|
@ -259,7 +259,7 @@ namespace utils
|
||||
return compute(data.data(), data.size());
|
||||
}
|
||||
|
||||
unsigned int jenkins_one_at_a_time::compute(const char* key, size_t len)
|
||||
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)
|
||||
|
Loading…
Reference in New Issue
Block a user