#pragma once #include #include #include #include #include "Version.hh" #include "PSOEncryption.hh" struct PSOCommandHeaderPC { le_uint16_t size; uint8_t command; uint8_t flag; } __attribute__((packed)); struct PSOCommandHeaderDCGC { uint8_t command; uint8_t flag; le_uint16_t size; } __attribute__((packed)); struct PSOCommandHeaderBB { le_uint16_t size; le_uint16_t command; le_uint32_t flag; } __attribute__((packed)); union PSOCommandHeader { PSOCommandHeaderDCGC dc; PSOCommandHeaderPC pc; PSOCommandHeaderDCGC gc; PSOCommandHeaderBB bb; uint16_t command(GameVersion version) const; void set_command(GameVersion version, uint16_t command); uint16_t size(GameVersion version) const; void set_size(GameVersion version, uint32_t size); uint32_t flag(GameVersion version) const; void set_flag(GameVersion version, uint32_t flag); static inline size_t header_size(GameVersion version) { return (version == GameVersion::BB) ? 8 : 4; } PSOCommandHeader(); } __attribute__((packed)); union PSOSubcommand { uint8_t byte[4]; le_uint16_t word[2]; le_uint32_t dword; } __attribute__((packed)); void for_each_received_command( struct bufferevent* bev, GameVersion version, PSOEncryption* crypt, std::function fn); void print_received_command( uint16_t command, uint32_t flag, const void* data, size_t size, GameVersion version, const char* name = nullptr, TerminalFormat color = TerminalFormat::FG_GREEN); // This function is used in a lot of places to check received command sizes and // cast them to the appropriate type template const T& check_size_t( const std::string& data, size_t min_size = sizeof(T), size_t max_size = sizeof(T)) { if (data.size() < min_size) { throw std::runtime_error(string_printf( "command too small (expected at least 0x%zX bytes, received 0x%zX bytes)", min_size, data.size())); } if (data.size() > max_size) { throw std::runtime_error(string_printf( "command too large (expected at most 0x%zX bytes, received 0x%zX bytes)", max_size, data.size())); } return *reinterpret_cast(data.data()); } template T& check_size_t( std::string& data, size_t min_size = sizeof(T), size_t max_size = sizeof(T)) { if (data.size() < min_size) { throw std::runtime_error(string_printf( "command too small (expected at least 0x%zX bytes, received 0x%zX bytes)", min_size, data.size())); } if (data.size() > max_size) { throw std::runtime_error(string_printf( "command too large (expected at most 0x%zX bytes, received 0x%zX bytes)", max_size, data.size())); } return *reinterpret_cast(data.data()); } void check_size_v(size_t size, size_t min_size, size_t max_size = 0);