From 7449d16c7e76e81d60f821d0974f5119785c5ea4 Mon Sep 17 00:00:00 2001 From: Moydow <28638419+Moydow@users.noreply.github.com> Date: Sun, 12 Jun 2022 21:44:11 +0100 Subject: [PATCH] Oodle support for 6.15 Requires the oodle2base.h and oodle2net.h header files, and oo2net_9_win64.dll, to be placed in /deps/Oodle/ - they are not included here --- CMakeLists.txt | 2 ++ deps/Oodle/CMakeLists.txt | 9 +++++ src/common/CMakeLists.txt | 4 ++- src/common/Network/CommonNetwork.h | 12 ++++--- src/common/Network/GamePacketParser.cpp | 47 +++++++++++++++++++++++-- src/common/Network/GamePacketParser.h | 7 ++++ src/common/Network/Oodle.cpp | 28 +++++++++++++++ src/common/Network/Oodle.h | 28 +++++++++++++++ 8 files changed, 130 insertions(+), 7 deletions(-) create mode 100644 deps/Oodle/CMakeLists.txt create mode 100644 src/common/Network/Oodle.cpp create mode 100644 src/common/Network/Oodle.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 76160f3d..6597c437 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,6 +15,7 @@ add_custom_target( copy_runtime_files ALL COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/web ${CMAKE_BINARY_DIR}/bin/web COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/sql_import.sh ${CMAKE_BINARY_DIR}/bin/sql_import.sh COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/bin/data/actions + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/deps/oo2net_9_win64.dll ${CMAKE_BINARY_DIR}/bin/oo2net_9_win64.dll COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/deps/ffxiv-actions/actions ${CMAKE_BINARY_DIR}/bin/data/actions ) ###################################### @@ -43,6 +44,7 @@ find_package( MySQL ) ############################## add_subdirectory( "deps/zlib" ) add_subdirectory( "deps/MySQL" ) +add_subdirectory( "deps/Oodle" ) add_subdirectory( "deps/datReader" ) add_subdirectory( "deps/mysqlConnector" ) add_subdirectory( "deps/recastnavigation" ) diff --git a/deps/Oodle/CMakeLists.txt b/deps/Oodle/CMakeLists.txt new file mode 100644 index 00000000..ef90dcbc --- /dev/null +++ b/deps/Oodle/CMakeLists.txt @@ -0,0 +1,9 @@ +add_library(OodleNet STATIC IMPORTED GLOBAL) + +set_target_properties( + OodleNet + PROPERTIES + IMPORTED_IMPLIB "${CMAKE_SOURCE_DIR}/deps/Oodle/oo2net_9_win64.lib" + IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/deps/Oodle/oo2net_9_win64.lib" + LINKER_LANGUAGE C +) \ No newline at end of file diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 88ab338d..ed7c32bd 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -20,7 +20,8 @@ target_link_libraries( common PUBLIC xivdat mysqlConnector - mysql ) + mysql + OodleNet ) if( UNIX ) target_link_libraries( common PUBLIC @@ -32,6 +33,7 @@ target_include_directories( common PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/" "${CMAKE_CURRENT_SOURCE_DIR}/../../deps/" + "${CMAKE_CURRENT_SOURCE_DIR}/../../deps/Oodle/" "${CMAKE_CURRENT_SOURCE_DIR}/../../deps/asio/asio/include/" "${CMAKE_CURRENT_SOURCE_DIR}/../../deps/spdlog/include/" ) diff --git a/src/common/Network/CommonNetwork.h b/src/common/Network/CommonNetwork.h index 1a2bd4d6..30050164 100644 --- a/src/common/Network/CommonNetwork.h +++ b/src/common/Network/CommonNetwork.h @@ -44,7 +44,7 @@ namespace Sapphire::Network::Packets * +-------------------------------+---------------+-------+-------+ * | timestamp | size | cType | count | * +---+---+-------+---------------+---------------+-------+-------+ - * | ? |CMP| ? | ? | + * | ? |CMP| ? | oodleDcmpSize | * +---+---+-------+---------------+ * (followed by /count/ FFXIVARR_PACKET_SEGMENTs) */ @@ -62,9 +62,13 @@ namespace Sapphire::Network::Packets /** The number of packet segments that follow. */ uint16_t count; uint8_t unknown_20; - /** Indicates if the data segments of this packet are compressed. */ - uint8_t isCompressed; - uint32_t unknown_24; + /** Indicates compression method of the data segments of this packet. + * 0: none, 1: Zlib, 2: Oodle + */ + uint8_t compressionType; + uint16_t unknown_22; + /** The size of the packet payload when decompressed, only used if compressionType = Oodle */ + uint32_t oodleDecompressedSize; }; inline std::ostream& operator<<( std::ostream& os, const FFXIVARR_PACKET_HEADER& hdr ) diff --git a/src/common/Network/GamePacketParser.cpp b/src/common/Network/GamePacketParser.cpp index b14d2657..f16248f2 100644 --- a/src/common/Network/GamePacketParser.cpp +++ b/src/common/Network/GamePacketParser.cpp @@ -1,7 +1,9 @@ #include "CommonNetwork.h" #include "GamePacketParser.h" +#include "Oodle.h" #include // memcpy +#include using namespace Sapphire; using namespace Sapphire::Network::Packets; @@ -49,10 +51,51 @@ PacketParseResult Network::Packets::getPackets( const std::vector< uint8_t >& bu std::vector< FFXIVARR_PACKET_RAW >& packets ) { // sanity check: check there's enough bytes in the buffer - const auto bytesExpected = packetHeader.size - sizeof( struct FFXIVARR_PACKET_HEADER ); + auto bytesExpected = packetHeader.size - sizeof( struct FFXIVARR_PACKET_HEADER ); if( buffer.size() - offset < bytesExpected ) return Incomplete; + std::vector< uint8_t > decompBuf; + + // check compression, do decompress if Oodle/Zlib + if( packetHeader.compressionType == Oodle ) + { + std::vector< uint8_t > inBuf; + inBuf.assign( buffer.begin() + sizeof( struct FFXIVARR_PACKET_HEADER ), buffer.end() ); + + std::vector< uint8_t > outBuf; + outBuf.resize( packetHeader.oodleDecompressedSize ); + + auto _oodle = Network::Oodle(); + bool oodleSuccess = _oodle.oodleDecode( inBuf, bytesExpected, outBuf, packetHeader.oodleDecompressedSize ); + + if( !oodleSuccess ) + { + Logger::warn( "Oodle decompression failed." ); + return Malformed; + } + + bytesExpected = packetHeader.oodleDecompressedSize; + + decompBuf.assign( buffer.begin(), buffer.begin() + sizeof( struct FFXIVARR_PACKET_HEADER ) ); + decompBuf.insert( decompBuf.end(), outBuf.begin(), outBuf.end() ); + } + + else if( packetHeader.compressionType == Zlib ) + { + // to do(?): Zlib decompression should go here, + // but I don't think the client ever sends Zlib packets? So it may not be needed + } + + else if( packetHeader.compressionType == NoCompression ) + decompBuf.assign( buffer.begin(), buffer.end() ); + + else + { + Logger::warn( "Unknown packet compression type: {}", packetHeader.compressionType ); + return Malformed; + } + // Loop each message uint32_t count = 0; uint32_t bytesProcessed = 0; @@ -61,7 +104,7 @@ PacketParseResult Network::Packets::getPackets( const std::vector< uint8_t >& bu FFXIVARR_PACKET_RAW rawPacket; // Copy ipc packet message - const auto packetResult = getPacket( buffer, offset + bytesProcessed, rawPacket ); + const auto packetResult = getPacket( decompBuf, offset + bytesProcessed, rawPacket ); if( packetResult != Success ) return packetResult; diff --git a/src/common/Network/GamePacketParser.h b/src/common/Network/GamePacketParser.h index c50b486a..a8083f8b 100644 --- a/src/common/Network/GamePacketParser.h +++ b/src/common/Network/GamePacketParser.h @@ -18,6 +18,13 @@ namespace Sapphire::Network::Packets Malformed }; + enum CompressionType + { + NoCompression, + Zlib, + Oodle, + }; + /// Read packet header from buffer with given offset. /// Buffer with given offset must be pointing to start of the new FFXIV packet. PacketParseResult getHeader( const std::vector< uint8_t >& buffer, const uint32_t offset, diff --git a/src/common/Network/Oodle.cpp b/src/common/Network/Oodle.cpp new file mode 100644 index 00000000..53a2044c --- /dev/null +++ b/src/common/Network/Oodle.cpp @@ -0,0 +1,28 @@ +#include "Oodle.h" + +using namespace Sapphire; + +Network::Oodle::Oodle() : + m_htbits(19) +{ + auto stateSize = OodleNetwork1UDP_State_Size(); + auto sharedSize = OodleNetwork1_Shared_Size( m_htbits ); + + m_state = std::vector< uint8_t >( stateSize, 0 ); + m_shared = std::vector< uint8_t >( sharedSize, 0 ); + m_window = std::vector< uint8_t >( 0x8000, 0 ); + + oodleInit(); +} + +void Network::Oodle::oodleInit() +{ + OodleNetwork1_Shared_SetWindow( (OodleNetwork1_Shared*) &m_shared[0], m_htbits, &m_window[0], (int) m_window.size() ); + OodleNetwork1UDP_Train( (OodleNetwork1UDP_State*) &m_state[0], (OodleNetwork1_Shared*) &m_shared[0], nullptr, nullptr, 0 ); +} + +bool Network::Oodle::oodleDecode( const std::vector< uint8_t > &enc, uint32_t encSize, std::vector< uint8_t > &dec, uint32_t decSize ) +{ + OodleNetwork1_Shared_SetWindow( (OodleNetwork1_Shared*) &m_shared[0], m_htbits, &m_window[0], (int) m_window.size() ); + return OodleNetwork1UDP_Decode( (OodleNetwork1UDP_State*) &m_state[0], (OodleNetwork1_Shared*) &m_shared[0], &enc[0], encSize, &dec[0], decSize ); +} \ No newline at end of file diff --git a/src/common/Network/Oodle.h b/src/common/Network/Oodle.h new file mode 100644 index 00000000..b01dcf29 --- /dev/null +++ b/src/common/Network/Oodle.h @@ -0,0 +1,28 @@ +#ifndef _OODLE_H +#define _OODLE_H + +#include + +#include + +namespace Sapphire::Network +{ + class Oodle + { + private: + std::vector< uint8_t > m_state; + std::vector< uint8_t > m_shared; + std::vector< uint8_t > m_window; + + int m_htbits; + + public: + Oodle(); + virtual ~Oodle() = default; + + void oodleInit(); + bool oodleDecode( const std::vector< uint8_t > &enc, uint32_t encSize, std::vector< uint8_t > &dec, uint32_t decSize ); + }; +} + +#endif // _OODLE_H \ No newline at end of file