#pragma once #include #include #include #include "PSOEncryption.hh" #include "Version.hh" struct PSOCommandHeaderPC { le_uint16_t size; uint8_t command; uint8_t flag; } __packed_ws__(PSOCommandHeaderPC, 4); struct PSOCommandHeaderDCV3 { uint8_t command; uint8_t flag; le_uint16_t size; } __packed_ws__(PSOCommandHeaderDCV3, 4); struct PSOCommandHeaderBB { le_uint16_t size; le_uint16_t command; le_uint32_t flag; } __packed_ws__(PSOCommandHeaderBB, 8); union PSOCommandHeader { PSOCommandHeaderDCV3 dc; PSOCommandHeaderPC pc; PSOCommandHeaderDCV3 gc; PSOCommandHeaderDCV3 xb; PSOCommandHeaderBB bb; uint16_t command(Version version) const; void set_command(Version version, uint16_t command); uint16_t size(Version version) const; void set_size(Version version, uint32_t size); uint32_t flag(Version version) const; void set_flag(Version version, uint32_t flag); static inline size_t header_size(Version version) { return (version == Version::BB_V4) ? 8 : 4; } PSOCommandHeader(); } __packed_ws__(PSOCommandHeader, 8); // This function is used in a lot of places to check received command sizes and cast them to the appropriate type template RetT& check_size_generic(PtrT data, size_t size, size_t min_size, size_t max_size) { if (size < min_size) { throw std::runtime_error(std::format( "command too small (expected at least 0x{:X} bytes, received 0x{:X} bytes)", min_size, size)); } if (size > max_size) { throw std::runtime_error(std::format( "command too large (expected at most 0x{:X} bytes, received 0x{:X} bytes)", max_size, size)); } return *reinterpret_cast(data); } template const T& check_size_t(const std::string& data, size_t min_size, size_t max_size) { return check_size_generic(data.data(), data.size(), min_size, max_size); } template const T& check_size_t(const std::string& data, size_t max_size) { return check_size_generic(data.data(), data.size(), sizeof(T), max_size); } template const T& check_size_t(const std::string& data) { return check_size_generic(data.data(), data.size(), sizeof(T), sizeof(T)); } template T& check_size_t(std::string& data, size_t min_size, size_t max_size) { return check_size_generic(data.data(), data.size(), min_size, max_size); } template T& check_size_t(std::string& data, size_t max_size) { return check_size_generic(data.data(), data.size(), sizeof(T), max_size); } template T& check_size_t(std::string& data) { return check_size_generic(data.data(), data.size(), sizeof(T), sizeof(T)); } template const T& check_size_t(const void* data, size_t size, size_t min_size, size_t max_size) { return check_size_generic(data, size, min_size, max_size); } template const T& check_size_t(const void* data, size_t size, size_t max_size) { return check_size_generic(data, size, sizeof(T), max_size); } template const T& check_size_t(const void* data, size_t size) { return check_size_generic(data, size, sizeof(T), sizeof(T)); } template T& check_size_t(void* data, size_t size, size_t min_size, size_t max_size) { return check_size_generic(data, size, min_size, max_size); } template T& check_size_t(void* data, size_t size, size_t max_size) { return check_size_generic(data, size, sizeof(T), max_size); } template T& check_size_t(void* data, size_t size) { return check_size_generic(data, size, sizeof(T), sizeof(T)); } template T* check_size_vec_t(std::string& data, size_t count, bool allow_extra = false) { size_t expected_size = count * sizeof(T); return &check_size_generic(data.data(), data.size(), expected_size, allow_extra ? 0xFFFF : expected_size); } void check_size_v(size_t size, size_t min_size, size_t max_size = 0); std::string prepend_command_header( Version version, bool encryption_enabled, uint16_t cmd, uint32_t flag, const std::string& data);