diff --git a/common/eq_dictionary.h b/common/eq_dictionary.h index 43511072f..3438f133b 100644 --- a/common/eq_dictionary.h +++ b/common/eq_dictionary.h @@ -149,6 +149,8 @@ public: static const uint32 BANDOLIER_SIZE = Titanium::consts::BANDOLIER_SIZE; // size = number of equipment slots in bandolier instance static const uint32 POTION_BELT_SIZE = Titanium::consts::POTION_BELT_SIZE; + static const size_t TEXT_LINK_BODY_LENGTH = 56; + // legacy-related functions //static int ServerToPerlSlot(int slot); // encode //static int PerlToServerSlot(int slot); // decode diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index 07daa97fd..32f7360fb 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -5269,6 +5269,23 @@ struct ClientMarqueeMessage_Struct { typedef std::list ItemList; +struct TextLinkBody_Struct { + uint8 unknown_1; /* '%1X' - Server field 1 */ + uint32 item_id; /* '%05X' - Server field 2 */ + uint32 augment_1; /* '%05X' - Server field 3 */ + uint32 augment_2; /* '%05X' - Server field 4 */ + uint32 augment_3; /* '%05X' - Server field 5 */ + uint32 augment_4; /* '%05X' - Server field 6 */ + uint32 augment_5; /* '%05X' - Server field 7 */ + uint32 augment_6; /* '%05X' - Server field 8 */ + uint8 unknown_2; /* '%1X' - Server field 9 */ + uint8 unknown_3; /* '%1X' - Server field 10 */ + uint32 unknown_4; /* '%04X' - Server field 11 */ + uint8 unknown_5; /* '%1X' - Server field 12 */ + uint32 ornament_icon; /* '%05X' - Server field 13 */ + int hash; /* '%08X' - Server field 14 */ +}; + // Restore structure packing to default #pragma pack() diff --git a/common/patches/rof.cpp b/common/patches/rof.cpp index 36d487545..d236d2b34 100644 --- a/common/patches/rof.cpp +++ b/common/patches/rof.cpp @@ -33,6 +33,18 @@ namespace RoF static inline uint32 RoFToServerMainInvSlot(structs::MainInvItemSlotStruct RoFSlot); static inline uint32 RoFToServerCorpseSlot(uint32 RoFCorpse); + // server to client text link converters + static inline void ServerToRoFTextLinks(std::string& rofTextLink, const std::string& serverTextLink); + static inline bool DegenerateServerTextLinkBody(TextLinkBody_Struct& serverLinkBodyStruct, const std::string& serverLinkBody); + static inline void ServerToRoFTextLinkBodyStruct(structs::TextLinkBody_Struct& rofLinkBodyStruct, const TextLinkBody_Struct& serverLinkBodyStruct); + static inline bool GenerateRoFTextLinkBody(std::string& rofLinkBody, const structs::TextLinkBody_Struct& rofLinkBodyStruct); + + // client to server text link converters + static inline void RoFToServerTextLinks(std::string& serverTextLink, const std::string& rofTextLink); + static inline bool DegenerateRoFTextLinkBody(structs::TextLinkBody_Struct& rofLinkBodyStruct, const std::string& rofLinkBody); + static inline void RoFToServerTextLinkBodyStruct(TextLinkBody_Struct& serverLinkBodyStruct, const structs::TextLinkBody_Struct& rofLinkBodyStruct); + static inline bool GenerateServerTextLinkBody(std::string& serverLinkBody, const TextLinkBody_Struct& serverLinkBodyStruct); + void Register(EQStreamIdentifier &into) { //create our opcode manager if we havent already @@ -488,7 +500,13 @@ namespace RoF unsigned char *__emu_buffer = in->pBuffer; - in->size = strlen(emu->sender) + 1 + strlen(emu->targetname) + 1 + strlen(emu->message) + 1 + 36; + std::string old_message = emu->message; + std::string new_message; + ServerToRoFTextLinks(new_message, old_message); + + //in->size = strlen(emu->sender) + 1 + strlen(emu->targetname) + 1 + strlen(emu->message) + 1 + 36; + in->size = strlen(emu->sender) + strlen(emu->targetname) + new_message.length() + 39; + in->pBuffer = new unsigned char[in->size]; char *OutBuffer = (char *)in->pBuffer; @@ -501,7 +519,7 @@ namespace RoF VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, 0); // Unknown VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->skill_in_language); - VARSTRUCT_ENCODE_STRING(OutBuffer, emu->message); + VARSTRUCT_ENCODE_STRING(OutBuffer, new_message.c_str()); VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown @@ -3096,6 +3114,44 @@ namespace RoF FINISH_ENCODE(); } + ENCODE(OP_SpecialMesg) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + SpecialMesg_Struct *emu = (SpecialMesg_Struct *)in->pBuffer; + + unsigned char *__emu_buffer = in->pBuffer; + + std::string old_message = &emu->message[strlen(emu->sayer)]; + std::string new_message; + ServerToRoFTextLinks(new_message, old_message); + + //in->size = 3 + 4 + 4 + strlen(emu->sayer) + 1 + 12 + new_message.length() + 1; + in->size = 25 + strlen(emu->sayer) + new_message.length(); + in->pBuffer = new unsigned char[in->size]; + + char *OutBuffer = (char *)in->pBuffer; + + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[0]); + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[1]); + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[2]); + + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->msg_type); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->target_spawn_id); + + VARSTRUCT_ENCODE_STRING(OutBuffer, emu->sayer); + + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); + + VARSTRUCT_ENCODE_STRING(OutBuffer, new_message.c_str()); + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + ENCODE(OP_Stun) { ENCODE_LENGTH_EXACT(Stun_Struct); @@ -4041,7 +4097,13 @@ namespace RoF uint32 Skill = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); - __packet->size = sizeof(ChannelMessage_Struct)+strlen(InBuffer) + 1; + std::string old_message = InBuffer; + std::string new_message; + RoFToServerTextLinks(new_message, old_message); + + //__packet->size = sizeof(ChannelMessage_Struct)+strlen(InBuffer) + 1; + __packet->size = sizeof(ChannelMessage_Struct) + new_message.length() + 1; + __packet->pBuffer = new unsigned char[__packet->size]; ChannelMessage_Struct *emu = (ChannelMessage_Struct *)__packet->pBuffer; @@ -4050,7 +4112,7 @@ namespace RoF emu->language = Language; emu->chan_num = Channel; emu->skill_in_language = Skill; - strcpy(emu->message, InBuffer); + strcpy(emu->message, new_message.c_str()); delete[] __eq_buffer; } @@ -5627,5 +5689,365 @@ namespace RoF { return (RoFCorpse - 1); } + + static inline void ServerToRoFTextLinks(std::string& rofTextLink, const std::string& serverTextLink) + { + const char delimiter = 0x12; + +#if EQDEBUG >= 6 + _log(CHANNELS__ERROR, "TextLink(Server->RoF): old message '%s'", serverTextLink.c_str()); + + if (consts::TEXT_LINK_BODY_LENGTH == EmuConstants::TEXT_LINK_BODY_LENGTH) { + _log(CHANNELS__ERROR, "TextLink(Server->RoF): link size equal, no conversion necessary"); + rofTextLink = serverTextLink; + return; + } + + if (serverTextLink.find(delimiter) == std::string::npos) { + _log(CHANNELS__ERROR, "TextLink(Server->RoF): delimiter not found, no conversion necessary"); + rofTextLink = serverTextLink; + return; + } + + bool conversion_error = false; + auto segments = SplitString(serverTextLink, delimiter); + + for (size_t iter = 1; iter < segments.size(); iter += 2) { + TextLinkBody_Struct old_body_data; + if (!DegenerateServerTextLinkBody(old_body_data, segments[iter].substr(0, EmuConstants::TEXT_LINK_BODY_LENGTH).c_str())) { + _log(CHANNELS__ERROR, "TextLink(Server->RoF): body degeneration error '%s'", segments[iter].substr(0, EmuConstants::TEXT_LINK_BODY_LENGTH).c_str()); + conversion_error = true; + } + else { + _log(CHANNELS__ERROR, "TextLink(Server->RoF): body degeneration success '%s'", segments[iter].substr(0, EmuConstants::TEXT_LINK_BODY_LENGTH).c_str()); + } + + structs::TextLinkBody_Struct new_body_data; + ServerToRoFTextLinkBodyStruct(new_body_data, old_body_data); + + std::string segment; + if (!GenerateRoFTextLinkBody(segment, new_body_data)) { + _log(CHANNELS__ERROR, "TextLink(Server->RoF): body generation error '%s'", segment.c_str()); + conversion_error = true; + } + else { + _log(CHANNELS__ERROR, "TextLink(Server->RoF): body generation success '%s'", segment.c_str()); + segment.append(segments[iter].substr(EmuConstants::TEXT_LINK_BODY_LENGTH).c_str()); + segments[iter] = segment.c_str(); + } + } + + if (conversion_error) { + _log(CHANNELS__ERROR, "TextLink(Server->RoF): conversion error"); + rofTextLink = serverTextLink; + return; + } + + for (size_t iter = 0; iter < segments.size(); ++iter) { + if (iter & 1) { + rofTextLink.push_back(delimiter); + rofTextLink.append(segments[iter].c_str()); + rofTextLink.push_back(delimiter); + } + else { + rofTextLink.append(segments[iter].c_str()); + } + + _log(CHANNELS__ERROR, "TextLink(Server->RoF): segment[%u] '%s'", iter, segments[iter].c_str()); + } + + _log(CHANNELS__ERROR, "TextLink(Server->RoF): new message '%s'", rofTextLink.c_str()); +#else + if ((consts::TEXT_LINK_BODY_LENGTH == EmuConstants::TEXT_LINK_BODY_LENGTH) || (serverTextLink.find(delimiter) == std::string::npos)) { + rofTextLink = serverTextLink; + return; + } + + bool conversion_error = false; + auto segments = SplitString(serverTextLink, delimiter); + + for (size_t iter = 1; iter < segments.size(); iter += 2) { + TextLinkBody_Struct old_body_data; + if (!DegenerateServerTextLinkBody(old_body_data, segments[iter].substr(0, EmuConstants::TEXT_LINK_BODY_LENGTH).c_str())) { + conversion_error = true; + break; + } + + structs::TextLinkBody_Struct new_body_data; + ServerToRoFTextLinkBodyStruct(new_body_data, old_body_data); + + std::string segment; + if (!GenerateRoFTextLinkBody(segment, new_body_data)) { + conversion_error = true; + break; + } + else { + segment.append(segments[iter].substr(EmuConstants::TEXT_LINK_BODY_LENGTH).c_str()); + segments[iter] = segment.c_str(); + } + } + + if (conversion_error) { + _log(CHANNELS__ERROR, "TextLink(Server->RoF): conversion error"); + rofTextLink = serverTextLink; + return; + } + + for (size_t iter = 0; iter < segments.size(); ++iter) { + if (iter & 1) { + rofTextLink.push_back(delimiter); + rofTextLink.append(segments[iter].c_str()); + rofTextLink.push_back(delimiter); + } + else { + rofTextLink.append(segments[iter].c_str()); + } + } +#endif + } + + static inline bool DegenerateServerTextLinkBody(TextLinkBody_Struct& serverLinkBodyStruct, const std::string& serverLinkBody) + { + memset(&serverLinkBodyStruct, 0, sizeof(TextLinkBody_Struct)); + if (serverLinkBody.length() != EmuConstants::TEXT_LINK_BODY_LENGTH) { return false; } + + serverLinkBodyStruct.unknown_1 = (uint8)strtol(serverLinkBody.substr(0, 1).c_str(), nullptr, 16); + serverLinkBodyStruct.item_id = (uint32)strtol(serverLinkBody.substr(1, 5).c_str(), nullptr, 16); + serverLinkBodyStruct.augment_1 = (uint32)strtol(serverLinkBody.substr(6, 5).c_str(), nullptr, 16); + serverLinkBodyStruct.augment_2 = (uint32)strtol(serverLinkBody.substr(11, 5).c_str(), nullptr, 16); + serverLinkBodyStruct.augment_3 = (uint32)strtol(serverLinkBody.substr(16, 5).c_str(), nullptr, 16); + serverLinkBodyStruct.augment_4 = (uint32)strtol(serverLinkBody.substr(21, 5).c_str(), nullptr, 16); + serverLinkBodyStruct.augment_5 = (uint32)strtol(serverLinkBody.substr(26, 5).c_str(), nullptr, 16); + serverLinkBodyStruct.augment_6 = (uint32)strtol(serverLinkBody.substr(31, 5).c_str(), nullptr, 16); + serverLinkBodyStruct.unknown_2 = (uint8)strtol(serverLinkBody.substr(36, 1).c_str(), nullptr, 16); + serverLinkBodyStruct.unknown_3 = (uint8)strtol(serverLinkBody.substr(37, 1).c_str(), nullptr, 16); + serverLinkBodyStruct.unknown_4 = (uint32)strtol(serverLinkBody.substr(38, 4).c_str(), nullptr, 16); + serverLinkBodyStruct.unknown_5 = (uint8)strtol(serverLinkBody.substr(42, 1).c_str(), nullptr, 16); + serverLinkBodyStruct.ornament_icon = (uint32)strtol(serverLinkBody.substr(43, 5).c_str(), nullptr, 16); + serverLinkBodyStruct.hash = (int)strtol(serverLinkBody.substr(48, 8).c_str(), nullptr, 16); + + return true; + } + + static inline void ServerToRoFTextLinkBodyStruct(structs::TextLinkBody_Struct& rofLinkBodyStruct, const TextLinkBody_Struct& serverLinkBodyStruct) + { + rofLinkBodyStruct.unknown_1 = serverLinkBodyStruct.unknown_1; + rofLinkBodyStruct.item_id = serverLinkBodyStruct.item_id; + rofLinkBodyStruct.augment_1 = serverLinkBodyStruct.augment_1; + rofLinkBodyStruct.augment_2 = serverLinkBodyStruct.augment_2; + rofLinkBodyStruct.augment_3 = serverLinkBodyStruct.augment_3; + rofLinkBodyStruct.augment_4 = serverLinkBodyStruct.augment_4; + rofLinkBodyStruct.augment_5 = serverLinkBodyStruct.augment_5; + rofLinkBodyStruct.augment_6 = serverLinkBodyStruct.augment_6; + rofLinkBodyStruct.unknown_2 = serverLinkBodyStruct.unknown_3; + rofLinkBodyStruct.unknown_3 = serverLinkBodyStruct.unknown_4; + rofLinkBodyStruct.unknown_4 = serverLinkBodyStruct.unknown_5; + rofLinkBodyStruct.ornament_icon = serverLinkBodyStruct.ornament_icon; + rofLinkBodyStruct.hash = serverLinkBodyStruct.hash; + } + + static inline bool GenerateRoFTextLinkBody(std::string& rofLinkBody, const structs::TextLinkBody_Struct& rofLinkBodyStruct) + { + rofLinkBody = StringFormat( + "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%1X" "%05X" "%08X", + rofLinkBodyStruct.unknown_1, + rofLinkBodyStruct.item_id, + rofLinkBodyStruct.augment_1, + rofLinkBodyStruct.augment_2, + rofLinkBodyStruct.augment_3, + rofLinkBodyStruct.augment_4, + rofLinkBodyStruct.augment_5, + rofLinkBodyStruct.augment_6, + rofLinkBodyStruct.unknown_2, + rofLinkBodyStruct.unknown_3, + rofLinkBodyStruct.unknown_4, + rofLinkBodyStruct.ornament_icon, + rofLinkBodyStruct.hash + ); + + if (rofLinkBody.length() != consts::TEXT_LINK_BODY_LENGTH) { return false; } + return true; + } + + static inline void RoFToServerTextLinks(std::string& serverTextLink, const std::string& rofTextLink) + { + const char delimiter = 0x12; + +#if EQDEBUG >= 6 + _log(CHANNELS__ERROR, "TextLink(RoF->Server): old message '%s'", rofTextLink.c_str()); + + if (EmuConstants::TEXT_LINK_BODY_LENGTH == consts::TEXT_LINK_BODY_LENGTH) { + _log(CHANNELS__ERROR, "TextLink(RoF->Server): link size equal, no conversion necessary"); + serverTextLink = rofTextLink; + return; + } + + if (rofTextLink.find(delimiter) == std::string::npos) { + _log(CHANNELS__ERROR, "TextLink(RoF->Server): delimiter not found, no conversion necessary"); + serverTextLink = rofTextLink; + return; + } + + bool conversion_error = false; + auto segments = SplitString(rofTextLink, delimiter); + + for (size_t iter = 1; iter < segments.size(); iter += 2) { + structs::TextLinkBody_Struct old_body_data; + if (!DegenerateRoFTextLinkBody(old_body_data, segments[iter].substr(0, consts::TEXT_LINK_BODY_LENGTH).c_str())) { + _log(CHANNELS__ERROR, "TextLink(RoF->Server): body degeneration error '%s'", segments[iter].substr(0, consts::TEXT_LINK_BODY_LENGTH).c_str()); + conversion_error = true; + } + else { + _log(CHANNELS__ERROR, "TextLink(RoF->Server): body degeneration success '%s'", segments[iter].substr(0, consts::TEXT_LINK_BODY_LENGTH).c_str()); + } + + TextLinkBody_Struct new_body_data; + RoFToServerTextLinkBodyStruct(new_body_data, old_body_data); + + std::string segment; + if (!GenerateServerTextLinkBody(segment, new_body_data)) { + _log(CHANNELS__ERROR, "TextLink(RoF->Server): body generation error '%s'", segment.c_str()); + conversion_error = true; + } + else { + _log(CHANNELS__ERROR, "TextLink(RoF->Server): body generation success '%s'", segment.c_str()); + segment.append(segments[iter].substr(consts::TEXT_LINK_BODY_LENGTH).c_str()); + segments[iter] = segment.c_str(); + } + } + + if (conversion_error) { + _log(CHANNELS__ERROR, "TextLink(RoF->Server): conversion error"); + serverTextLink = rofTextLink; + return; + } + + for (size_t iter = 0; iter < segments.size(); ++iter) { + if (iter & 1) { + serverTextLink.push_back(delimiter); + serverTextLink.append(segments[iter].c_str()); + serverTextLink.push_back(delimiter); + } + else { + serverTextLink.append(segments[iter].c_str()); + } + + _log(CHANNELS__ERROR, "TextLink(RoF->Server): segment[%u] '%s'", iter, segments[iter].c_str()); + } + + _log(CHANNELS__ERROR, "TextLink(RoF->Server): new message '%s'", serverTextLink.c_str()); +#else + if ((EmuConstants::TEXT_LINK_BODY_LENGTH == consts::TEXT_LINK_BODY_LENGTH) || (rofTextLink.find(delimiter) == std::string::npos)) { + serverTextLink = rofTextLink; + return; + } + + bool conversion_error = false; + auto segments = SplitString(rofTextLink, delimiter); + + for (size_t iter = 1; iter < segments.size(); iter += 2) { + structs::TextLinkBody_Struct old_body_data; + if (!DegenerateRoFTextLinkBody(old_body_data, segments[iter].substr(0, consts::TEXT_LINK_BODY_LENGTH).c_str())) { + conversion_error = true; + break; + } + + TextLinkBody_Struct new_body_data; + RoFToServerTextLinkBodyStruct(new_body_data, old_body_data); + + std::string segment; + if (!GenerateServerTextLinkBody(segment, new_body_data)) { + conversion_error = true; + break; + } + else { + segment.append(segments[iter].substr(consts::TEXT_LINK_BODY_LENGTH).c_str()); + segments[iter] = segment.c_str(); + } + } + + if (conversion_error) { + _log(CHANNELS__ERROR, "TextLink(RoF->Server): conversion error"); + serverTextLink = rofTextLink; + return; + } + + for (size_t iter = 0; iter < segments.size(); ++iter) { + if (iter & 1) { + serverTextLink.push_back(delimiter); + serverTextLink.append(segments[iter].c_str()); + serverTextLink.push_back(delimiter); + } + else { + serverTextLink.append(segments[iter].c_str()); + } + } +#endif + } + + static inline bool DegenerateRoFTextLinkBody(structs::TextLinkBody_Struct& rofLinkBodyStruct, const std::string& rofLinkBody) + { + // RoF: "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%1X" "%05X" "%08X" + memset(&rofLinkBodyStruct, 0, sizeof(structs::TextLinkBody_Struct)); + if (rofLinkBody.length() != consts::TEXT_LINK_BODY_LENGTH) { return false; } + + rofLinkBodyStruct.unknown_1 = (uint8)strtol(rofLinkBody.substr(0, 1).c_str(), nullptr, 16); + rofLinkBodyStruct.item_id = (uint32)strtol(rofLinkBody.substr(1, 5).c_str(), nullptr, 16); + rofLinkBodyStruct.augment_1 = (uint32)strtol(rofLinkBody.substr(6, 5).c_str(), nullptr, 16); + rofLinkBodyStruct.augment_2 = (uint32)strtol(rofLinkBody.substr(11, 5).c_str(), nullptr, 16); + rofLinkBodyStruct.augment_3 = (uint32)strtol(rofLinkBody.substr(16, 5).c_str(), nullptr, 16); + rofLinkBodyStruct.augment_4 = (uint32)strtol(rofLinkBody.substr(21, 5).c_str(), nullptr, 16); + rofLinkBodyStruct.augment_5 = (uint32)strtol(rofLinkBody.substr(26, 5).c_str(), nullptr, 16); + rofLinkBodyStruct.augment_6 = (uint32)strtol(rofLinkBody.substr(31, 5).c_str(), nullptr, 16); + rofLinkBodyStruct.unknown_2 = (uint8)strtol(rofLinkBody.substr(36, 1).c_str(), nullptr, 16); + rofLinkBodyStruct.unknown_3 = (uint32)strtol(rofLinkBody.substr(37, 4).c_str(), nullptr, 16); + rofLinkBodyStruct.unknown_4 = (uint8)strtol(rofLinkBody.substr(41, 1).c_str(), nullptr, 16); + rofLinkBodyStruct.ornament_icon = (uint32)strtol(rofLinkBody.substr(42, 5).c_str(), nullptr, 16); + rofLinkBodyStruct.hash = (int)strtol(rofLinkBody.substr(47, 8).c_str(), nullptr, 16); + + return true; + } + + static inline void RoFToServerTextLinkBodyStruct(TextLinkBody_Struct& serverLinkBodyStruct, const structs::TextLinkBody_Struct& rofLinkBodyStruct) + { + serverLinkBodyStruct.unknown_1 = rofLinkBodyStruct.unknown_1; + serverLinkBodyStruct.item_id = rofLinkBodyStruct.item_id; + serverLinkBodyStruct.augment_1 = rofLinkBodyStruct.augment_1; + serverLinkBodyStruct.augment_2 = rofLinkBodyStruct.augment_2; + serverLinkBodyStruct.augment_3 = rofLinkBodyStruct.augment_3; + serverLinkBodyStruct.augment_4 = rofLinkBodyStruct.augment_4; + serverLinkBodyStruct.augment_5 = rofLinkBodyStruct.augment_5; + serverLinkBodyStruct.augment_6 = rofLinkBodyStruct.augment_6; + serverLinkBodyStruct.unknown_2 = NOT_USED; + serverLinkBodyStruct.unknown_3 = rofLinkBodyStruct.unknown_2; + serverLinkBodyStruct.unknown_4 = rofLinkBodyStruct.unknown_3; + serverLinkBodyStruct.unknown_5 = rofLinkBodyStruct.unknown_4; + serverLinkBodyStruct.ornament_icon = rofLinkBodyStruct.ornament_icon; + serverLinkBodyStruct.hash = rofLinkBodyStruct.hash; + } + + static inline bool GenerateServerTextLinkBody(std::string& serverLinkBody, const TextLinkBody_Struct& serverLinkBodyStruct) + { + serverLinkBody = StringFormat( + "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%01X" "%01X" "%04X" "%01X" "%05X" "%08X", + serverLinkBodyStruct.unknown_1, + serverLinkBodyStruct.item_id, + serverLinkBodyStruct.augment_1, + serverLinkBodyStruct.augment_2, + serverLinkBodyStruct.augment_3, + serverLinkBodyStruct.augment_4, + serverLinkBodyStruct.augment_5, + serverLinkBodyStruct.augment_6, + serverLinkBodyStruct.unknown_2, + serverLinkBodyStruct.unknown_3, + serverLinkBodyStruct.unknown_4, + serverLinkBodyStruct.unknown_5, + serverLinkBodyStruct.ornament_icon, + serverLinkBodyStruct.hash + ); + + if (serverLinkBody.length() != EmuConstants::TEXT_LINK_BODY_LENGTH) { return false; } + return true; + } } // end namespace RoF diff --git a/common/patches/rof2.cpp b/common/patches/rof2.cpp index 02ac289a9..6356a6f7d 100644 --- a/common/patches/rof2.cpp +++ b/common/patches/rof2.cpp @@ -33,6 +33,18 @@ namespace RoF2 static inline uint32 RoF2ToServerMainInvSlot(structs::MainInvItemSlotStruct RoF2Slot); static inline uint32 RoF2ToServerCorpseSlot(uint32 RoF2Corpse); + // server to client text link converters + static inline void ServerToRoF2TextLinks(std::string& rof2TextLink, const std::string& serverTextLink); + static inline bool DegenerateServerTextLinkBody(TextLinkBody_Struct& serverLinkBodyStruct, const std::string& serverLinkBody); + static inline void ServerToRoF2TextLinkBodyStruct(structs::TextLinkBody_Struct& rof2LinkBodyStruct, const TextLinkBody_Struct& serverLinkBodyStruct); + static inline bool GenerateRoF2TextLinkBody(std::string& rof2LinkBody, const structs::TextLinkBody_Struct& rof2LinkBodyStruct); + + // client to server text link converters + static inline void RoF2ToServerTextLinks(std::string& serverTextLink, const std::string& rof2TextLink); + static inline bool DegenerateRoF2TextLinkBody(structs::TextLinkBody_Struct& rof2LinkBodyStruct, const std::string& rof2LinkBody); + static inline void RoF2ToServerTextLinkBodyStruct(TextLinkBody_Struct& serverLinkBodyStruct, const structs::TextLinkBody_Struct& rof2LinkBodyStruct); + static inline bool GenerateServerTextLinkBody(std::string& serverLinkBody, const TextLinkBody_Struct& serverLinkBodyStruct); + void Register(EQStreamIdentifier &into) { //create our opcode manager if we havent already @@ -554,7 +566,13 @@ namespace RoF2 unsigned char *__emu_buffer = in->pBuffer; - in->size = strlen(emu->sender) + 1 + strlen(emu->targetname) + 1 + strlen(emu->message) + 1 + 36; + std::string old_message = emu->message; + std::string new_message; + ServerToRoF2TextLinks(new_message, old_message); + + //in->size = strlen(emu->sender) + 1 + strlen(emu->targetname) + 1 + strlen(emu->message) + 1 + 36; + in->size = strlen(emu->sender) + strlen(emu->targetname) + new_message.length() + 39; + in->pBuffer = new unsigned char[in->size]; char *OutBuffer = (char *)in->pBuffer; @@ -567,7 +585,7 @@ namespace RoF2 VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, 0); // Unknown VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->skill_in_language); - VARSTRUCT_ENCODE_STRING(OutBuffer, emu->message); + VARSTRUCT_ENCODE_STRING(OutBuffer, new_message.c_str()); VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown @@ -3162,6 +3180,44 @@ namespace RoF2 FINISH_ENCODE(); } + ENCODE(OP_SpecialMesg) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + SpecialMesg_Struct *emu = (SpecialMesg_Struct *)in->pBuffer; + + unsigned char *__emu_buffer = in->pBuffer; + + std::string old_message = &emu->message[strlen(emu->sayer)]; + std::string new_message; + ServerToRoF2TextLinks(new_message, old_message); + + //in->size = 3 + 4 + 4 + strlen(emu->sayer) + 1 + 12 + new_message.length() + 1; + in->size = 25 + strlen(emu->sayer) + new_message.length(); + in->pBuffer = new unsigned char[in->size]; + + char *OutBuffer = (char *)in->pBuffer; + + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[0]); + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[1]); + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[2]); + + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->msg_type); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->target_spawn_id); + + VARSTRUCT_ENCODE_STRING(OutBuffer, emu->sayer); + + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); + + VARSTRUCT_ENCODE_STRING(OutBuffer, new_message.c_str()); + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + ENCODE(OP_Stun) { ENCODE_LENGTH_EXACT(Stun_Struct); @@ -4113,7 +4169,13 @@ namespace RoF2 uint32 Skill = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); - __packet->size = sizeof(ChannelMessage_Struct)+strlen(InBuffer) + 1; + std::string old_message = InBuffer; + std::string new_message; + RoF2ToServerTextLinks(new_message, old_message); + + //__packet->size = sizeof(ChannelMessage_Struct)+strlen(InBuffer) + 1; + __packet->size = sizeof(ChannelMessage_Struct) + new_message.length() + 1; + __packet->pBuffer = new unsigned char[__packet->size]; ChannelMessage_Struct *emu = (ChannelMessage_Struct *)__packet->pBuffer; @@ -4122,7 +4184,7 @@ namespace RoF2 emu->language = Language; emu->chan_num = Channel; emu->skill_in_language = Skill; - strcpy(emu->message, InBuffer); + strcpy(emu->message, new_message.c_str()); delete[] __eq_buffer; } @@ -5722,5 +5784,368 @@ namespace RoF2 { return (RoF2Corpse + EmuConstants::CORPSE_BEGIN - 1); } + + static inline void ServerToRoF2TextLinks(std::string& rof2TextLink, const std::string& serverTextLink) + { + const char delimiter = 0x12; + +#if EQDEBUG >= 6 + _log(CHANNELS__ERROR, "TextLink(Server->RoF2): old message '%s'", serverTextLink.c_str()); + + if (consts::TEXT_LINK_BODY_LENGTH == EmuConstants::TEXT_LINK_BODY_LENGTH) { + _log(CHANNELS__ERROR, "TextLink(Server->RoF2): link size equal, no conversion necessary"); + rof2TextLink = serverTextLink; + return; + } + + if (serverTextLink.find(delimiter) == std::string::npos) { + _log(CHANNELS__ERROR, "TextLink(Server->RoF2): delimiter not found, no conversion necessary"); + rof2TextLink = serverTextLink; + return; + } + + bool conversion_error = false; + auto segments = SplitString(serverTextLink, delimiter); + + for (size_t iter = 1; iter < segments.size(); iter += 2) { + TextLinkBody_Struct old_body_data; + if (!DegenerateServerTextLinkBody(old_body_data, segments[iter].substr(0, EmuConstants::TEXT_LINK_BODY_LENGTH).c_str())) { + _log(CHANNELS__ERROR, "TextLink(Server->RoF2): body degeneration error '%s'", segments[iter].substr(0, EmuConstants::TEXT_LINK_BODY_LENGTH).c_str()); + conversion_error = true; + } + else { + _log(CHANNELS__ERROR, "TextLink(Server->RoF2): body degeneration success '%s'", segments[iter].substr(0, EmuConstants::TEXT_LINK_BODY_LENGTH).c_str()); + } + + structs::TextLinkBody_Struct new_body_data; + ServerToRoF2TextLinkBodyStruct(new_body_data, old_body_data); + + std::string segment; + if (!GenerateRoF2TextLinkBody(segment, new_body_data)) { + _log(CHANNELS__ERROR, "TextLink(Server->RoF2): body generation error '%s'", segment.c_str()); + conversion_error = true; + } + else { + _log(CHANNELS__ERROR, "TextLink(Server->RoF2): body generation success '%s'", segment.c_str()); + segment.append(segments[iter].substr(EmuConstants::TEXT_LINK_BODY_LENGTH).c_str()); + segments[iter] = segment.c_str(); + } + } + + if (conversion_error) { + _log(CHANNELS__ERROR, "TextLink(Server->RoF2): conversion error"); + rof2TextLink = serverTextLink; + return; + } + + for (size_t iter = 0; iter < segments.size(); ++iter) { + if (iter & 1) { + rof2TextLink.push_back(delimiter); + rof2TextLink.append(segments[iter].c_str()); + rof2TextLink.push_back(delimiter); + } + else { + rof2TextLink.append(segments[iter].c_str()); + } + + _log(CHANNELS__ERROR, "TextLink(Server->RoF2): segment[%u] '%s'", iter, segments[iter].c_str()); + } + + _log(CHANNELS__ERROR, "TextLink(Server->RoF2): new message '%s'", rof2TextLink.c_str()); +#else + if ((consts::TEXT_LINK_BODY_LENGTH == EmuConstants::TEXT_LINK_BODY_LENGTH) || (serverTextLink.find(delimiter) == std::string::npos)) { + rof2TextLink = serverTextLink; + return; + } + + bool conversion_error = false; + auto segments = SplitString(serverTextLink, delimiter); + + for (size_t iter = 1; iter < segments.size(); iter += 2) { + TextLinkBody_Struct old_body_data; + if (!DegenerateServerTextLinkBody(old_body_data, segments[iter].substr(0, EmuConstants::TEXT_LINK_BODY_LENGTH).c_str())) { + conversion_error = true; + break; + } + + structs::TextLinkBody_Struct new_body_data; + ServerToRoF2TextLinkBodyStruct(new_body_data, old_body_data); + + std::string segment; + if (!GenerateRoF2TextLinkBody(segment, new_body_data)) { + conversion_error = true; + break; + } + else { + segment.append(segments[iter].substr(EmuConstants::TEXT_LINK_BODY_LENGTH).c_str()); + segments[iter] = segment.c_str(); + } + } + + if (conversion_error) { + _log(CHANNELS__ERROR, "TextLink(Server->RoF2): conversion error"); + rof2TextLink = serverTextLink; + return; + } + + for (size_t iter = 0; iter < segments.size(); ++iter) { + if (iter & 1) { + rof2TextLink.push_back(delimiter); + rof2TextLink.append(segments[iter].c_str()); + rof2TextLink.push_back(delimiter); + } + else { + rof2TextLink.append(segments[iter].c_str()); + } + } +#endif + } + + static inline bool DegenerateServerTextLinkBody(TextLinkBody_Struct& serverLinkBodyStruct, const std::string& serverLinkBody) + { + memset(&serverLinkBodyStruct, 0, sizeof(TextLinkBody_Struct)); + if (serverLinkBody.length() != EmuConstants::TEXT_LINK_BODY_LENGTH) { return false; } + + serverLinkBodyStruct.unknown_1 = (uint8)strtol(serverLinkBody.substr(0, 1).c_str(), nullptr, 16); + serverLinkBodyStruct.item_id = (uint32)strtol(serverLinkBody.substr(1, 5).c_str(), nullptr, 16); + serverLinkBodyStruct.augment_1 = (uint32)strtol(serverLinkBody.substr(6, 5).c_str(), nullptr, 16); + serverLinkBodyStruct.augment_2 = (uint32)strtol(serverLinkBody.substr(11, 5).c_str(), nullptr, 16); + serverLinkBodyStruct.augment_3 = (uint32)strtol(serverLinkBody.substr(16, 5).c_str(), nullptr, 16); + serverLinkBodyStruct.augment_4 = (uint32)strtol(serverLinkBody.substr(21, 5).c_str(), nullptr, 16); + serverLinkBodyStruct.augment_5 = (uint32)strtol(serverLinkBody.substr(26, 5).c_str(), nullptr, 16); + serverLinkBodyStruct.augment_6 = (uint32)strtol(serverLinkBody.substr(31, 5).c_str(), nullptr, 16); + serverLinkBodyStruct.unknown_2 = (uint8)strtol(serverLinkBody.substr(36, 1).c_str(), nullptr, 16); + serverLinkBodyStruct.unknown_3 = (uint8)strtol(serverLinkBody.substr(37, 1).c_str(), nullptr, 16); + serverLinkBodyStruct.unknown_4 = (uint32)strtol(serverLinkBody.substr(38, 4).c_str(), nullptr, 16); + serverLinkBodyStruct.unknown_5 = (uint8)strtol(serverLinkBody.substr(42, 1).c_str(), nullptr, 16); + serverLinkBodyStruct.ornament_icon = (uint32)strtol(serverLinkBody.substr(43, 5).c_str(), nullptr, 16); + serverLinkBodyStruct.hash = (int)strtol(serverLinkBody.substr(48, 8).c_str(), nullptr, 16); + + return true; + } + + static inline void ServerToRoF2TextLinkBodyStruct(structs::TextLinkBody_Struct& rof2LinkBodyStruct, const TextLinkBody_Struct& serverLinkBodyStruct) + { + rof2LinkBodyStruct.unknown_1 = serverLinkBodyStruct.unknown_1; + rof2LinkBodyStruct.item_id = serverLinkBodyStruct.item_id; + rof2LinkBodyStruct.augment_1 = serverLinkBodyStruct.augment_1; + rof2LinkBodyStruct.augment_2 = serverLinkBodyStruct.augment_2; + rof2LinkBodyStruct.augment_3 = serverLinkBodyStruct.augment_3; + rof2LinkBodyStruct.augment_4 = serverLinkBodyStruct.augment_4; + rof2LinkBodyStruct.augment_5 = serverLinkBodyStruct.augment_5; + rof2LinkBodyStruct.augment_6 = serverLinkBodyStruct.augment_6; + rof2LinkBodyStruct.unknown_2 = serverLinkBodyStruct.unknown_2; + rof2LinkBodyStruct.unknown_3 = serverLinkBodyStruct.unknown_3; + rof2LinkBodyStruct.unknown_4 = serverLinkBodyStruct.unknown_4; + rof2LinkBodyStruct.unknown_5 = serverLinkBodyStruct.unknown_5; + rof2LinkBodyStruct.ornament_icon = serverLinkBodyStruct.ornament_icon; + rof2LinkBodyStruct.hash = serverLinkBodyStruct.hash; + } + + static inline bool GenerateRoF2TextLinkBody(std::string& rof2LinkBody, const structs::TextLinkBody_Struct& rof2LinkBodyStruct) + { + rof2LinkBody = StringFormat( + "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%1X" "%04X" "%1X" "%05X" "%08X", + rof2LinkBodyStruct.unknown_1, + rof2LinkBodyStruct.item_id, + rof2LinkBodyStruct.augment_1, + rof2LinkBodyStruct.augment_2, + rof2LinkBodyStruct.augment_3, + rof2LinkBodyStruct.augment_4, + rof2LinkBodyStruct.augment_5, + rof2LinkBodyStruct.augment_6, + rof2LinkBodyStruct.unknown_2, + rof2LinkBodyStruct.unknown_3, + rof2LinkBodyStruct.unknown_4, + rof2LinkBodyStruct.unknown_5, + rof2LinkBodyStruct.ornament_icon, + rof2LinkBodyStruct.hash + ); + + if (rof2LinkBody.length() != consts::TEXT_LINK_BODY_LENGTH) { return false; } + return true; + } + + static inline void RoF2ToServerTextLinks(std::string& serverTextLink, const std::string& rof2TextLink) + { + const char delimiter = 0x12; + +#if EQDEBUG >= 6 + _log(CHANNELS__ERROR, "TextLink(RoF2->Server): old message '%s'", rof2TextLink.c_str()); + + if (EmuConstants::TEXT_LINK_BODY_LENGTH == consts::TEXT_LINK_BODY_LENGTH) { + _log(CHANNELS__ERROR, "TextLink(RoF2->Server): link size equal, no conversion necessary"); + serverTextLink = rof2TextLink; + return; + } + + if (rof2TextLink.find(delimiter) == std::string::npos) { + _log(CHANNELS__ERROR, "TextLink(RoF2->Server): delimiter not found, no conversion necessary"); + serverTextLink = rof2TextLink; + return; + } + + bool conversion_error = false; + auto segments = SplitString(rof2TextLink, delimiter); + + for (size_t iter = 1; iter < segments.size(); iter += 2) { + structs::TextLinkBody_Struct old_body_data; + if (!DegenerateRoF2TextLinkBody(old_body_data, segments[iter].substr(0, consts::TEXT_LINK_BODY_LENGTH).c_str())) { + _log(CHANNELS__ERROR, "TextLink(RoF2->Server): body degeneration error '%s'", segments[iter].substr(0, consts::TEXT_LINK_BODY_LENGTH).c_str()); + conversion_error = true; + } + else { + _log(CHANNELS__ERROR, "TextLink(RoF2->Server): body degeneration success '%s'", segments[iter].substr(0, consts::TEXT_LINK_BODY_LENGTH).c_str()); + } + + TextLinkBody_Struct new_body_data; + RoF2ToServerTextLinkBodyStruct(new_body_data, old_body_data); + + std::string segment; + if (!GenerateServerTextLinkBody(segment, new_body_data)) { + _log(CHANNELS__ERROR, "TextLink(RoF2->Server): body generation error '%s'", segment.c_str()); + conversion_error = true; + } + else { + _log(CHANNELS__ERROR, "TextLink(RoF2->Server): body generation success '%s'", segment.c_str()); + segment.append(segments[iter].substr(consts::TEXT_LINK_BODY_LENGTH).c_str()); + segments[iter] = segment.c_str(); + } + } + + if (conversion_error) { + _log(CHANNELS__ERROR, "TextLink(RoF2->Server): conversion error"); + serverTextLink = rof2TextLink; + return; + } + + for (size_t iter = 0; iter < segments.size(); ++iter) { + if (iter & 1) { + serverTextLink.push_back(delimiter); + serverTextLink.append(segments[iter].c_str()); + serverTextLink.push_back(delimiter); + } + else { + serverTextLink.append(segments[iter].c_str()); + } + + _log(CHANNELS__ERROR, "TextLink(RoF2->Server): segment[%u] '%s'", iter, segments[iter].c_str()); + } + + _log(CHANNELS__ERROR, "TextLink(RoF2->Server): new message '%s'", serverTextLink.c_str()); +#else + if ((EmuConstants::TEXT_LINK_BODY_LENGTH == consts::TEXT_LINK_BODY_LENGTH) || (rof2TextLink.find(delimiter) == std::string::npos)) { + serverTextLink = rof2TextLink; + return; + } + + bool conversion_error = false; + auto segments = SplitString(rof2TextLink, delimiter); + + for (size_t iter = 1; iter < segments.size(); iter += 2) { + structs::TextLinkBody_Struct old_body_data; + if (!DegenerateRoF2TextLinkBody(old_body_data, segments[iter].substr(0, consts::TEXT_LINK_BODY_LENGTH).c_str())) { + conversion_error = true; + break; + } + + TextLinkBody_Struct new_body_data; + RoF2ToServerTextLinkBodyStruct(new_body_data, old_body_data); + + std::string segment; + if (!GenerateServerTextLinkBody(segment, new_body_data)) { + conversion_error = true; + break; + } + else { + segment.append(segments[iter].substr(consts::TEXT_LINK_BODY_LENGTH).c_str()); + segments[iter] = segment.c_str(); + } + } + + if (conversion_error) { + _log(CHANNELS__ERROR, "TextLink(RoF2->Server): conversion error"); + serverTextLink = rof2TextLink; + return; + } + + for (size_t iter = 0; iter < segments.size(); ++iter) { + if (iter & 1) { + serverTextLink.push_back(delimiter); + serverTextLink.append(segments[iter].c_str()); + serverTextLink.push_back(delimiter); + } + else { + serverTextLink.append(segments[iter].c_str()); + } + } +#endif + } + + static inline bool DegenerateRoF2TextLinkBody(structs::TextLinkBody_Struct& rof2LinkBodyStruct, const std::string& rof2LinkBody) + { + // RoF2: "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%1X" "%04X" "%1X" "%05X" "%08X" + memset(&rof2LinkBodyStruct, 0, sizeof(structs::TextLinkBody_Struct)); + if (rof2LinkBody.length() != consts::TEXT_LINK_BODY_LENGTH) { return false; } + + rof2LinkBodyStruct.unknown_1 = (uint8)strtol(rof2LinkBody.substr(0, 1).c_str(), nullptr, 16); + rof2LinkBodyStruct.item_id = (uint32)strtol(rof2LinkBody.substr(1, 5).c_str(), nullptr, 16); + rof2LinkBodyStruct.augment_1 = (uint32)strtol(rof2LinkBody.substr(6, 5).c_str(), nullptr, 16); + rof2LinkBodyStruct.augment_2 = (uint32)strtol(rof2LinkBody.substr(11, 5).c_str(), nullptr, 16); + rof2LinkBodyStruct.augment_3 = (uint32)strtol(rof2LinkBody.substr(16, 5).c_str(), nullptr, 16); + rof2LinkBodyStruct.augment_4 = (uint32)strtol(rof2LinkBody.substr(21, 5).c_str(), nullptr, 16); + rof2LinkBodyStruct.augment_5 = (uint32)strtol(rof2LinkBody.substr(26, 5).c_str(), nullptr, 16); + rof2LinkBodyStruct.augment_6 = (uint32)strtol(rof2LinkBody.substr(31, 5).c_str(), nullptr, 16); + rof2LinkBodyStruct.unknown_2 = (uint8)strtol(rof2LinkBody.substr(36, 1).c_str(), nullptr, 16); + rof2LinkBodyStruct.unknown_3 = (uint32)strtol(rof2LinkBody.substr(37, 4).c_str(), nullptr, 16); + rof2LinkBodyStruct.unknown_4 = (uint8)strtol(rof2LinkBody.substr(41, 1).c_str(), nullptr, 16); + rof2LinkBodyStruct.unknown_5 = (uint8)strtol(rof2LinkBody.substr(42, 1).c_str(), nullptr, 16); + rof2LinkBodyStruct.ornament_icon = (uint32)strtol(rof2LinkBody.substr(43, 5).c_str(), nullptr, 16); + rof2LinkBodyStruct.hash = (int)strtol(rof2LinkBody.substr(48, 8).c_str(), nullptr, 16); + + return true; + } + + static inline void RoF2ToServerTextLinkBodyStruct(TextLinkBody_Struct& serverLinkBodyStruct, const structs::TextLinkBody_Struct& rof2LinkBodyStruct) + { + serverLinkBodyStruct.unknown_1 = rof2LinkBodyStruct.unknown_1; + serverLinkBodyStruct.item_id = rof2LinkBodyStruct.item_id; + serverLinkBodyStruct.augment_1 = rof2LinkBodyStruct.augment_1; + serverLinkBodyStruct.augment_2 = rof2LinkBodyStruct.augment_2; + serverLinkBodyStruct.augment_3 = rof2LinkBodyStruct.augment_3; + serverLinkBodyStruct.augment_4 = rof2LinkBodyStruct.augment_4; + serverLinkBodyStruct.augment_5 = rof2LinkBodyStruct.augment_5; + serverLinkBodyStruct.augment_6 = rof2LinkBodyStruct.augment_6; + serverLinkBodyStruct.unknown_2 = rof2LinkBodyStruct.unknown_2; + serverLinkBodyStruct.unknown_3 = rof2LinkBodyStruct.unknown_3; + serverLinkBodyStruct.unknown_4 = rof2LinkBodyStruct.unknown_4; + serverLinkBodyStruct.unknown_5 = rof2LinkBodyStruct.unknown_5; + serverLinkBodyStruct.ornament_icon = rof2LinkBodyStruct.ornament_icon; + serverLinkBodyStruct.hash = rof2LinkBodyStruct.hash; + } + + static inline bool GenerateServerTextLinkBody(std::string& serverLinkBody, const TextLinkBody_Struct& serverLinkBodyStruct) + { + serverLinkBody = StringFormat( + "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%01X" "%01X" "%04X" "%01X" "%05X" "%08X", + serverLinkBodyStruct.unknown_1, + serverLinkBodyStruct.item_id, + serverLinkBodyStruct.augment_1, + serverLinkBodyStruct.augment_2, + serverLinkBodyStruct.augment_3, + serverLinkBodyStruct.augment_4, + serverLinkBodyStruct.augment_5, + serverLinkBodyStruct.augment_6, + serverLinkBodyStruct.unknown_2, + serverLinkBodyStruct.unknown_3, + serverLinkBodyStruct.unknown_4, + serverLinkBodyStruct.unknown_5, + serverLinkBodyStruct.ornament_icon, + serverLinkBodyStruct.hash + ); + + if (serverLinkBody.length() != EmuConstants::TEXT_LINK_BODY_LENGTH) { return false; } + return true; + } } // end namespace RoF2 diff --git a/common/patches/rof2_constants.h b/common/patches/rof2_constants.h index 596ffa7e7..b4cbb15e0 100644 --- a/common/patches/rof2_constants.h +++ b/common/patches/rof2_constants.h @@ -181,6 +181,8 @@ namespace RoF2 { static const uint32 BANDOLIERS_COUNT = 20; // count = number of bandolier instances static const uint32 BANDOLIER_SIZE = 4; // size = number of equipment slots in bandolier instance static const uint32 POTION_BELT_SIZE = 5; + + static const size_t TEXT_LINK_BODY_LENGTH = 56; } namespace limits { diff --git a/common/patches/rof2_ops.h b/common/patches/rof2_ops.h index 79614504d..66b86d756 100644 --- a/common/patches/rof2_ops.h +++ b/common/patches/rof2_ops.h @@ -95,6 +95,7 @@ E(OP_SkillUpdate) E(OP_SomeItemPacketMaybe) E(OP_SpawnAppearance) E(OP_SpawnDoor) +E(OP_SpecialMesg) E(OP_Stun) E(OP_TargetBuffs) E(OP_TaskDescription) diff --git a/common/patches/rof2_structs.h b/common/patches/rof2_structs.h index 9ba71574c..60cf07808 100644 --- a/common/patches/rof2_structs.h +++ b/common/patches/rof2_structs.h @@ -4872,6 +4872,25 @@ struct MercenaryMerchantResponse_Struct { /*0004*/ }; +// RoF2: "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%1X" "%04X" "%1X" "%05X" "%08X" +struct TextLinkBody_Struct +{ + uint8 unknown_1; /* '%1X' - RoF2 field 1 / Server field 1 */ + uint32 item_id; /* '%05X' - RoF2 field 2 / Server field 2 */ + uint32 augment_1; /* '%05X' - RoF2 field 3 / Server field 3 */ + uint32 augment_2; /* '%05X' - RoF2 field 4 / Server field 4 */ + uint32 augment_3; /* '%05X' - RoF2 field 5 / Server field 5 */ + uint32 augment_4; /* '%05X' - RoF2 field 6 / Server field 6 */ + uint32 augment_5; /* '%05X' - RoF2 field 7 / Server field 7 */ + uint32 augment_6; /* '%05X' - RoF2 field 8 / Server field 8 */ + uint8 unknown_2; /* '%1X' - RoF2 field 9 / Server field 9 */ + uint8 unknown_3; /* '%1X' - RoF2 field 10 / Server field 10 */ + uint32 unknown_4; /* '%04X' - RoF2 field 11 / Server field 11 */ + uint8 unknown_5; /* '%1X' - RoF2 field 12 / Server field 12 */ + uint32 ornament_icon; /* '%05X' - RoF2 field 13 / Server field 13 */ + int hash; /* '%08X' - RoF2 field 14 / Server field 14 */ +}; + }; //end namespace structs }; //end namespace RoF2 diff --git a/common/patches/rof_constants.h b/common/patches/rof_constants.h index 97cb25aa5..233a1b36c 100644 --- a/common/patches/rof_constants.h +++ b/common/patches/rof_constants.h @@ -180,6 +180,8 @@ namespace RoF { static const uint32 BANDOLIERS_COUNT = 20; // count = number of bandolier instances static const uint32 BANDOLIER_SIZE = 4; // size = number of equipment slots in bandolier instance static const uint32 POTION_BELT_SIZE = 5; + + static const size_t TEXT_LINK_BODY_LENGTH = 55; } namespace limits { diff --git a/common/patches/rof_ops.h b/common/patches/rof_ops.h index 878870a81..ef97dda99 100644 --- a/common/patches/rof_ops.h +++ b/common/patches/rof_ops.h @@ -84,6 +84,7 @@ E(OP_SkillUpdate) E(OP_SomeItemPacketMaybe) E(OP_SpawnAppearance) E(OP_SpawnDoor) +E(OP_SpecialMesg) E(OP_Stun) E(OP_TargetBuffs) E(OP_TaskDescription) diff --git a/common/patches/rof_structs.h b/common/patches/rof_structs.h index fb2f09615..4aa2c04a2 100644 --- a/common/patches/rof_structs.h +++ b/common/patches/rof_structs.h @@ -4894,6 +4894,24 @@ struct MercenaryMerchantResponse_Struct { /*0004*/ }; +// RoF: "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%1X" "%05X" "%08X" +struct TextLinkBody_Struct +{ + uint8 unknown_1; /* '%1X' - RoF field 1 / Server field 1 */ + uint32 item_id; /* '%05X' - RoF field 2 / Server field 2 */ + uint32 augment_1; /* '%05X' - RoF field 3 / Server field 3 */ + uint32 augment_2; /* '%05X' - RoF field 4 / Server field 4 */ + uint32 augment_3; /* '%05X' - RoF field 5 / Server field 5 */ + uint32 augment_4; /* '%05X' - RoF field 6 / Server field 6 */ + uint32 augment_5; /* '%05X' - RoF field 7 / Server field 7 */ + uint32 augment_6; /* '%05X' - RoF field 8 / Server field 8 */ + uint8 unknown_2; /* '%1X' - RoF field 9 / Server field 10 */ + uint32 unknown_3; /* '%04X' - RoF field 10 / Server field 11 */ + uint8 unknown_4; /* '%1X' - RoF field 11 / Server field 12 */ + uint32 ornament_icon; /* '%05X' - RoF field 12 / Server field 13 */ + int hash; /* '%08X' - RoF field 13 / Server field 14 */ +}; + }; //end namespace structs }; //end namespace RoF diff --git a/common/patches/sod.cpp b/common/patches/sod.cpp index 7355526c4..b30bad97d 100644 --- a/common/patches/sod.cpp +++ b/common/patches/sod.cpp @@ -31,6 +31,18 @@ namespace SoD static inline uint32 SoDToServerSlot(uint32 SoDSlot); static inline uint32 SoDToServerCorpseSlot(uint32 SoDCorpse); + // server to client text link converters + static inline void ServerToSoDTextLinks(std::string& sodTextLink, const std::string& serverTextLink); + static inline bool DegenerateServerTextLinkBody(TextLinkBody_Struct& serverLinkBodyStruct, const std::string& serverLinkBody); + static inline void ServerToSoDTextLinkBodyStruct(structs::TextLinkBody_Struct& sodLinkBodyStruct, const TextLinkBody_Struct& serverLinkBodyStruct); + static inline bool GenerateSoDTextLinkBody(std::string& sodLinkBody, const structs::TextLinkBody_Struct& sodLinkBodyStruct); + + // client to server text link converters + static inline void SoDToServerTextLinks(std::string& serverTextLink, const std::string& sodTextLink); + static inline bool DegenerateSoDTextLinkBody(structs::TextLinkBody_Struct& sodLinkBodyStruct, const std::string& sodLinkBody); + static inline void SoDToServerTextLinkBodyStruct(TextLinkBody_Struct& serverLinkBodyStruct, const structs::TextLinkBody_Struct& sodLinkBodyStruct); + static inline bool GenerateServerTextLinkBody(std::string& serverLinkBody, const TextLinkBody_Struct& serverLinkBodyStruct); + void Register(EQStreamIdentifier &into) { //create our opcode manager if we havent already @@ -296,6 +308,35 @@ namespace SoD FINISH_ENCODE(); } + ENCODE(OP_ChannelMessage) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + ChannelMessage_Struct *emu = (ChannelMessage_Struct *)in->pBuffer; + + unsigned char *__emu_buffer = in->pBuffer; + + std::string old_message = emu->message; + std::string new_message; + ServerToSoDTextLinks(new_message, old_message); + + in->size = sizeof(ChannelMessage_Struct) + new_message.length() + 1; + + in->pBuffer = new unsigned char[in->size]; + + char *OutBuffer = (char *)in->pBuffer; + + memcpy(OutBuffer, __emu_buffer, sizeof(ChannelMessage_Struct)); + + OutBuffer += sizeof(ChannelMessage_Struct); + + VARSTRUCT_ENCODE_STRING(OutBuffer, new_message.c_str()); + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + ENCODE(OP_CharInventory) { //consume the packet @@ -1963,6 +2004,44 @@ namespace SoD FINISH_ENCODE(); } + ENCODE(OP_SpecialMesg) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + SpecialMesg_Struct *emu = (SpecialMesg_Struct *)in->pBuffer; + + unsigned char *__emu_buffer = in->pBuffer; + + std::string old_message = &emu->message[strlen(emu->sayer)]; + std::string new_message; + ServerToSoDTextLinks(new_message, old_message); + + //in->size = 3 + 4 + 4 + strlen(emu->sayer) + 1 + 12 + new_message.length() + 1; + in->size = 25 + strlen(emu->sayer) + new_message.length(); + in->pBuffer = new unsigned char[in->size]; + + char *OutBuffer = (char *)in->pBuffer; + + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[0]); + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[1]); + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[2]); + + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->msg_type); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->target_spawn_id); + + VARSTRUCT_ENCODE_STRING(OutBuffer, emu->sayer); + + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); + + VARSTRUCT_ENCODE_STRING(OutBuffer, new_message.c_str()); + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + ENCODE(OP_Stun) { ENCODE_LENGTH_EXACT(Stun_Struct); @@ -2729,6 +2808,25 @@ namespace SoD FINISH_DIRECT_DECODE(); } + DECODE(OP_ChannelMessage) + { + unsigned char *__eq_buffer = __packet->pBuffer; + + std::string old_message = (char *)&__eq_buffer[sizeof(ChannelMessage_Struct)]; + std::string new_message; + SoDToServerTextLinks(new_message, old_message); + + __packet->size = sizeof(ChannelMessage_Struct) + new_message.length() + 1; + __packet->pBuffer = new unsigned char[__packet->size]; + + ChannelMessage_Struct *emu = (ChannelMessage_Struct *)__packet->pBuffer; + + memcpy(emu, __eq_buffer, sizeof(ChannelMessage_Struct)); + strcpy(emu->message, new_message.c_str()); + + delete[] __eq_buffer; + } + DECODE(OP_CharacterCreate) { DECODE_LENGTH_EXACT(structs::CharCreate_Struct); @@ -3683,5 +3781,362 @@ namespace SoD //uint32 ServerCorpse; return (SoDCorpse - 1); } + + static inline void ServerToSoDTextLinks(std::string& sodTextLink, const std::string& serverTextLink) + { + const char delimiter = 0x12; + +#if EQDEBUG >= 6 + _log(CHANNELS__ERROR, "TextLink(Server->SoD): old message '%s'", serverTextLink.c_str()); + + if (consts::TEXT_LINK_BODY_LENGTH == EmuConstants::TEXT_LINK_BODY_LENGTH) { + _log(CHANNELS__ERROR, "TextLink(Server->SoD): link size equal, no conversion necessary"); + sodTextLink = serverTextLink; + return; + } + + if (serverTextLink.find(delimiter) == std::string::npos) { + _log(CHANNELS__ERROR, "TextLink(Server->SoD): delimiter not found, no conversion necessary"); + sodTextLink = serverTextLink; + return; + } + + bool conversion_error = false; + auto segments = SplitString(serverTextLink, delimiter); + + for (size_t iter = 1; iter < segments.size(); iter += 2) { + TextLinkBody_Struct old_body_data; + if (!DegenerateServerTextLinkBody(old_body_data, segments[iter].substr(0, EmuConstants::TEXT_LINK_BODY_LENGTH).c_str())) { + _log(CHANNELS__ERROR, "TextLink(Server->SoD): body degeneration error '%s'", segments[iter].substr(0, EmuConstants::TEXT_LINK_BODY_LENGTH).c_str()); + conversion_error = true; + } + else { + _log(CHANNELS__ERROR, "TextLink(Server->SoD): body degeneration success '%s'", segments[iter].substr(0, EmuConstants::TEXT_LINK_BODY_LENGTH).c_str()); + } + + structs::TextLinkBody_Struct new_body_data; + ServerToSoDTextLinkBodyStruct(new_body_data, old_body_data); + + std::string segment; + if (!GenerateSoDTextLinkBody(segment, new_body_data)) { + _log(CHANNELS__ERROR, "TextLink(Server->SoD): body generation error '%s'", segment.c_str()); + conversion_error = true; + } + else { + _log(CHANNELS__ERROR, "TextLink(Server->SoD): body generation success '%s'", segment.c_str()); + segment.append(segments[iter].substr(EmuConstants::TEXT_LINK_BODY_LENGTH).c_str()); + segments[iter] = segment.c_str(); + } + } + + if (conversion_error) { + _log(CHANNELS__ERROR, "TextLink(Server->SoD): conversion error"); + sodTextLink = serverTextLink; + return; + } + + for (size_t iter = 0; iter < segments.size(); ++iter) { + if (iter & 1) { + sodTextLink.push_back(delimiter); + sodTextLink.append(segments[iter].c_str()); + sodTextLink.push_back(delimiter); + } + else { + sodTextLink.append(segments[iter].c_str()); + } + + _log(CHANNELS__ERROR, "TextLink(Server->SoD): segment[%u] '%s'", iter, segments[iter].c_str()); + } + + _log(CHANNELS__ERROR, "TextLink(Server->SoD): new message '%s'", sodTextLink.c_str()); +#else + if ((consts::TEXT_LINK_BODY_LENGTH == EmuConstants::TEXT_LINK_BODY_LENGTH) || (serverTextLink.find(delimiter) == std::string::npos)) { + sodTextLink = serverTextLink; + return; + } + + bool conversion_error = false; + auto segments = SplitString(serverTextLink, delimiter); + + for (size_t iter = 1; iter < segments.size(); iter += 2) { + TextLinkBody_Struct old_body_data; + if (!DegenerateServerTextLinkBody(old_body_data, segments[iter].substr(0, EmuConstants::TEXT_LINK_BODY_LENGTH).c_str())) { + conversion_error = true; + break; + } + + structs::TextLinkBody_Struct new_body_data; + ServerToSoDTextLinkBodyStruct(new_body_data, old_body_data); + + std::string segment; + if (!GenerateSoDTextLinkBody(segment, new_body_data)) { + conversion_error = true; + break; + } + else { + segment.append(segments[iter].substr(EmuConstants::TEXT_LINK_BODY_LENGTH).c_str()); + segments[iter] = segment.c_str(); + } + } + + if (conversion_error) { + _log(CHANNELS__ERROR, "TextLink(Server->SoD): conversion error"); + sodTextLink = serverTextLink; + return; + } + + for (size_t iter = 0; iter < segments.size(); ++iter) { + if (iter & 1) { + sodTextLink.push_back(delimiter); + sodTextLink.append(segments[iter].c_str()); + sodTextLink.push_back(delimiter); + } + else { + sodTextLink.append(segments[iter].c_str()); + } + } +#endif + } + + static inline bool DegenerateServerTextLinkBody(TextLinkBody_Struct& serverLinkBodyStruct, const std::string& serverLinkBody) + { + memset(&serverLinkBodyStruct, 0, sizeof(TextLinkBody_Struct)); + if (serverLinkBody.length() != EmuConstants::TEXT_LINK_BODY_LENGTH) { return false; } + + serverLinkBodyStruct.unknown_1 = (uint8)strtol(serverLinkBody.substr(0, 1).c_str(), nullptr, 16); + serverLinkBodyStruct.item_id = (uint32)strtol(serverLinkBody.substr(1, 5).c_str(), nullptr, 16); + serverLinkBodyStruct.augment_1 = (uint32)strtol(serverLinkBody.substr(6, 5).c_str(), nullptr, 16); + serverLinkBodyStruct.augment_2 = (uint32)strtol(serverLinkBody.substr(11, 5).c_str(), nullptr, 16); + serverLinkBodyStruct.augment_3 = (uint32)strtol(serverLinkBody.substr(16, 5).c_str(), nullptr, 16); + serverLinkBodyStruct.augment_4 = (uint32)strtol(serverLinkBody.substr(21, 5).c_str(), nullptr, 16); + serverLinkBodyStruct.augment_5 = (uint32)strtol(serverLinkBody.substr(26, 5).c_str(), nullptr, 16); + serverLinkBodyStruct.augment_6 = (uint32)strtol(serverLinkBody.substr(31, 5).c_str(), nullptr, 16); + serverLinkBodyStruct.unknown_2 = (uint8)strtol(serverLinkBody.substr(36, 1).c_str(), nullptr, 16); + serverLinkBodyStruct.unknown_3 = (uint8)strtol(serverLinkBody.substr(37, 1).c_str(), nullptr, 16); + serverLinkBodyStruct.unknown_4 = (uint32)strtol(serverLinkBody.substr(38, 4).c_str(), nullptr, 16); + serverLinkBodyStruct.unknown_5 = (uint8)strtol(serverLinkBody.substr(42, 1).c_str(), nullptr, 16); + serverLinkBodyStruct.ornament_icon = (uint32)strtol(serverLinkBody.substr(43, 5).c_str(), nullptr, 16); + serverLinkBodyStruct.hash = (int)strtol(serverLinkBody.substr(48, 8).c_str(), nullptr, 16); + + return true; + } + + static inline void ServerToSoDTextLinkBodyStruct(structs::TextLinkBody_Struct& sodLinkBodyStruct, const TextLinkBody_Struct& serverLinkBodyStruct) + { + sodLinkBodyStruct.unknown_1 = serverLinkBodyStruct.unknown_1; + sodLinkBodyStruct.item_id = serverLinkBodyStruct.item_id; + sodLinkBodyStruct.augment_1 = serverLinkBodyStruct.augment_1; + sodLinkBodyStruct.augment_2 = serverLinkBodyStruct.augment_2; + sodLinkBodyStruct.augment_3 = serverLinkBodyStruct.augment_3; + sodLinkBodyStruct.augment_4 = serverLinkBodyStruct.augment_4; + sodLinkBodyStruct.augment_5 = serverLinkBodyStruct.augment_5; + sodLinkBodyStruct.unknown_2 = serverLinkBodyStruct.unknown_3; + sodLinkBodyStruct.unknown_3 = serverLinkBodyStruct.unknown_4; + sodLinkBodyStruct.unknown_4 = serverLinkBodyStruct.unknown_5; + sodLinkBodyStruct.ornament_icon = serverLinkBodyStruct.ornament_icon; + sodLinkBodyStruct.hash = serverLinkBodyStruct.hash; + } + + static inline bool GenerateSoDTextLinkBody(std::string& sodLinkBody, const structs::TextLinkBody_Struct& sodLinkBodyStruct) + { + sodLinkBody = StringFormat( + "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%1X" "%05X" "%08X", + sodLinkBodyStruct.unknown_1, + sodLinkBodyStruct.item_id, + sodLinkBodyStruct.augment_1, + sodLinkBodyStruct.augment_2, + sodLinkBodyStruct.augment_3, + sodLinkBodyStruct.augment_4, + sodLinkBodyStruct.augment_5, + sodLinkBodyStruct.unknown_2, + sodLinkBodyStruct.unknown_3, + sodLinkBodyStruct.unknown_4, + sodLinkBodyStruct.ornament_icon, + sodLinkBodyStruct.hash + ); + + if (sodLinkBody.length() != consts::TEXT_LINK_BODY_LENGTH) { return false; } + return true; + } + + static inline void SoDToServerTextLinks(std::string& serverTextLink, const std::string& sodTextLink) + { + const char delimiter = 0x12; + +#if EQDEBUG >= 6 + _log(CHANNELS__ERROR, "TextLink(SoD->Server): old message '%s'", sodTextLink.c_str()); + + if (EmuConstants::TEXT_LINK_BODY_LENGTH == consts::TEXT_LINK_BODY_LENGTH) { + _log(CHANNELS__ERROR, "TextLink(SoD->Server): link size equal, no conversion necessary"); + serverTextLink = sodTextLink; + return; + } + + if (sodTextLink.find(delimiter) == std::string::npos) { + _log(CHANNELS__ERROR, "TextLink(SoD->Server): delimiter not found, no conversion necessary"); + serverTextLink = sodTextLink; + return; + } + + bool conversion_error = false; + auto segments = SplitString(sodTextLink, delimiter); + + for (size_t iter = 1; iter < segments.size(); iter += 2) { + structs::TextLinkBody_Struct old_body_data; + if (!DegenerateSoDTextLinkBody(old_body_data, segments[iter].substr(0, consts::TEXT_LINK_BODY_LENGTH).c_str())) { + _log(CHANNELS__ERROR, "TextLink(SoD->Server): body degeneration error '%s'", segments[iter].substr(0, consts::TEXT_LINK_BODY_LENGTH).c_str()); + conversion_error = true; + } + else { + _log(CHANNELS__ERROR, "TextLink(SoD->Server): body degeneration success '%s'", segments[iter].substr(0, consts::TEXT_LINK_BODY_LENGTH).c_str()); + } + + TextLinkBody_Struct new_body_data; + SoDToServerTextLinkBodyStruct(new_body_data, old_body_data); + + std::string segment; + if (!GenerateServerTextLinkBody(segment, new_body_data)) { + _log(CHANNELS__ERROR, "TextLink(SoD->Server): body generation error '%s'", segment.c_str()); + conversion_error = true; + } + else { + _log(CHANNELS__ERROR, "TextLink(SoD->Server): body generation success '%s'", segment.c_str()); + segment.append(segments[iter].substr(consts::TEXT_LINK_BODY_LENGTH).c_str()); + segments[iter] = segment.c_str(); + } + } + + if (conversion_error) { + _log(CHANNELS__ERROR, "TextLink(SoD->Server): conversion error"); + serverTextLink = sodTextLink; + return; + } + + for (size_t iter = 0; iter < segments.size(); ++iter) { + if (iter & 1) { + serverTextLink.push_back(delimiter); + serverTextLink.append(segments[iter].c_str()); + serverTextLink.push_back(delimiter); + } + else { + serverTextLink.append(segments[iter].c_str()); + } + + _log(CHANNELS__ERROR, "TextLink(SoD->Server): segment[%u] '%s'", iter, segments[iter].c_str()); + } + + _log(CHANNELS__ERROR, "TextLink(SoD->Server): new message '%s'", serverTextLink.c_str()); +#else + if ((EmuConstants::TEXT_LINK_BODY_LENGTH == consts::TEXT_LINK_BODY_LENGTH) || (sodTextLink.find(delimiter) == std::string::npos)) { + serverTextLink = sodTextLink; + return; + } + + bool conversion_error = false; + auto segments = SplitString(sodTextLink, delimiter); + + for (size_t iter = 1; iter < segments.size(); iter += 2) { + structs::TextLinkBody_Struct old_body_data; + if (!DegenerateSoDTextLinkBody(old_body_data, segments[iter].substr(0, consts::TEXT_LINK_BODY_LENGTH).c_str())) { + conversion_error = true; + break; + } + + TextLinkBody_Struct new_body_data; + SoDToServerTextLinkBodyStruct(new_body_data, old_body_data); + + std::string segment; + if (!GenerateServerTextLinkBody(segment, new_body_data)) { + conversion_error = true; + break; + } + else { + segment.append(segments[iter].substr(consts::TEXT_LINK_BODY_LENGTH).c_str()); + segments[iter] = segment.c_str(); + } + } + + if (conversion_error) { + _log(CHANNELS__ERROR, "TextLink(SoD->Server): conversion error"); + serverTextLink = sodTextLink; + return; + } + + for (size_t iter = 0; iter < segments.size(); ++iter) { + if (iter & 1) { + serverTextLink.push_back(delimiter); + serverTextLink.append(segments[iter].c_str()); + serverTextLink.push_back(delimiter); + } + else { + serverTextLink.append(segments[iter].c_str()); + } + } +#endif + } + + static inline bool DegenerateSoDTextLinkBody(structs::TextLinkBody_Struct& sodLinkBodyStruct, const std::string& sodLinkBody) + { + // SoF: "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%1X" "%05X" "%08X" + memset(&sodLinkBodyStruct, 0, sizeof(structs::TextLinkBody_Struct)); + if (sodLinkBody.length() != consts::TEXT_LINK_BODY_LENGTH) { return false; } + + sodLinkBodyStruct.unknown_1 = (uint8)strtol(sodLinkBody.substr(0, 1).c_str(), nullptr, 16); + sodLinkBodyStruct.item_id = (uint32)strtol(sodLinkBody.substr(1, 5).c_str(), nullptr, 16); + sodLinkBodyStruct.augment_1 = (uint32)strtol(sodLinkBody.substr(6, 5).c_str(), nullptr, 16); + sodLinkBodyStruct.augment_2 = (uint32)strtol(sodLinkBody.substr(11, 5).c_str(), nullptr, 16); + sodLinkBodyStruct.augment_3 = (uint32)strtol(sodLinkBody.substr(16, 5).c_str(), nullptr, 16); + sodLinkBodyStruct.augment_4 = (uint32)strtol(sodLinkBody.substr(21, 5).c_str(), nullptr, 16); + sodLinkBodyStruct.augment_5 = (uint32)strtol(sodLinkBody.substr(26, 5).c_str(), nullptr, 16); + sodLinkBodyStruct.unknown_2 = (uint8)strtol(sodLinkBody.substr(31, 1).c_str(), nullptr, 16); + sodLinkBodyStruct.unknown_3 = (uint32)strtol(sodLinkBody.substr(32, 4).c_str(), nullptr, 16); + sodLinkBodyStruct.unknown_4 = (uint8)strtol(sodLinkBody.substr(36, 1).c_str(), nullptr, 16); + sodLinkBodyStruct.ornament_icon = (uint32)strtol(sodLinkBody.substr(37, 5).c_str(), nullptr, 16); + sodLinkBodyStruct.hash = (int)strtol(sodLinkBody.substr(42, 8).c_str(), nullptr, 16); + + return true; + } + + static inline void SoDToServerTextLinkBodyStruct(TextLinkBody_Struct& serverLinkBodyStruct, const structs::TextLinkBody_Struct& sodLinkBodyStruct) + { + serverLinkBodyStruct.unknown_1 = sodLinkBodyStruct.unknown_1; + serverLinkBodyStruct.item_id = sodLinkBodyStruct.item_id; + serverLinkBodyStruct.augment_1 = sodLinkBodyStruct.augment_1; + serverLinkBodyStruct.augment_2 = sodLinkBodyStruct.augment_2; + serverLinkBodyStruct.augment_3 = sodLinkBodyStruct.augment_3; + serverLinkBodyStruct.augment_4 = sodLinkBodyStruct.augment_4; + serverLinkBodyStruct.augment_5 = sodLinkBodyStruct.augment_5; + serverLinkBodyStruct.augment_6 = NOT_USED; + serverLinkBodyStruct.unknown_2 = NOT_USED; + serverLinkBodyStruct.unknown_3 = sodLinkBodyStruct.unknown_2; + serverLinkBodyStruct.unknown_4 = sodLinkBodyStruct.unknown_3; + serverLinkBodyStruct.unknown_5 = sodLinkBodyStruct.unknown_4; + serverLinkBodyStruct.ornament_icon = sodLinkBodyStruct.ornament_icon; + serverLinkBodyStruct.hash = sodLinkBodyStruct.hash; + } + + static inline bool GenerateServerTextLinkBody(std::string& serverLinkBody, const TextLinkBody_Struct& serverLinkBodyStruct) + { + serverLinkBody = StringFormat( + "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%01X" "%01X" "%04X" "%01X" "%05X" "%08X", + serverLinkBodyStruct.unknown_1, + serverLinkBodyStruct.item_id, + serverLinkBodyStruct.augment_1, + serverLinkBodyStruct.augment_2, + serverLinkBodyStruct.augment_3, + serverLinkBodyStruct.augment_4, + serverLinkBodyStruct.augment_5, + serverLinkBodyStruct.augment_6, + serverLinkBodyStruct.unknown_2, + serverLinkBodyStruct.unknown_3, + serverLinkBodyStruct.unknown_4, + serverLinkBodyStruct.unknown_5, + serverLinkBodyStruct.ornament_icon, + serverLinkBodyStruct.hash + ); + + if (serverLinkBody.length() != EmuConstants::TEXT_LINK_BODY_LENGTH) { return false; } + return true; + } } // end namespace SoD diff --git a/common/patches/sod_constants.h b/common/patches/sod_constants.h index 8bdf45532..89749fd56 100644 --- a/common/patches/sod_constants.h +++ b/common/patches/sod_constants.h @@ -177,6 +177,8 @@ namespace SoD { static const uint32 BANDOLIERS_COUNT = 20; // count = number of bandolier instances static const uint32 BANDOLIER_SIZE = 4; // size = number of equipment slots in bandolier instance static const uint32 POTION_BELT_SIZE = 5; + + static const size_t TEXT_LINK_BODY_LENGTH = 50; } namespace limits { diff --git a/common/patches/sod_ops.h b/common/patches/sod_ops.h index 038b42e08..e3b6c1f41 100644 --- a/common/patches/sod_ops.h +++ b/common/patches/sod_ops.h @@ -8,6 +8,7 @@ E(OP_Barter) E(OP_BazaarSearch) E(OP_Buff) E(OP_CancelTrade) +E(OP_ChannelMessage) E(OP_CharInventory) E(OP_ClientUpdate) E(OP_Consider) @@ -57,6 +58,7 @@ E(OP_ShopPlayerBuy) E(OP_ShopPlayerSell) E(OP_SomeItemPacketMaybe) E(OP_SpawnDoor) +E(OP_SpecialMesg) E(OP_Stun) E(OP_TargetBuffs) E(OP_Track) @@ -81,6 +83,7 @@ D(OP_BazaarSearch) D(OP_Buff) D(OP_Bug) D(OP_CastSpell) +D(OP_ChannelMessage) D(OP_CharacterCreate) D(OP_ClientUpdate) D(OP_Consider) diff --git a/common/patches/sod_structs.h b/common/patches/sod_structs.h index 7ec101fd3..3c1acc37d 100644 --- a/common/patches/sod_structs.h +++ b/common/patches/sod_structs.h @@ -4409,6 +4409,22 @@ struct MercenaryAssign_Struct { /*0012*/ }; +// SoF: "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%1X" "%05X" "%08X" +struct TextLinkBody_Struct +{ + uint8 unknown_1; /* '%1X' - SoD field 1 / Server field 1 */ + uint32 item_id; /* '%05X' - SoD field 2 / Server field 2 */ + uint32 augment_1; /* '%05X' - SoD field 3 / Server field 3 */ + uint32 augment_2; /* '%05X' - SoD field 4 / Server field 4 */ + uint32 augment_3; /* '%05X' - SoD field 5 / Server field 5 */ + uint32 augment_4; /* '%05X' - SoD field 6 / Server field 6 */ + uint32 augment_5; /* '%05X' - SoD field 7 / Server field 7 */ + uint8 unknown_2; /* '%1X' - SoD field 8 / Server field 10 */ + uint32 unknown_3; /* '%04X' - SoD field 9 / Server field 11 */ + uint8 unknown_4; /* '%1X' - SoD field 10 / Server field 12 */ + uint32 ornament_icon; /* '%05X' - SoD field 11 / Server field 13 */ + int hash; /* '%08X' - SoD field 12 / Server field 14 */ +}; }; //end namespace structs }; //end namespace SoD diff --git a/common/patches/sof.cpp b/common/patches/sof.cpp index fc46c53cb..cd5a1ef71 100644 --- a/common/patches/sof.cpp +++ b/common/patches/sof.cpp @@ -31,6 +31,18 @@ namespace SoF static inline uint32 SoFToServerSlot(uint32 SoFSlot); static inline uint32 SoFToServerCorpseSlot(uint32 SoFCorpse); + // server to client text link converters + static inline void ServerToSoFTextLinks(std::string& sofTextLink, const std::string& serverTextLink); + static inline bool DegenerateServerTextLinkBody(TextLinkBody_Struct& serverLinkBodyStruct, const std::string& serverLinkBody); + static inline void ServerToSoFTextLinkBodyStruct(structs::TextLinkBody_Struct& sofLinkBodyStruct, const TextLinkBody_Struct& serverLinkBodyStruct); + static inline bool GenerateSoFTextLinkBody(std::string& sofLinkBody, const structs::TextLinkBody_Struct& sofLinkBodyStruct); + + // client to server text link converters + static inline void SoFToServerTextLinks(std::string& serverTextLink, const std::string& sofTextLink); + static inline bool DegenerateSoFTextLinkBody(structs::TextLinkBody_Struct& sofLinkBodyStruct, const std::string& sofLinkBody); + static inline void SoFToServerTextLinkBodyStruct(TextLinkBody_Struct& serverLinkBodyStruct, const structs::TextLinkBody_Struct& sofLinkBodyStruct); + static inline bool GenerateServerTextLinkBody(std::string& serverLinkBody, const TextLinkBody_Struct& serverLinkBodyStruct); + void Register(EQStreamIdentifier &into) { //create our opcode manager if we havent already @@ -278,6 +290,35 @@ namespace SoF FINISH_ENCODE(); } + ENCODE(OP_ChannelMessage) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + ChannelMessage_Struct *emu = (ChannelMessage_Struct *)in->pBuffer; + + unsigned char *__emu_buffer = in->pBuffer; + + std::string old_message = emu->message; + std::string new_message; + ServerToSoFTextLinks(new_message, old_message); + + in->size = sizeof(ChannelMessage_Struct) + new_message.length() + 1; + + in->pBuffer = new unsigned char[in->size]; + + char *OutBuffer = (char *)in->pBuffer; + + memcpy(OutBuffer, __emu_buffer, sizeof(ChannelMessage_Struct)); + + OutBuffer += sizeof(ChannelMessage_Struct); + + VARSTRUCT_ENCODE_STRING(OutBuffer, new_message.c_str()); + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + ENCODE(OP_CharInventory) { //consume the packet @@ -1609,6 +1650,44 @@ namespace SoF FINISH_ENCODE(); } + ENCODE(OP_SpecialMesg) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + SpecialMesg_Struct *emu = (SpecialMesg_Struct *)in->pBuffer; + + unsigned char *__emu_buffer = in->pBuffer; + + std::string old_message = &emu->message[strlen(emu->sayer)]; + std::string new_message; + ServerToSoFTextLinks(new_message, old_message); + + //in->size = 3 + 4 + 4 + strlen(emu->sayer) + 1 + 12 + new_message.length() + 1; + in->size = 25 + strlen(emu->sayer) + new_message.length(); + in->pBuffer = new unsigned char[in->size]; + + char *OutBuffer = (char *)in->pBuffer; + + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[0]); + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[1]); + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[2]); + + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->msg_type); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->target_spawn_id); + + VARSTRUCT_ENCODE_STRING(OutBuffer, emu->sayer); + + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); + + VARSTRUCT_ENCODE_STRING(OutBuffer, new_message.c_str()); + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + ENCODE(OP_Stun) { ENCODE_LENGTH_EXACT(Stun_Struct); @@ -2129,6 +2208,25 @@ namespace SoF FINISH_DIRECT_DECODE(); } + DECODE(OP_ChannelMessage) + { + unsigned char *__eq_buffer = __packet->pBuffer; + + std::string old_message = (char *)&__eq_buffer[sizeof(ChannelMessage_Struct)]; + std::string new_message; + SoFToServerTextLinks(new_message, old_message); + + __packet->size = sizeof(ChannelMessage_Struct) + new_message.length() + 1; + __packet->pBuffer = new unsigned char[__packet->size]; + + ChannelMessage_Struct *emu = (ChannelMessage_Struct *)__packet->pBuffer; + + memcpy(emu, __eq_buffer, sizeof(ChannelMessage_Struct)); + strcpy(emu->message, new_message.c_str()); + + delete[] __eq_buffer; + } + DECODE(OP_CharacterCreate) { DECODE_LENGTH_EXACT(structs::CharCreate_Struct); @@ -3005,5 +3103,362 @@ namespace SoF //uint32 ServerCorpse; return (SoFCorpse - 1); } + + static inline void ServerToSoFTextLinks(std::string& sofTextLink, const std::string& serverTextLink) + { + const char delimiter = 0x12; + +#if EQDEBUG >= 6 + _log(CHANNELS__ERROR, "TextLink(Server->SoF): old message '%s'", serverTextLink.c_str()); + + if (consts::TEXT_LINK_BODY_LENGTH == EmuConstants::TEXT_LINK_BODY_LENGTH) { + _log(CHANNELS__ERROR, "TextLink(Server->SoF): link size equal, no conversion necessary"); + sofTextLink = serverTextLink; + return; + } + + if (serverTextLink.find(delimiter) == std::string::npos) { + _log(CHANNELS__ERROR, "TextLink(Server->SoF): delimiter not found, no conversion necessary"); + sofTextLink = serverTextLink; + return; + } + + bool conversion_error = false; + auto segments = SplitString(serverTextLink, delimiter); + + for (size_t iter = 1; iter < segments.size(); iter += 2) { + TextLinkBody_Struct old_body_data; + if (!DegenerateServerTextLinkBody(old_body_data, segments[iter].substr(0, EmuConstants::TEXT_LINK_BODY_LENGTH).c_str())) { + _log(CHANNELS__ERROR, "TextLink(Server->SoF): body degeneration error '%s'", segments[iter].substr(0, EmuConstants::TEXT_LINK_BODY_LENGTH).c_str()); + conversion_error = true; + } + else { + _log(CHANNELS__ERROR, "TextLink(Server->SoF): body degeneration success '%s'", segments[iter].substr(0, EmuConstants::TEXT_LINK_BODY_LENGTH).c_str()); + } + + structs::TextLinkBody_Struct new_body_data; + ServerToSoFTextLinkBodyStruct(new_body_data, old_body_data); + + std::string segment; + if (!GenerateSoFTextLinkBody(segment, new_body_data)) { + _log(CHANNELS__ERROR, "TextLink(Server->SoF): body generation error '%s'", segment.c_str()); + conversion_error = true; + } + else { + _log(CHANNELS__ERROR, "TextLink(Server->SoF): body generation success '%s'", segment.c_str()); + segment.append(segments[iter].substr(EmuConstants::TEXT_LINK_BODY_LENGTH).c_str()); + segments[iter] = segment.c_str(); + } + } + + if (conversion_error) { + _log(CHANNELS__ERROR, "TextLink(Server->SoF): conversion error"); + sofTextLink = serverTextLink; + return; + } + + for (size_t iter = 0; iter < segments.size(); ++iter) { + if (iter & 1) { + sofTextLink.push_back(delimiter); + sofTextLink.append(segments[iter].c_str()); + sofTextLink.push_back(delimiter); + } + else { + sofTextLink.append(segments[iter].c_str()); + } + + _log(CHANNELS__ERROR, "TextLink(Server->SoF): segment[%u] '%s'", iter, segments[iter].c_str()); + } + + _log(CHANNELS__ERROR, "TextLink(Server->SoF): new message '%s'", sofTextLink.c_str()); +#else + if ((consts::TEXT_LINK_BODY_LENGTH == EmuConstants::TEXT_LINK_BODY_LENGTH) || (serverTextLink.find(delimiter) == std::string::npos)) { + sofTextLink = serverTextLink; + return; + } + + bool conversion_error = false; + auto segments = SplitString(serverTextLink, delimiter); + + for (size_t iter = 1; iter < segments.size(); iter += 2) { + TextLinkBody_Struct old_body_data; + if (!DegenerateServerTextLinkBody(old_body_data, segments[iter].substr(0, EmuConstants::TEXT_LINK_BODY_LENGTH).c_str())) { + conversion_error = true; + break; + } + + structs::TextLinkBody_Struct new_body_data; + ServerToSoFTextLinkBodyStruct(new_body_data, old_body_data); + + std::string segment; + if (!GenerateSoFTextLinkBody(segment, new_body_data)) { + conversion_error = true; + break; + } + else { + segment.append(segments[iter].substr(EmuConstants::TEXT_LINK_BODY_LENGTH).c_str()); + segments[iter] = segment.c_str(); + } + } + + if (conversion_error) { + _log(CHANNELS__ERROR, "TextLink(Server->SoF): conversion error"); + sofTextLink = serverTextLink; + return; + } + + for (size_t iter = 0; iter < segments.size(); ++iter) { + if (iter & 1) { + sofTextLink.push_back(delimiter); + sofTextLink.append(segments[iter].c_str()); + sofTextLink.push_back(delimiter); + } + else { + sofTextLink.append(segments[iter].c_str()); + } + } +#endif + } + + static inline bool DegenerateServerTextLinkBody(TextLinkBody_Struct& serverLinkBodyStruct, const std::string& serverLinkBody) + { + memset(&serverLinkBodyStruct, 0, sizeof(TextLinkBody_Struct)); + if (serverLinkBody.length() != EmuConstants::TEXT_LINK_BODY_LENGTH) { return false; } + + serverLinkBodyStruct.unknown_1 = (uint8)strtol(serverLinkBody.substr(0, 1).c_str(), nullptr, 16); + serverLinkBodyStruct.item_id = (uint32)strtol(serverLinkBody.substr(1, 5).c_str(), nullptr, 16); + serverLinkBodyStruct.augment_1 = (uint32)strtol(serverLinkBody.substr(6, 5).c_str(), nullptr, 16); + serverLinkBodyStruct.augment_2 = (uint32)strtol(serverLinkBody.substr(11, 5).c_str(), nullptr, 16); + serverLinkBodyStruct.augment_3 = (uint32)strtol(serverLinkBody.substr(16, 5).c_str(), nullptr, 16); + serverLinkBodyStruct.augment_4 = (uint32)strtol(serverLinkBody.substr(21, 5).c_str(), nullptr, 16); + serverLinkBodyStruct.augment_5 = (uint32)strtol(serverLinkBody.substr(26, 5).c_str(), nullptr, 16); + serverLinkBodyStruct.augment_6 = (uint32)strtol(serverLinkBody.substr(31, 5).c_str(), nullptr, 16); + serverLinkBodyStruct.unknown_2 = (uint8)strtol(serverLinkBody.substr(36, 1).c_str(), nullptr, 16); + serverLinkBodyStruct.unknown_3 = (uint8)strtol(serverLinkBody.substr(37, 1).c_str(), nullptr, 16); + serverLinkBodyStruct.unknown_4 = (uint32)strtol(serverLinkBody.substr(38, 4).c_str(), nullptr, 16); + serverLinkBodyStruct.unknown_5 = (uint8)strtol(serverLinkBody.substr(42, 1).c_str(), nullptr, 16); + serverLinkBodyStruct.ornament_icon = (uint32)strtol(serverLinkBody.substr(43, 5).c_str(), nullptr, 16); + serverLinkBodyStruct.hash = (int)strtol(serverLinkBody.substr(48, 8).c_str(), nullptr, 16); + + return true; + } + + static inline void ServerToSoFTextLinkBodyStruct(structs::TextLinkBody_Struct& sofLinkBodyStruct, const TextLinkBody_Struct& serverLinkBodyStruct) + { + sofLinkBodyStruct.unknown_1 = serverLinkBodyStruct.unknown_1; + sofLinkBodyStruct.item_id = serverLinkBodyStruct.item_id; + sofLinkBodyStruct.augment_1 = serverLinkBodyStruct.augment_1; + sofLinkBodyStruct.augment_2 = serverLinkBodyStruct.augment_2; + sofLinkBodyStruct.augment_3 = serverLinkBodyStruct.augment_3; + sofLinkBodyStruct.augment_4 = serverLinkBodyStruct.augment_4; + sofLinkBodyStruct.augment_5 = serverLinkBodyStruct.augment_5; + sofLinkBodyStruct.unknown_2 = serverLinkBodyStruct.unknown_3; + sofLinkBodyStruct.unknown_3 = serverLinkBodyStruct.unknown_4; + sofLinkBodyStruct.unknown_4 = serverLinkBodyStruct.unknown_5; + sofLinkBodyStruct.ornament_icon = serverLinkBodyStruct.ornament_icon; + sofLinkBodyStruct.hash = serverLinkBodyStruct.hash; + } + + static inline bool GenerateSoFTextLinkBody(std::string& sofLinkBody, const structs::TextLinkBody_Struct& sofLinkBodyStruct) + { + sofLinkBody = StringFormat( + "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%1X" "%05X" "%08X", + sofLinkBodyStruct.unknown_1, + sofLinkBodyStruct.item_id, + sofLinkBodyStruct.augment_1, + sofLinkBodyStruct.augment_2, + sofLinkBodyStruct.augment_3, + sofLinkBodyStruct.augment_4, + sofLinkBodyStruct.augment_5, + sofLinkBodyStruct.unknown_2, + sofLinkBodyStruct.unknown_3, + sofLinkBodyStruct.unknown_4, + sofLinkBodyStruct.ornament_icon, + sofLinkBodyStruct.hash + ); + + if (sofLinkBody.length() != consts::TEXT_LINK_BODY_LENGTH) { return false; } + return true; + } + + static inline void SoFToServerTextLinks(std::string& serverTextLink, const std::string& sofTextLink) + { + const char delimiter = 0x12; + +#if EQDEBUG >= 6 + _log(CHANNELS__ERROR, "TextLink(SoF->Server): old message '%s'", sofTextLink.c_str()); + + if (EmuConstants::TEXT_LINK_BODY_LENGTH == consts::TEXT_LINK_BODY_LENGTH) { + _log(CHANNELS__ERROR, "TextLink(SoF->Server): link size equal, no conversion necessary"); + serverTextLink = sofTextLink; + return; + } + + if (sofTextLink.find(delimiter) == std::string::npos) { + _log(CHANNELS__ERROR, "TextLink(SoF->Server): delimiter not found, no conversion necessary"); + serverTextLink = sofTextLink; + return; + } + + bool conversion_error = false; + auto segments = SplitString(sofTextLink, delimiter); + + for (size_t iter = 1; iter < segments.size(); iter += 2) { + structs::TextLinkBody_Struct old_body_data; + if (!DegenerateSoFTextLinkBody(old_body_data, segments[iter].substr(0, consts::TEXT_LINK_BODY_LENGTH).c_str())) { + _log(CHANNELS__ERROR, "TextLink(SoF->Server): body degeneration error '%s'", segments[iter].substr(0, consts::TEXT_LINK_BODY_LENGTH).c_str()); + conversion_error = true; + } + else { + _log(CHANNELS__ERROR, "TextLink(SoF->Server): body degeneration success '%s'", segments[iter].substr(0, consts::TEXT_LINK_BODY_LENGTH).c_str()); + } + + TextLinkBody_Struct new_body_data; + SoFToServerTextLinkBodyStruct(new_body_data, old_body_data); + + std::string segment; + if (!GenerateServerTextLinkBody(segment, new_body_data)) { + _log(CHANNELS__ERROR, "TextLink(SoF->Server): body generation error '%s'", segment.c_str()); + conversion_error = true; + } + else { + _log(CHANNELS__ERROR, "TextLink(SoF->Server): body generation success '%s'", segment.c_str()); + segment.append(segments[iter].substr(consts::TEXT_LINK_BODY_LENGTH).c_str()); + segments[iter] = segment.c_str(); + } + } + + if (conversion_error) { + _log(CHANNELS__ERROR, "TextLink(SoF->Server): conversion error"); + serverTextLink = sofTextLink; + return; + } + + for (size_t iter = 0; iter < segments.size(); ++iter) { + if (iter & 1) { + serverTextLink.push_back(delimiter); + serverTextLink.append(segments[iter].c_str()); + serverTextLink.push_back(delimiter); + } + else { + serverTextLink.append(segments[iter].c_str()); + } + + _log(CHANNELS__ERROR, "TextLink(SoF->Server): segment[%u] '%s'", iter, segments[iter].c_str()); + } + + _log(CHANNELS__ERROR, "TextLink(SoF->Server): new message '%s'", serverTextLink.c_str()); +#else + if ((EmuConstants::TEXT_LINK_BODY_LENGTH == consts::TEXT_LINK_BODY_LENGTH) || (sofTextLink.find(delimiter) == std::string::npos)) { + serverTextLink = sofTextLink; + return; + } + + bool conversion_error = false; + auto segments = SplitString(sofTextLink, delimiter); + + for (size_t iter = 1; iter < segments.size(); iter += 2) { + structs::TextLinkBody_Struct old_body_data; + if (!DegenerateSoFTextLinkBody(old_body_data, segments[iter].substr(0, consts::TEXT_LINK_BODY_LENGTH).c_str())) { + conversion_error = true; + break; + } + + TextLinkBody_Struct new_body_data; + SoFToServerTextLinkBodyStruct(new_body_data, old_body_data); + + std::string segment; + if (!GenerateServerTextLinkBody(segment, new_body_data)) { + conversion_error = true; + break; + } + else { + segment.append(segments[iter].substr(consts::TEXT_LINK_BODY_LENGTH).c_str()); + segments[iter] = segment.c_str(); + } + } + + if (conversion_error) { + _log(CHANNELS__ERROR, "TextLink(SoF->Server): conversion error"); + serverTextLink = sofTextLink; + return; + } + + for (size_t iter = 0; iter < segments.size(); ++iter) { + if (iter & 1) { + serverTextLink.push_back(delimiter); + serverTextLink.append(segments[iter].c_str()); + serverTextLink.push_back(delimiter); + } + else { + serverTextLink.append(segments[iter].c_str()); + } + } +#endif + } + + static inline bool DegenerateSoFTextLinkBody(structs::TextLinkBody_Struct& sofLinkBodyStruct, const std::string& sofLinkBody) + { + // SoF: "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%1X" "%05X" "%08X" + memset(&sofLinkBodyStruct, 0, sizeof(structs::TextLinkBody_Struct)); + if (sofLinkBody.length() != consts::TEXT_LINK_BODY_LENGTH) { return false; } + + sofLinkBodyStruct.unknown_1 = (uint8)strtol(sofLinkBody.substr(0, 1).c_str(), nullptr, 16); + sofLinkBodyStruct.item_id = (uint32)strtol(sofLinkBody.substr(1, 5).c_str(), nullptr, 16); + sofLinkBodyStruct.augment_1 = (uint32)strtol(sofLinkBody.substr(6, 5).c_str(), nullptr, 16); + sofLinkBodyStruct.augment_2 = (uint32)strtol(sofLinkBody.substr(11, 5).c_str(), nullptr, 16); + sofLinkBodyStruct.augment_3 = (uint32)strtol(sofLinkBody.substr(16, 5).c_str(), nullptr, 16); + sofLinkBodyStruct.augment_4 = (uint32)strtol(sofLinkBody.substr(21, 5).c_str(), nullptr, 16); + sofLinkBodyStruct.augment_5 = (uint32)strtol(sofLinkBody.substr(26, 5).c_str(), nullptr, 16); + sofLinkBodyStruct.unknown_2 = (uint8)strtol(sofLinkBody.substr(31, 1).c_str(), nullptr, 16); + sofLinkBodyStruct.unknown_3 = (uint32)strtol(sofLinkBody.substr(32, 4).c_str(), nullptr, 16); + sofLinkBodyStruct.unknown_4 = (uint8)strtol(sofLinkBody.substr(36, 1).c_str(), nullptr, 16); + sofLinkBodyStruct.ornament_icon = (uint32)strtol(sofLinkBody.substr(37, 5).c_str(), nullptr, 16); + sofLinkBodyStruct.hash = (int)strtol(sofLinkBody.substr(42, 8).c_str(), nullptr, 16); + + return true; + } + + static inline void SoFToServerTextLinkBodyStruct(TextLinkBody_Struct& serverLinkBodyStruct, const structs::TextLinkBody_Struct& sofLinkBodyStruct) + { + serverLinkBodyStruct.unknown_1 = sofLinkBodyStruct.unknown_1; + serverLinkBodyStruct.item_id = sofLinkBodyStruct.item_id; + serverLinkBodyStruct.augment_1 = sofLinkBodyStruct.augment_1; + serverLinkBodyStruct.augment_2 = sofLinkBodyStruct.augment_2; + serverLinkBodyStruct.augment_3 = sofLinkBodyStruct.augment_3; + serverLinkBodyStruct.augment_4 = sofLinkBodyStruct.augment_4; + serverLinkBodyStruct.augment_5 = sofLinkBodyStruct.augment_5; + serverLinkBodyStruct.augment_6 = NOT_USED; + serverLinkBodyStruct.unknown_2 = NOT_USED; + serverLinkBodyStruct.unknown_3 = sofLinkBodyStruct.unknown_2; + serverLinkBodyStruct.unknown_4 = sofLinkBodyStruct.unknown_3; + serverLinkBodyStruct.unknown_5 = sofLinkBodyStruct.unknown_4; + serverLinkBodyStruct.ornament_icon = sofLinkBodyStruct.ornament_icon; + serverLinkBodyStruct.hash = sofLinkBodyStruct.hash; + } + + static inline bool GenerateServerTextLinkBody(std::string& serverLinkBody, const TextLinkBody_Struct& serverLinkBodyStruct) + { + serverLinkBody = StringFormat( + "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%01X" "%01X" "%04X" "%01X" "%05X" "%08X", + serverLinkBodyStruct.unknown_1, + serverLinkBodyStruct.item_id, + serverLinkBodyStruct.augment_1, + serverLinkBodyStruct.augment_2, + serverLinkBodyStruct.augment_3, + serverLinkBodyStruct.augment_4, + serverLinkBodyStruct.augment_5, + serverLinkBodyStruct.augment_6, + serverLinkBodyStruct.unknown_2, + serverLinkBodyStruct.unknown_3, + serverLinkBodyStruct.unknown_4, + serverLinkBodyStruct.unknown_5, + serverLinkBodyStruct.ornament_icon, + serverLinkBodyStruct.hash + ); + + if (serverLinkBody.length() != EmuConstants::TEXT_LINK_BODY_LENGTH) { return false; } + return true; + } } // end namespace SoF diff --git a/common/patches/sof_constants.h b/common/patches/sof_constants.h index 0b959ef3a..5e0f4b18a 100644 --- a/common/patches/sof_constants.h +++ b/common/patches/sof_constants.h @@ -177,6 +177,8 @@ namespace SoF { static const uint32 BANDOLIERS_COUNT = 20; // count = number of bandolier instances static const uint32 BANDOLIER_SIZE = 4; // size = number of equipment slots in bandolier instance static const uint32 POTION_BELT_SIZE = 5; + + static const size_t TEXT_LINK_BODY_LENGTH = 50; } namespace limits { diff --git a/common/patches/sof_ops.h b/common/patches/sof_ops.h index 132e3dc77..88d468c82 100644 --- a/common/patches/sof_ops.h +++ b/common/patches/sof_ops.h @@ -8,6 +8,7 @@ E(OP_BazaarSearch) E(OP_BecomeTrader) E(OP_Buff) E(OP_CancelTrade) +E(OP_ChannelMessage) E(OP_CharInventory) E(OP_ClientUpdate) E(OP_Consider) @@ -50,6 +51,7 @@ E(OP_SendZonepoints) E(OP_ShopPlayerSell) E(OP_SomeItemPacketMaybe) E(OP_SpawnDoor) +E(OP_SpecialMesg) E(OP_Stun) E(OP_Track) E(OP_Trader) @@ -70,6 +72,7 @@ D(OP_AugmentInfo) D(OP_AugmentItem) D(OP_Buff) D(OP_CastSpell) +D(OP_ChannelMessage) D(OP_CharacterCreate) D(OP_ClientUpdate) D(OP_Consider) diff --git a/common/patches/sof_structs.h b/common/patches/sof_structs.h index 51f41a590..5986e0029 100644 --- a/common/patches/sof_structs.h +++ b/common/patches/sof_structs.h @@ -4115,18 +4115,24 @@ struct AltCurrencySellItem_Struct { /*010*/ uint32 cost; }; +// SoF: "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%1X" "%05X" "%08X" +struct TextLinkBody_Struct +{ + uint8 unknown_1; /* '%1X' - SoF field 1 / Server field 1 */ + uint32 item_id; /* '%05X' - SoF field 2 / Server field 2 */ + uint32 augment_1; /* '%05X' - SoF field 3 / Server field 3 */ + uint32 augment_2; /* '%05X' - SoF field 4 / Server field 4 */ + uint32 augment_3; /* '%05X' - SoF field 5 / Server field 5 */ + uint32 augment_4; /* '%05X' - SoF field 6 / Server field 6 */ + uint32 augment_5; /* '%05X' - SoF field 7 / Server field 7 */ + uint8 unknown_2; /* '%1X' - SoF field 8 / Server field 10 */ + uint32 unknown_3; /* '%04X' - SoF field 9 / Server field 11 */ + uint8 unknown_4; /* '%1X' - SoF field 10 / Server field 12 */ + uint32 ornament_icon; /* '%05X' - SoF field 11 / Server field 13 */ + int hash; /* '%08X' - SoF field 12 / Server field 14 */ +}; }; //end namespace structs }; //end namespace SoF #endif /*SoF_STRUCTS_H_*/ - - - - - - - - - - diff --git a/common/patches/titanium.cpp b/common/patches/titanium.cpp index f9ad6a4ff..fb7faa4f9 100644 --- a/common/patches/titanium.cpp +++ b/common/patches/titanium.cpp @@ -7,6 +7,7 @@ #include "../races.h" #include "../eq_packet_structs.h" +#include "../misc_functions.h" #include "../string_util.h" #include "../item.h" #include "titanium_structs.h" @@ -28,6 +29,18 @@ namespace Titanium static inline uint32 TitaniumToServerSlot(int16 TitaniumSlot); static inline uint32 TitaniumToServerCorpseSlot(int16 TitaniumCorpse); + // server to client text link converters + static inline void ServerToTitaniumTextLinks(std::string& titaniumTextLink, const std::string& serverTextLink); + static inline bool DegenerateServerTextLinkBody(TextLinkBody_Struct& serverLinkBodyStruct, const std::string& serverLinkBody); + static inline void ServerToTitaniumTextLinkBodyStruct(structs::TextLinkBody_Struct& titaniumLinkBodyStruct, const TextLinkBody_Struct& serverLinkBodyStruct); + static inline bool GenerateTitaniumTextLinkBody(std::string& titaniumLinkBody, const structs::TextLinkBody_Struct& titaniumLinkBodyStruct); + + // client to server text link converters + static inline void TitaniumToServerTextLinks(std::string& serverTextLink, const std::string& titaniumTextLink); + static inline bool DegenerateTitaniumTextLinkBody(structs::TextLinkBody_Struct& titaniumLinkBodyStruct, const std::string& titaniumLinkBody); + static inline void TitaniumToServerTextLinkBodyStruct(TextLinkBody_Struct& serverLinkBodyStruct, const structs::TextLinkBody_Struct& titaniumLinkBodyStruct); + static inline bool GenerateServerTextLinkBody(std::string& serverLinkBody, const TextLinkBody_Struct& serverLinkBodyStruct); + void Register(EQStreamIdentifier &into) { //create our opcode manager if we havent already @@ -220,6 +233,35 @@ namespace Titanium FINISH_ENCODE(); } + ENCODE(OP_ChannelMessage) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + ChannelMessage_Struct *emu = (ChannelMessage_Struct *)in->pBuffer; + + unsigned char *__emu_buffer = in->pBuffer; + + std::string old_message = emu->message; + std::string new_message; + ServerToTitaniumTextLinks(new_message, old_message); + + in->size = sizeof(ChannelMessage_Struct) + new_message.length() + 1; + + in->pBuffer = new unsigned char[in->size]; + + char *OutBuffer = (char *)in->pBuffer; + + memcpy(OutBuffer, __emu_buffer, sizeof(ChannelMessage_Struct)); + + OutBuffer += sizeof(ChannelMessage_Struct); + + VARSTRUCT_ENCODE_STRING(OutBuffer, new_message.c_str()); + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + ENCODE(OP_CharInventory) { //consume the packet @@ -1070,6 +1112,44 @@ namespace Titanium FINISH_ENCODE(); } + ENCODE(OP_SpecialMesg) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + SpecialMesg_Struct *emu = (SpecialMesg_Struct *)in->pBuffer; + + unsigned char *__emu_buffer = in->pBuffer; + + std::string old_message = &emu->message[strlen(emu->sayer)]; + std::string new_message; + ServerToTitaniumTextLinks(new_message, old_message); + + //in->size = 3 + 4 + 4 + strlen(emu->sayer) + 1 + 12 + new_message.length() + 1; + in->size = 25 + strlen(emu->sayer) + new_message.length(); + in->pBuffer = new unsigned char[in->size]; + + char *OutBuffer = (char *)in->pBuffer; + + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[0]); + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[1]); + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[2]); + + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->msg_type); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->target_spawn_id); + + VARSTRUCT_ENCODE_STRING(OutBuffer, emu->sayer); + + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); + + VARSTRUCT_ENCODE_STRING(OutBuffer, new_message.c_str()); + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + ENCODE(OP_Track) { EQApplicationPacket *in = *p; @@ -1371,6 +1451,25 @@ namespace Titanium FINISH_DIRECT_DECODE(); } + DECODE(OP_ChannelMessage) + { + unsigned char *__eq_buffer = __packet->pBuffer; + + std::string old_message = (char *)&__eq_buffer[sizeof(ChannelMessage_Struct)]; + std::string new_message; + TitaniumToServerTextLinks(new_message, old_message); + + __packet->size = sizeof(ChannelMessage_Struct) + new_message.length() + 1; + __packet->pBuffer = new unsigned char[__packet->size]; + + ChannelMessage_Struct *emu = (ChannelMessage_Struct *)__packet->pBuffer; + + memcpy(emu, __eq_buffer, sizeof(ChannelMessage_Struct)); + strcpy(emu->message, new_message.c_str()); + + delete[] __eq_buffer; + } + DECODE(OP_CharacterCreate) { DECODE_LENGTH_EXACT(structs::CharCreate_Struct); @@ -1763,5 +1862,359 @@ namespace Titanium //uint32 ServerCorpse; return TitaniumCorpse; } + + static inline void ServerToTitaniumTextLinks(std::string& titaniumTextLink, const std::string& serverTextLink) + { + const char delimiter = 0x12; + +#if EQDEBUG >= 6 + _log(CHANNELS__ERROR, "TextLink(Server->Titanium): old message '%s'", serverTextLink.c_str()); + + if (consts::TEXT_LINK_BODY_LENGTH == EmuConstants::TEXT_LINK_BODY_LENGTH) { + _log(CHANNELS__ERROR, "TextLink(Server->Titanium): link size equal, no conversion necessary"); + titaniumTextLink = serverTextLink; + return; + } + + if (serverTextLink.find(delimiter) == std::string::npos) { + _log(CHANNELS__ERROR, "TextLink(Server->Titanium): delimiter not found, no conversion necessary"); + titaniumTextLink = serverTextLink; + return; + } + + bool conversion_error = false; + auto segments = SplitString(serverTextLink, delimiter); + + for (size_t iter = 1; iter < segments.size(); iter += 2) { + TextLinkBody_Struct old_body_data; + if (!DegenerateServerTextLinkBody(old_body_data, segments[iter].substr(0, EmuConstants::TEXT_LINK_BODY_LENGTH).c_str())) { + _log(CHANNELS__ERROR, "TextLink(Server->Titanium): body degeneration error '%s'", segments[iter].substr(0, EmuConstants::TEXT_LINK_BODY_LENGTH).c_str()); + conversion_error = true; + } + else { + _log(CHANNELS__ERROR, "TextLink(Server->Titanium): body degeneration success '%s'", segments[iter].substr(0, EmuConstants::TEXT_LINK_BODY_LENGTH).c_str()); + } + + structs::TextLinkBody_Struct new_body_data; + ServerToTitaniumTextLinkBodyStruct(new_body_data, old_body_data); + + std::string segment; + if (!GenerateTitaniumTextLinkBody(segment, new_body_data)) { + _log(CHANNELS__ERROR, "TextLink(Server->Titanium): body generation error '%s'", segment.c_str()); + conversion_error = true; + } + else { + _log(CHANNELS__ERROR, "TextLink(Server->Titanium): body generation success '%s'", segment.c_str()); + segment.append(segments[iter].substr(EmuConstants::TEXT_LINK_BODY_LENGTH).c_str()); + segments[iter] = segment.c_str(); + } + } + + if (conversion_error) { + _log(CHANNELS__ERROR, "TextLink(Server->Titanium): conversion error"); + titaniumTextLink = serverTextLink; + return; + } + + for (size_t iter = 0; iter < segments.size(); ++iter) { + if (iter & 1) { + titaniumTextLink.push_back(delimiter); + titaniumTextLink.append(segments[iter].c_str()); + titaniumTextLink.push_back(delimiter); + } + else { + titaniumTextLink.append(segments[iter].c_str()); + } + + _log(CHANNELS__ERROR, "TextLink(Server->Titanium): segment[%u] '%s'", iter, segments[iter].c_str()); + } + + _log(CHANNELS__ERROR, "TextLink(Server->Titanium): new message '%s'", titaniumTextLink.c_str()); +#else + if ((consts::TEXT_LINK_BODY_LENGTH == EmuConstants::TEXT_LINK_BODY_LENGTH) || (serverTextLink.find(delimiter) == std::string::npos)) { + titaniumTextLink = serverTextLink; + return; + } + + bool conversion_error = false; + auto segments = SplitString(serverTextLink, delimiter); + + for (size_t iter = 1; iter < segments.size(); iter += 2) { + TextLinkBody_Struct old_body_data; + if (!DegenerateServerTextLinkBody(old_body_data, segments[iter].substr(0, EmuConstants::TEXT_LINK_BODY_LENGTH).c_str())) { + conversion_error = true; + break; + } + + structs::TextLinkBody_Struct new_body_data; + ServerToTitaniumTextLinkBodyStruct(new_body_data, old_body_data); + + std::string segment; + if (!GenerateTitaniumTextLinkBody(segment, new_body_data)) { + conversion_error = true; + break; + } + else { + segment.append(segments[iter].substr(EmuConstants::TEXT_LINK_BODY_LENGTH).c_str()); + segments[iter] = segment.c_str(); + } + } + + if (conversion_error) { + _log(CHANNELS__ERROR, "TextLink(Server->Titanium): conversion error"); + titaniumTextLink = serverTextLink; + return; + } + + for (size_t iter = 0; iter < segments.size(); ++iter) { + if (iter & 1) { + titaniumTextLink.push_back(delimiter); + titaniumTextLink.append(segments[iter].c_str()); + titaniumTextLink.push_back(delimiter); + } + else { + titaniumTextLink.append(segments[iter].c_str()); + } + } +#endif + } + + static inline bool DegenerateServerTextLinkBody(TextLinkBody_Struct& serverLinkBodyStruct, const std::string& serverLinkBody) + { + memset(&serverLinkBodyStruct, 0, sizeof(TextLinkBody_Struct)); + if (serverLinkBody.length() != EmuConstants::TEXT_LINK_BODY_LENGTH) { return false; } + + serverLinkBodyStruct.unknown_1 = (uint8)strtol(serverLinkBody.substr(0, 1).c_str(), nullptr, 16); + serverLinkBodyStruct.item_id = (uint32)strtol(serverLinkBody.substr(1, 5).c_str(), nullptr, 16); + serverLinkBodyStruct.augment_1 = (uint32)strtol(serverLinkBody.substr(6, 5).c_str(), nullptr, 16); + serverLinkBodyStruct.augment_2 = (uint32)strtol(serverLinkBody.substr(11, 5).c_str(), nullptr, 16); + serverLinkBodyStruct.augment_3 = (uint32)strtol(serverLinkBody.substr(16, 5).c_str(), nullptr, 16); + serverLinkBodyStruct.augment_4 = (uint32)strtol(serverLinkBody.substr(21, 5).c_str(), nullptr, 16); + serverLinkBodyStruct.augment_5 = (uint32)strtol(serverLinkBody.substr(26, 5).c_str(), nullptr, 16); + serverLinkBodyStruct.augment_6 = (uint32)strtol(serverLinkBody.substr(31, 5).c_str(), nullptr, 16); + serverLinkBodyStruct.unknown_2 = (uint8)strtol(serverLinkBody.substr(36, 1).c_str(), nullptr, 16); + serverLinkBodyStruct.unknown_3 = (uint8)strtol(serverLinkBody.substr(37, 1).c_str(), nullptr, 16); + serverLinkBodyStruct.unknown_4 = (uint32)strtol(serverLinkBody.substr(38, 4).c_str(), nullptr, 16); + serverLinkBodyStruct.unknown_5 = (uint8)strtol(serverLinkBody.substr(42, 1).c_str(), nullptr, 16); + serverLinkBodyStruct.ornament_icon = (uint32)strtol(serverLinkBody.substr(43, 5).c_str(), nullptr, 16); + serverLinkBodyStruct.hash = (int)strtol(serverLinkBody.substr(48, 8).c_str(), nullptr, 16); + + return true; + } + + static inline void ServerToTitaniumTextLinkBodyStruct(structs::TextLinkBody_Struct& titaniumLinkBodyStruct, const TextLinkBody_Struct& serverLinkBodyStruct) + { + titaniumLinkBodyStruct.unknown_1 = serverLinkBodyStruct.unknown_1; + titaniumLinkBodyStruct.item_id = serverLinkBodyStruct.item_id; + titaniumLinkBodyStruct.augment_1 = serverLinkBodyStruct.augment_1; + titaniumLinkBodyStruct.augment_2 = serverLinkBodyStruct.augment_2; + titaniumLinkBodyStruct.augment_3 = serverLinkBodyStruct.augment_3; + titaniumLinkBodyStruct.augment_4 = serverLinkBodyStruct.augment_4; + titaniumLinkBodyStruct.augment_5 = serverLinkBodyStruct.augment_5; + titaniumLinkBodyStruct.unknown_2 = serverLinkBodyStruct.unknown_3; + titaniumLinkBodyStruct.unknown_3 = serverLinkBodyStruct.unknown_4; + titaniumLinkBodyStruct.unknown_4 = serverLinkBodyStruct.unknown_5; + titaniumLinkBodyStruct.hash = serverLinkBodyStruct.hash; + } + + static inline bool GenerateTitaniumTextLinkBody(std::string& titaniumLinkBody, const structs::TextLinkBody_Struct& titaniumLinkBodyStruct) + { + titaniumLinkBody = StringFormat( + "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%1X" "%08X", + titaniumLinkBodyStruct.unknown_1, + titaniumLinkBodyStruct.item_id, + titaniumLinkBodyStruct.augment_1, + titaniumLinkBodyStruct.augment_2, + titaniumLinkBodyStruct.augment_3, + titaniumLinkBodyStruct.augment_4, + titaniumLinkBodyStruct.augment_5, + titaniumLinkBodyStruct.unknown_2, + titaniumLinkBodyStruct.unknown_3, + titaniumLinkBodyStruct.unknown_4, + titaniumLinkBodyStruct.hash + ); + + if (titaniumLinkBody.length() != consts::TEXT_LINK_BODY_LENGTH) { return false; } + return true; + } + + static inline void TitaniumToServerTextLinks(std::string& serverTextLink, const std::string& titaniumTextLink) + { + const char delimiter = 0x12; + +#if EQDEBUG >= 6 + _log(CHANNELS__ERROR, "TextLink(Titanium->Server): old message '%s'", titaniumTextLink.c_str()); + + if (EmuConstants::TEXT_LINK_BODY_LENGTH == consts::TEXT_LINK_BODY_LENGTH) { + _log(CHANNELS__ERROR, "TextLink(Titanium->Server): link size equal, no conversion necessary"); + serverTextLink = titaniumTextLink; + return; + } + + if (titaniumTextLink.find(delimiter) == std::string::npos) { + _log(CHANNELS__ERROR, "TextLink(Titanium->Server): delimiter not found, no conversion necessary"); + serverTextLink = titaniumTextLink; + return; + } + + bool conversion_error = false; + auto segments = SplitString(titaniumTextLink, delimiter); + + for (size_t iter = 1; iter < segments.size(); iter += 2) { + structs::TextLinkBody_Struct old_body_data; + if (!DegenerateTitaniumTextLinkBody(old_body_data, segments[iter].substr(0, consts::TEXT_LINK_BODY_LENGTH).c_str())) { + _log(CHANNELS__ERROR, "TextLink(Titanium->Server): body degeneration error '%s'", segments[iter].substr(0, consts::TEXT_LINK_BODY_LENGTH).c_str()); + conversion_error = true; + } + else { + _log(CHANNELS__ERROR, "TextLink(Titanium->Server): body degeneration success '%s'", segments[iter].substr(0, consts::TEXT_LINK_BODY_LENGTH).c_str()); + } + + TextLinkBody_Struct new_body_data; + TitaniumToServerTextLinkBodyStruct(new_body_data, old_body_data); + + std::string segment; + if (!GenerateServerTextLinkBody(segment, new_body_data)) { + _log(CHANNELS__ERROR, "TextLink(Titanium->Server): body generation error '%s'", segment.c_str()); + conversion_error = true; + } + else { + _log(CHANNELS__ERROR, "TextLink(Titanium->Server): body generation success '%s'", segment.c_str()); + segment.append(segments[iter].substr(consts::TEXT_LINK_BODY_LENGTH).c_str()); + segments[iter] = segment.c_str(); + } + } + + if (conversion_error) { + _log(CHANNELS__ERROR, "TextLink(Titanium->Server): conversion error"); + serverTextLink = titaniumTextLink; + return; + } + + for (size_t iter = 0; iter < segments.size(); ++iter) { + if (iter & 1) { + serverTextLink.push_back(delimiter); + serverTextLink.append(segments[iter].c_str()); + serverTextLink.push_back(delimiter); + } + else { + serverTextLink.append(segments[iter].c_str()); + } + + _log(CHANNELS__ERROR, "TextLink(Titanium->Server): segment[%u] '%s'", iter, segments[iter].c_str()); + } + + _log(CHANNELS__ERROR, "TextLink(Titanium->Server): new message '%s'", serverTextLink.c_str()); +#else + if ((EmuConstants::TEXT_LINK_BODY_LENGTH == consts::TEXT_LINK_BODY_LENGTH) || (titaniumTextLink.find(delimiter) == std::string::npos)) { + serverTextLink = titaniumTextLink; + return; + } + + bool conversion_error = false; + auto segments = SplitString(titaniumTextLink, delimiter); + + for (size_t iter = 1; iter < segments.size(); iter += 2) { + structs::TextLinkBody_Struct old_body_data; + if (!DegenerateTitaniumTextLinkBody(old_body_data, segments[iter].substr(0, consts::TEXT_LINK_BODY_LENGTH).c_str())) { + conversion_error = true; + break; + } + + TextLinkBody_Struct new_body_data; + TitaniumToServerTextLinkBodyStruct(new_body_data, old_body_data); + + std::string segment; + if (!GenerateServerTextLinkBody(segment, new_body_data)) { + conversion_error = true; + break; + } + else { + segment.append(segments[iter].substr(consts::TEXT_LINK_BODY_LENGTH).c_str()); + segments[iter] = segment.c_str(); + } + } + + if (conversion_error) { + _log(CHANNELS__ERROR, "TextLink(Titanium->Server): conversion error"); + serverTextLink = titaniumTextLink; + return; + } + + for (size_t iter = 0; iter < segments.size(); ++iter) { + if (iter & 1) { + serverTextLink.push_back(delimiter); + serverTextLink.append(segments[iter].c_str()); + serverTextLink.push_back(delimiter); + } + else { + serverTextLink.append(segments[iter].c_str()); + } + } +#endif + } + + static inline bool DegenerateTitaniumTextLinkBody(structs::TextLinkBody_Struct& titaniumLinkBodyStruct, const std::string& titaniumLinkBody) + { + // 6.2: "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%1X" "%08X" + memset(&titaniumLinkBodyStruct, 0, sizeof(structs::TextLinkBody_Struct)); + if (titaniumLinkBody.length() != consts::TEXT_LINK_BODY_LENGTH) { return false; } + + titaniumLinkBodyStruct.unknown_1 = (uint8)strtol(titaniumLinkBody.substr(0, 1).c_str(), nullptr, 16); + titaniumLinkBodyStruct.item_id = (uint32)strtol(titaniumLinkBody.substr(1, 5).c_str(), nullptr, 16); + titaniumLinkBodyStruct.augment_1 = (uint32)strtol(titaniumLinkBody.substr(6, 5).c_str(), nullptr, 16); + titaniumLinkBodyStruct.augment_2 = (uint32)strtol(titaniumLinkBody.substr(11, 5).c_str(), nullptr, 16); + titaniumLinkBodyStruct.augment_3 = (uint32)strtol(titaniumLinkBody.substr(16, 5).c_str(), nullptr, 16); + titaniumLinkBodyStruct.augment_4 = (uint32)strtol(titaniumLinkBody.substr(21, 5).c_str(), nullptr, 16); + titaniumLinkBodyStruct.augment_5 = (uint32)strtol(titaniumLinkBody.substr(26, 5).c_str(), nullptr, 16); + titaniumLinkBodyStruct.unknown_2 = (uint8)strtol(titaniumLinkBody.substr(31, 1).c_str(), nullptr, 16); + titaniumLinkBodyStruct.unknown_3 = (uint32)strtol(titaniumLinkBody.substr(32, 4).c_str(), nullptr, 16); + titaniumLinkBodyStruct.unknown_4 = (uint8)strtol(titaniumLinkBody.substr(36, 1).c_str(), nullptr, 16); + titaniumLinkBodyStruct.hash = (int)strtol(titaniumLinkBody.substr(37, 8).c_str(), nullptr, 16); + + return true; + } + + static inline void TitaniumToServerTextLinkBodyStruct(TextLinkBody_Struct& serverLinkBodyStruct, const structs::TextLinkBody_Struct& titaniumLinkBodyStruct) + { + serverLinkBodyStruct.unknown_1 = titaniumLinkBodyStruct.unknown_1; + serverLinkBodyStruct.item_id = titaniumLinkBodyStruct.item_id; + serverLinkBodyStruct.augment_1 = titaniumLinkBodyStruct.augment_1; + serverLinkBodyStruct.augment_2 = titaniumLinkBodyStruct.augment_2; + serverLinkBodyStruct.augment_3 = titaniumLinkBodyStruct.augment_3; + serverLinkBodyStruct.augment_4 = titaniumLinkBodyStruct.augment_4; + serverLinkBodyStruct.augment_5 = titaniumLinkBodyStruct.augment_5; + serverLinkBodyStruct.augment_6 = NOT_USED; + serverLinkBodyStruct.unknown_2 = NOT_USED; + serverLinkBodyStruct.unknown_3 = titaniumLinkBodyStruct.unknown_2; + serverLinkBodyStruct.unknown_4 = titaniumLinkBodyStruct.unknown_3; + serverLinkBodyStruct.unknown_5 = titaniumLinkBodyStruct.unknown_4; + serverLinkBodyStruct.ornament_icon = NOT_USED; + serverLinkBodyStruct.hash = titaniumLinkBodyStruct.hash; + } + + static inline bool GenerateServerTextLinkBody(std::string& serverLinkBody, const TextLinkBody_Struct& serverLinkBodyStruct) + { + serverLinkBody = StringFormat( + "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%01X" "%01X" "%04X" "%01X" "%05X" "%08X", + serverLinkBodyStruct.unknown_1, + serverLinkBodyStruct.item_id, + serverLinkBodyStruct.augment_1, + serverLinkBodyStruct.augment_2, + serverLinkBodyStruct.augment_3, + serverLinkBodyStruct.augment_4, + serverLinkBodyStruct.augment_5, + serverLinkBodyStruct.augment_6, + serverLinkBodyStruct.unknown_2, + serverLinkBodyStruct.unknown_3, + serverLinkBodyStruct.unknown_4, + serverLinkBodyStruct.unknown_5, + serverLinkBodyStruct.ornament_icon, + serverLinkBodyStruct.hash + ); + + if (serverLinkBody.length() != EmuConstants::TEXT_LINK_BODY_LENGTH) { return false; } + return true; + } } // end namespace Titanium diff --git a/common/patches/titanium_constants.h b/common/patches/titanium_constants.h index 85e525fe8..ff44513c2 100644 --- a/common/patches/titanium_constants.h +++ b/common/patches/titanium_constants.h @@ -176,6 +176,8 @@ namespace Titanium { static const uint32 BANDOLIERS_COUNT = 4; // count = number of bandolier instances static const uint32 BANDOLIER_SIZE = 4; // size = number of equipment slots in bandolier instance static const uint32 POTION_BELT_SIZE = 4; + + static const size_t TEXT_LINK_BODY_LENGTH = 45; } namespace limits { diff --git a/common/patches/titanium_ops.h b/common/patches/titanium_ops.h index 338558602..a3d2e71af 100644 --- a/common/patches/titanium_ops.h +++ b/common/patches/titanium_ops.h @@ -4,6 +4,7 @@ E(OP_AdventureMerchantSell) E(OP_ApplyPoison) E(OP_BazaarSearch) E(OP_BecomeTrader) +E(OP_ChannelMessage) E(OP_CharInventory) E(OP_DeleteCharge) E(OP_DeleteItem) @@ -35,6 +36,7 @@ E(OP_RespondAA) E(OP_SendCharInfo) E(OP_SendAATable) E(OP_ShopPlayerSell) +E(OP_SpecialMesg) E(OP_Track) E(OP_Trader) E(OP_TraderBuy) @@ -49,6 +51,7 @@ D(OP_AdventureMerchantSell) D(OP_ApplyPoison) D(OP_AugmentItem) D(OP_CastSpell) +D(OP_ChannelMessage) D(OP_CharacterCreate) D(OP_Consume) D(OP_DeleteItem) diff --git a/common/patches/titanium_structs.h b/common/patches/titanium_structs.h index 0b5b1ec25..7fb2df5cd 100644 --- a/common/patches/titanium_structs.h +++ b/common/patches/titanium_structs.h @@ -3329,19 +3329,23 @@ struct LFGuild_GuildToggle_Struct /*616*/ }; +// 6.2: "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%1X" "%08X" +struct TextLinkBody_Struct +{ + uint8 unknown_1; /* '%1X' - Titanium field 1 / Server field 1 */ + uint32 item_id; /* '%05X' - Titanium field 2 / Server field 2 */ + uint32 augment_1; /* '%05X' - Titanium field 3 / Server field 3 */ + uint32 augment_2; /* '%05X' - Titanium field 4 / Server field 4 */ + uint32 augment_3; /* '%05X' - Titanium field 5 / Server field 5 */ + uint32 augment_4; /* '%05X' - Titanium field 6 / Server field 6 */ + uint32 augment_5; /* '%05X' - Titanium field 7 / Server field 7 */ + uint8 unknown_2; /* '%1X' - Titanium field 8 / Server field 10 */ + uint32 unknown_3; /* '%04X' - Titanium field 9 / Server field 11 */ + uint8 unknown_4; /* '%1X' - Titanium field 10 / Server field 12 */ + int hash; /* '%08X' - Titanium field 11 / Server field 14 */ +}; + }; //end namespace structs }; //end namespace Titanium - - #endif /*Titanium_STRUCTS_H_*/ - - - - - - - - - - diff --git a/common/patches/underfoot.cpp b/common/patches/underfoot.cpp index 5ee38f586..e45dae148 100644 --- a/common/patches/underfoot.cpp +++ b/common/patches/underfoot.cpp @@ -31,6 +31,18 @@ namespace Underfoot static inline uint32 UnderfootToServerSlot(uint32 UnderfootSlot); static inline uint32 UnderfootToServerCorpseSlot(uint32 UnderfootCorpse); + // server to client text link converters + static inline void ServerToUnderfootTextLinks(std::string& underfootTextLink, const std::string& serverTextLink); + static inline bool DegenerateServerTextLinkBody(TextLinkBody_Struct& serverLinkBodyStruct, const std::string& serverLinkBody); + static inline void ServerToUnderfootTextLinkBodyStruct(structs::TextLinkBody_Struct& underfootLinkBodyStruct, const TextLinkBody_Struct& serverLinkBodyStruct); + static inline bool GenerateUnderfootTextLinkBody(std::string& underfootLinkBody, const structs::TextLinkBody_Struct& underfootLinkBodyStruct); + + // client to server text link converters + static inline void UnderfootToServerTextLinks(std::string& serverTextLink, const std::string& underfootTextLink); + static inline bool DegenerateUnderfootTextLinkBody(structs::TextLinkBody_Struct& underfootLinkBodyStruct, const std::string& underfootLinkBody); + static inline void UnderfootToServerTextLinkBodyStruct(TextLinkBody_Struct& serverLinkBodyStruct, const structs::TextLinkBody_Struct& underfootLinkBodyStruct); + static inline bool GenerateServerTextLinkBody(std::string& serverLinkBody, const TextLinkBody_Struct& serverLinkBodyStruct); + void Register(EQStreamIdentifier &into) { //create our opcode manager if we havent already @@ -432,7 +444,12 @@ namespace Underfoot unsigned char *__emu_buffer = in->pBuffer; - in->size = strlen(emu->sender) + 1 + strlen(emu->targetname) + 1 + strlen(emu->message) + 1 + 36; + std::string old_message = emu->message; + std::string new_message; + ServerToUnderfootTextLinks(new_message, old_message); + + //in->size = strlen(emu->sender) + 1 + strlen(emu->targetname) + 1 + strlen(emu->message) + 1 + 36; + in->size = strlen(emu->sender) + strlen(emu->targetname) + new_message.length() + 39; in->pBuffer = new unsigned char[in->size]; @@ -446,7 +463,7 @@ namespace Underfoot VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, 0); // Unknown VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->skill_in_language); - VARSTRUCT_ENCODE_STRING(OutBuffer, emu->message); + VARSTRUCT_ENCODE_STRING(OutBuffer, new_message.c_str()); VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown @@ -2298,6 +2315,44 @@ namespace Underfoot FINISH_ENCODE(); } + ENCODE(OP_SpecialMesg) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + SpecialMesg_Struct *emu = (SpecialMesg_Struct *)in->pBuffer; + + unsigned char *__emu_buffer = in->pBuffer; + + std::string old_message = &emu->message[strlen(emu->sayer)]; + std::string new_message; + ServerToUnderfootTextLinks(new_message, old_message); + + //in->size = 3 + 4 + 4 + strlen(emu->sayer) + 1 + 12 + new_message.length() + 1; + in->size = 25 + strlen(emu->sayer) + new_message.length(); + in->pBuffer = new unsigned char[in->size]; + + char *OutBuffer = (char *)in->pBuffer; + + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[0]); + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[1]); + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->header[2]); + + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->msg_type); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->target_spawn_id); + + VARSTRUCT_ENCODE_STRING(OutBuffer, emu->sayer); + + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); + + VARSTRUCT_ENCODE_STRING(OutBuffer, new_message.c_str()); + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + ENCODE(OP_Stun) { ENCODE_LENGTH_EXACT(Stun_Struct); @@ -3036,7 +3091,13 @@ namespace Underfoot uint32 Skill = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); - __packet->size = sizeof(ChannelMessage_Struct)+strlen(InBuffer) + 1; + std::string old_message = InBuffer; + std::string new_message; + UnderfootToServerTextLinks(new_message, old_message); + + //__packet->size = sizeof(ChannelMessage_Struct)+strlen(InBuffer) + 1; + __packet->size = sizeof(ChannelMessage_Struct) + new_message.length() + 1; + __packet->pBuffer = new unsigned char[__packet->size]; ChannelMessage_Struct *emu = (ChannelMessage_Struct *)__packet->pBuffer; @@ -3045,7 +3106,7 @@ namespace Underfoot emu->language = Language; emu->chan_num = Channel; emu->skill_in_language = Skill; - strcpy(emu->message, InBuffer); + strcpy(emu->message, new_message.c_str()); delete[] __eq_buffer; } @@ -4155,5 +4216,362 @@ namespace Underfoot //uint32 ServerCorpse; return (UnderfootCorpse - 1); } + + static inline void ServerToUnderfootTextLinks(std::string& underfootTextLink, const std::string& serverTextLink) + { + const char delimiter = 0x12; + +#if EQDEBUG >= 6 + _log(CHANNELS__ERROR, "TextLink(Server->Underfoot): old message '%s'", serverTextLink.c_str()); + + if (consts::TEXT_LINK_BODY_LENGTH == EmuConstants::TEXT_LINK_BODY_LENGTH) { + _log(CHANNELS__ERROR, "TextLink(Server->Underfoot): link size equal, no conversion necessary"); + underfootTextLink = serverTextLink; + return; + } + + if (serverTextLink.find(delimiter) == std::string::npos) { + _log(CHANNELS__ERROR, "TextLink(Server->Underfoot): delimiter not found, no conversion necessary"); + underfootTextLink = serverTextLink; + return; + } + + bool conversion_error = false; + auto segments = SplitString(serverTextLink, delimiter); + + for (size_t iter = 1; iter < segments.size(); iter += 2) { + TextLinkBody_Struct old_body_data; + if (!DegenerateServerTextLinkBody(old_body_data, segments[iter].substr(0, EmuConstants::TEXT_LINK_BODY_LENGTH).c_str())) { + _log(CHANNELS__ERROR, "TextLink(Server->Underfoot): body degeneration error '%s'", segments[iter].substr(0, EmuConstants::TEXT_LINK_BODY_LENGTH).c_str()); + conversion_error = true; + } + else { + _log(CHANNELS__ERROR, "TextLink(Server->Underfoot): body degeneration success '%s'", segments[iter].substr(0, EmuConstants::TEXT_LINK_BODY_LENGTH).c_str()); + } + + structs::TextLinkBody_Struct new_body_data; + ServerToUnderfootTextLinkBodyStruct(new_body_data, old_body_data); + + std::string segment; + if (!GenerateUnderfootTextLinkBody(segment, new_body_data)) { + _log(CHANNELS__ERROR, "TextLink(Server->Underfoot): body generation error '%s'", segment.c_str()); + conversion_error = true; + } + else { + _log(CHANNELS__ERROR, "TextLink(Server->Underfoot): body generation success '%s'", segment.c_str()); + segment.append(segments[iter].substr(EmuConstants::TEXT_LINK_BODY_LENGTH).c_str()); + segments[iter] = segment.c_str(); + } + } + + if (conversion_error) { + _log(CHANNELS__ERROR, "TextLink(Server->Underfoot): conversion error"); + underfootTextLink = serverTextLink; + return; + } + + for (size_t iter = 0; iter < segments.size(); ++iter) { + if (iter & 1) { + underfootTextLink.push_back(delimiter); + underfootTextLink.append(segments[iter].c_str()); + underfootTextLink.push_back(delimiter); + } + else { + underfootTextLink.append(segments[iter].c_str()); + } + + _log(CHANNELS__ERROR, "TextLink(Server->Underfoot): segment[%u] '%s'", iter, segments[iter].c_str()); + } + + _log(CHANNELS__ERROR, "TextLink(Server->Underfoot): new message '%s'", underfootTextLink.c_str()); +#else + if ((consts::TEXT_LINK_BODY_LENGTH == EmuConstants::TEXT_LINK_BODY_LENGTH) || (serverTextLink.find(delimiter) == std::string::npos)) { + underfootTextLink = serverTextLink; + return; + } + + bool conversion_error = false; + auto segments = SplitString(serverTextLink, delimiter); + + for (size_t iter = 1; iter < segments.size(); iter += 2) { + TextLinkBody_Struct old_body_data; + if (!DegenerateServerTextLinkBody(old_body_data, segments[iter].substr(0, EmuConstants::TEXT_LINK_BODY_LENGTH).c_str())) { + conversion_error = true; + break; + } + + structs::TextLinkBody_Struct new_body_data; + ServerToUnderfootTextLinkBodyStruct(new_body_data, old_body_data); + + std::string segment; + if (!GenerateUnderfootTextLinkBody(segment, new_body_data)) { + conversion_error = true; + break; + } + else { + segment.append(segments[iter].substr(EmuConstants::TEXT_LINK_BODY_LENGTH).c_str()); + segments[iter] = segment.c_str(); + } + } + + if (conversion_error) { + _log(CHANNELS__ERROR, "TextLink(Server->Underfoot): conversion error"); + underfootTextLink = serverTextLink; + return; + } + + for (size_t iter = 0; iter < segments.size(); ++iter) { + if (iter & 1) { + underfootTextLink.push_back(delimiter); + underfootTextLink.append(segments[iter].c_str()); + underfootTextLink.push_back(delimiter); + } + else { + underfootTextLink.append(segments[iter].c_str()); + } + } +#endif + } + + static inline bool DegenerateServerTextLinkBody(TextLinkBody_Struct& serverLinkBodyStruct, const std::string& serverLinkBody) + { + memset(&serverLinkBodyStruct, 0, sizeof(TextLinkBody_Struct)); + if (serverLinkBody.length() != EmuConstants::TEXT_LINK_BODY_LENGTH) { return false; } + + serverLinkBodyStruct.unknown_1 = (uint8)strtol(serverLinkBody.substr(0, 1).c_str(), nullptr, 16); + serverLinkBodyStruct.item_id = (uint32)strtol(serverLinkBody.substr(1, 5).c_str(), nullptr, 16); + serverLinkBodyStruct.augment_1 = (uint32)strtol(serverLinkBody.substr(6, 5).c_str(), nullptr, 16); + serverLinkBodyStruct.augment_2 = (uint32)strtol(serverLinkBody.substr(11, 5).c_str(), nullptr, 16); + serverLinkBodyStruct.augment_3 = (uint32)strtol(serverLinkBody.substr(16, 5).c_str(), nullptr, 16); + serverLinkBodyStruct.augment_4 = (uint32)strtol(serverLinkBody.substr(21, 5).c_str(), nullptr, 16); + serverLinkBodyStruct.augment_5 = (uint32)strtol(serverLinkBody.substr(26, 5).c_str(), nullptr, 16); + serverLinkBodyStruct.augment_6 = (uint32)strtol(serverLinkBody.substr(31, 5).c_str(), nullptr, 16); + serverLinkBodyStruct.unknown_2 = (uint8)strtol(serverLinkBody.substr(36, 1).c_str(), nullptr, 16); + serverLinkBodyStruct.unknown_3 = (uint8)strtol(serverLinkBody.substr(37, 1).c_str(), nullptr, 16); + serverLinkBodyStruct.unknown_4 = (uint32)strtol(serverLinkBody.substr(38, 4).c_str(), nullptr, 16); + serverLinkBodyStruct.unknown_5 = (uint8)strtol(serverLinkBody.substr(42, 1).c_str(), nullptr, 16); + serverLinkBodyStruct.ornament_icon = (uint32)strtol(serverLinkBody.substr(43, 5).c_str(), nullptr, 16); + serverLinkBodyStruct.hash = (int)strtol(serverLinkBody.substr(48, 8).c_str(), nullptr, 16); + + return true; + } + + static inline void ServerToUnderfootTextLinkBodyStruct(structs::TextLinkBody_Struct& underfootLinkBodyStruct, const TextLinkBody_Struct& serverLinkBodyStruct) + { + underfootLinkBodyStruct.unknown_1 = serverLinkBodyStruct.unknown_1; + underfootLinkBodyStruct.item_id = serverLinkBodyStruct.item_id; + underfootLinkBodyStruct.augment_1 = serverLinkBodyStruct.augment_1; + underfootLinkBodyStruct.augment_2 = serverLinkBodyStruct.augment_2; + underfootLinkBodyStruct.augment_3 = serverLinkBodyStruct.augment_3; + underfootLinkBodyStruct.augment_4 = serverLinkBodyStruct.augment_4; + underfootLinkBodyStruct.augment_5 = serverLinkBodyStruct.augment_5; + underfootLinkBodyStruct.unknown_2 = serverLinkBodyStruct.unknown_3; + underfootLinkBodyStruct.unknown_3 = serverLinkBodyStruct.unknown_4; + underfootLinkBodyStruct.unknown_4 = serverLinkBodyStruct.unknown_5; + underfootLinkBodyStruct.ornament_icon = serverLinkBodyStruct.ornament_icon; + underfootLinkBodyStruct.hash = serverLinkBodyStruct.hash; + } + + static inline bool GenerateUnderfootTextLinkBody(std::string& underfootLinkBody, const structs::TextLinkBody_Struct& underfootLinkBodyStruct) + { + underfootLinkBody = StringFormat( + "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%1X" "%05X" "%08X", + underfootLinkBodyStruct.unknown_1, + underfootLinkBodyStruct.item_id, + underfootLinkBodyStruct.augment_1, + underfootLinkBodyStruct.augment_2, + underfootLinkBodyStruct.augment_3, + underfootLinkBodyStruct.augment_4, + underfootLinkBodyStruct.augment_5, + underfootLinkBodyStruct.unknown_2, + underfootLinkBodyStruct.unknown_3, + underfootLinkBodyStruct.unknown_4, + underfootLinkBodyStruct.ornament_icon, + underfootLinkBodyStruct.hash + ); + + if (underfootLinkBody.length() != consts::TEXT_LINK_BODY_LENGTH) { return false; } + return true; + } + + static inline void UnderfootToServerTextLinks(std::string& serverTextLink, const std::string& underfootTextLink) + { + const char delimiter = 0x12; + +#if EQDEBUG >= 6 + _log(CHANNELS__ERROR, "TextLink(Underfoot->Server): old message '%s'", underfootTextLink.c_str()); + + if (EmuConstants::TEXT_LINK_BODY_LENGTH == consts::TEXT_LINK_BODY_LENGTH) { + _log(CHANNELS__ERROR, "TextLink(Underfoot->Server): link size equal, no conversion necessary"); + serverTextLink = underfootTextLink; + return; + } + + if (underfootTextLink.find(delimiter) == std::string::npos) { + _log(CHANNELS__ERROR, "TextLink(Underfoot->Server): delimiter not found, no conversion necessary"); + serverTextLink = underfootTextLink; + return; + } + + bool conversion_error = false; + auto segments = SplitString(underfootTextLink, delimiter); + + for (size_t iter = 1; iter < segments.size(); iter += 2) { + structs::TextLinkBody_Struct old_body_data; + if (!DegenerateUnderfootTextLinkBody(old_body_data, segments[iter].substr(0, consts::TEXT_LINK_BODY_LENGTH).c_str())) { + _log(CHANNELS__ERROR, "TextLink(Underfoot->Server): body degeneration error '%s'", segments[iter].substr(0, consts::TEXT_LINK_BODY_LENGTH).c_str()); + conversion_error = true; + } + else { + _log(CHANNELS__ERROR, "TextLink(Underfoot->Server): body degeneration success '%s'", segments[iter].substr(0, consts::TEXT_LINK_BODY_LENGTH).c_str()); + } + + TextLinkBody_Struct new_body_data; + UnderfootToServerTextLinkBodyStruct(new_body_data, old_body_data); + + std::string segment; + if (!GenerateServerTextLinkBody(segment, new_body_data)) { + _log(CHANNELS__ERROR, "TextLink(Underfoot->Server): body generation error '%s'", segment.c_str()); + conversion_error = true; + } + else { + _log(CHANNELS__ERROR, "TextLink(Underfoot->Server): body generation success '%s'", segment.c_str()); + segment.append(segments[iter].substr(consts::TEXT_LINK_BODY_LENGTH).c_str()); + segments[iter] = segment.c_str(); + } + } + + if (conversion_error) { + _log(CHANNELS__ERROR, "TextLink(Underfoot->Server): conversion error"); + serverTextLink = underfootTextLink; + return; + } + + for (size_t iter = 0; iter < segments.size(); ++iter) { + if (iter & 1) { + serverTextLink.push_back(delimiter); + serverTextLink.append(segments[iter].c_str()); + serverTextLink.push_back(delimiter); + } + else { + serverTextLink.append(segments[iter].c_str()); + } + + _log(CHANNELS__ERROR, "TextLink(Underfoot->Server): segment[%u] '%s'", iter, segments[iter].c_str()); + } + + _log(CHANNELS__ERROR, "TextLink(Underfoot->Server): new message '%s'", serverTextLink.c_str()); +#else + if ((EmuConstants::TEXT_LINK_BODY_LENGTH == consts::TEXT_LINK_BODY_LENGTH) || (underfootTextLink.find(delimiter) == std::string::npos)) { + serverTextLink = underfootTextLink; + return; + } + + bool conversion_error = false; + auto segments = SplitString(underfootTextLink, delimiter); + + for (size_t iter = 1; iter < segments.size(); iter += 2) { + structs::TextLinkBody_Struct old_body_data; + if (!DegenerateUnderfootTextLinkBody(old_body_data, segments[iter].substr(0, consts::TEXT_LINK_BODY_LENGTH).c_str())) { + conversion_error = true; + break; + } + + TextLinkBody_Struct new_body_data; + UnderfootToServerTextLinkBodyStruct(new_body_data, old_body_data); + + std::string segment; + if (!GenerateServerTextLinkBody(segment, new_body_data)) { + conversion_error = true; + break; + } + else { + segment.append(segments[iter].substr(consts::TEXT_LINK_BODY_LENGTH).c_str()); + segments[iter] = segment.c_str(); + } + } + + if (conversion_error) { + _log(CHANNELS__ERROR, "TextLink(Underfoot->Server): conversion error"); + serverTextLink = underfootTextLink; + return; + } + + for (size_t iter = 0; iter < segments.size(); ++iter) { + if (iter & 1) { + serverTextLink.push_back(delimiter); + serverTextLink.append(segments[iter].c_str()); + serverTextLink.push_back(delimiter); + } + else { + serverTextLink.append(segments[iter].c_str()); + } + } +#endif + } + + static inline bool DegenerateUnderfootTextLinkBody(structs::TextLinkBody_Struct& underfootLinkBodyStruct, const std::string& underfootLinkBody) + { + // SoF: "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%1X" "%05X" "%08X" + memset(&underfootLinkBodyStruct, 0, sizeof(structs::TextLinkBody_Struct)); + if (underfootLinkBody.length() != consts::TEXT_LINK_BODY_LENGTH) { return false; } + + underfootLinkBodyStruct.unknown_1 = (uint8)strtol(underfootLinkBody.substr(0, 1).c_str(), nullptr, 16); + underfootLinkBodyStruct.item_id = (uint32)strtol(underfootLinkBody.substr(1, 5).c_str(), nullptr, 16); + underfootLinkBodyStruct.augment_1 = (uint32)strtol(underfootLinkBody.substr(6, 5).c_str(), nullptr, 16); + underfootLinkBodyStruct.augment_2 = (uint32)strtol(underfootLinkBody.substr(11, 5).c_str(), nullptr, 16); + underfootLinkBodyStruct.augment_3 = (uint32)strtol(underfootLinkBody.substr(16, 5).c_str(), nullptr, 16); + underfootLinkBodyStruct.augment_4 = (uint32)strtol(underfootLinkBody.substr(21, 5).c_str(), nullptr, 16); + underfootLinkBodyStruct.augment_5 = (uint32)strtol(underfootLinkBody.substr(26, 5).c_str(), nullptr, 16); + underfootLinkBodyStruct.unknown_2 = (uint8)strtol(underfootLinkBody.substr(31, 1).c_str(), nullptr, 16); + underfootLinkBodyStruct.unknown_3 = (uint32)strtol(underfootLinkBody.substr(32, 4).c_str(), nullptr, 16); + underfootLinkBodyStruct.unknown_4 = (uint8)strtol(underfootLinkBody.substr(36, 1).c_str(), nullptr, 16); + underfootLinkBodyStruct.ornament_icon = (uint32)strtol(underfootLinkBody.substr(37, 5).c_str(), nullptr, 16); + underfootLinkBodyStruct.hash = (int)strtol(underfootLinkBody.substr(42, 8).c_str(), nullptr, 16); + + return true; + } + + static inline void UnderfootToServerTextLinkBodyStruct(TextLinkBody_Struct& serverLinkBodyStruct, const structs::TextLinkBody_Struct& underfootLinkBodyStruct) + { + serverLinkBodyStruct.unknown_1 = underfootLinkBodyStruct.unknown_1; + serverLinkBodyStruct.item_id = underfootLinkBodyStruct.item_id; + serverLinkBodyStruct.augment_1 = underfootLinkBodyStruct.augment_1; + serverLinkBodyStruct.augment_2 = underfootLinkBodyStruct.augment_2; + serverLinkBodyStruct.augment_3 = underfootLinkBodyStruct.augment_3; + serverLinkBodyStruct.augment_4 = underfootLinkBodyStruct.augment_4; + serverLinkBodyStruct.augment_5 = underfootLinkBodyStruct.augment_5; + serverLinkBodyStruct.augment_6 = NOT_USED; + serverLinkBodyStruct.unknown_2 = NOT_USED; + serverLinkBodyStruct.unknown_3 = underfootLinkBodyStruct.unknown_2; + serverLinkBodyStruct.unknown_4 = underfootLinkBodyStruct.unknown_3; + serverLinkBodyStruct.unknown_5 = underfootLinkBodyStruct.unknown_4; + serverLinkBodyStruct.ornament_icon = underfootLinkBodyStruct.ornament_icon; + serverLinkBodyStruct.hash = underfootLinkBodyStruct.hash; + } + + static inline bool GenerateServerTextLinkBody(std::string& serverLinkBody, const TextLinkBody_Struct& serverLinkBodyStruct) + { + serverLinkBody = StringFormat( + "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%01X" "%01X" "%04X" "%01X" "%05X" "%08X", + serverLinkBodyStruct.unknown_1, + serverLinkBodyStruct.item_id, + serverLinkBodyStruct.augment_1, + serverLinkBodyStruct.augment_2, + serverLinkBodyStruct.augment_3, + serverLinkBodyStruct.augment_4, + serverLinkBodyStruct.augment_5, + serverLinkBodyStruct.augment_6, + serverLinkBodyStruct.unknown_2, + serverLinkBodyStruct.unknown_3, + serverLinkBodyStruct.unknown_4, + serverLinkBodyStruct.unknown_5, + serverLinkBodyStruct.ornament_icon, + serverLinkBodyStruct.hash + ); + + if (serverLinkBody.length() != EmuConstants::TEXT_LINK_BODY_LENGTH) { return false; } + return true; + } } // end namespace Underfoot diff --git a/common/patches/underfoot_constants.h b/common/patches/underfoot_constants.h index b89a4f255..a656c56eb 100644 --- a/common/patches/underfoot_constants.h +++ b/common/patches/underfoot_constants.h @@ -177,6 +177,8 @@ namespace Underfoot { static const uint32 BANDOLIERS_COUNT = 20; // count = number of bandolier instances static const uint32 BANDOLIER_SIZE = 4; // size = number of equipment slots in bandolier instance static const uint32 POTION_BELT_SIZE = 5; + + static const size_t TEXT_LINK_BODY_LENGTH = 50; } namespace limits { diff --git a/common/patches/underfoot_ops.h b/common/patches/underfoot_ops.h index cfb4d4fe9..61f4f1c0f 100644 --- a/common/patches/underfoot_ops.h +++ b/common/patches/underfoot_ops.h @@ -65,6 +65,7 @@ E(OP_ShopPlayerSell) E(OP_SomeItemPacketMaybe) E(OP_SpawnAppearance) E(OP_SpawnDoor) +E(OP_SpecialMesg) E(OP_Stun) E(OP_TargetBuffs) E(OP_Track) diff --git a/common/patches/underfoot_structs.h b/common/patches/underfoot_structs.h index 3a63a8c0f..9180a762b 100644 --- a/common/patches/underfoot_structs.h +++ b/common/patches/underfoot_structs.h @@ -4530,6 +4530,23 @@ struct MercenaryAssign_Struct { /*0012*/ }; +// SoF: "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%1X" "%05X" "%08X" +struct TextLinkBody_Struct +{ + uint8 unknown_1; /* '%1X' - Underfoot field 1 / Server field 1 */ + uint32 item_id; /* '%05X' - Underfoot field 2 / Server field 2 */ + uint32 augment_1; /* '%05X' - Underfoot field 3 / Server field 3 */ + uint32 augment_2; /* '%05X' - Underfoot field 4 / Server field 4 */ + uint32 augment_3; /* '%05X' - Underfoot field 5 / Server field 5 */ + uint32 augment_4; /* '%05X' - Underfoot field 6 / Server field 6 */ + uint32 augment_5; /* '%05X' - Underfoot field 7 / Server field 7 */ + uint8 unknown_2; /* '%1X' - Underfoot field 8 / Server field 10 */ + uint32 unknown_3; /* '%04X' - Underfoot field 9 / Server field 11 */ + uint8 unknown_4; /* '%1X' - Underfoot field 10 / Server field 12 */ + uint32 ornament_icon; /* '%05X' - Underfoot field 11 / Server field 13 */ + int hash; /* '%08X' - Underfoot field 12 / Server field 14 */ +}; + }; //end namespace structs }; //end namespace Underfoot diff --git a/zone/bot.cpp b/zone/bot.cpp index d41b6db11..bd7c9f946 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -11707,7 +11707,6 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { std::string item_link; Client::TextLink linker; linker.SetLinkType(linker.linkItemInst); - linker.SetClientVersion(c->GetClientVersion()); for(int i = EmuConstants::EQUIPMENT_BEGIN; i <= EmuConstants::EQUIPMENT_END; ++i) { if((i == MainSecondary) && is2Hweapon) { diff --git a/zone/client.cpp b/zone/client.cpp index 0ad34d752..54324932d 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -8271,71 +8271,27 @@ std::string Client::TextLink::GenerateLink() generate_body(); generate_text(); - if (m_LinkBody.length() && m_LinkText.length()) { - m_Link.append(StringFormat("%c", 0x12)); + if ((m_LinkBody.length() == EmuConstants::TEXT_LINK_BODY_LENGTH) && (m_LinkText.length() > 0)) { + m_Link.push_back(0x12); m_Link.append(m_LinkBody); m_Link.append(m_LinkText); - m_Link.append(StringFormat("%c", 0x12)); + m_Link.push_back(0x12); } if ((m_Link.length() == 0) || (m_Link.length() > 250)) { m_Error = true; m_Link = ""; - _log(CHANNELS__ERROR, "TextLink::GenerateLink() failed to generate a useable text link (LinkType: %i, Lengths: {l: %u, b: %u, t: %u})", + _log(CHANNELS__ERROR, "TextLink::GenerateLink() failed to generate a useable text link (LinkType: %i, Lengths: {link: %u, body: %u, text: %u})", m_LinkType, m_Link.length(), m_LinkBody.length(), m_LinkText.length()); +#if EQDEBUG >= 5 + _log(CHANNELS__ERROR, ">> LinkBody: %s", m_LinkBody.c_str()); + _log(CHANNELS__ERROR, ">> LinkText: %s", m_LinkText.c_str()); +#endif } return m_Link; } -const char* Client::TextLink::GetLink() -{ - if (m_Link.length() == 0) - return nullptr; - - return m_Link.c_str(); -} - -const char* Client::TextLink::GetLinkBody() -{ - if (m_LinkBody.length() == 0) - return nullptr; - - return m_LinkBody.c_str(); -} - -const char* Client::TextLink::GetLinkText() -{ - if (m_LinkText.length() == 0) - return nullptr; - - return m_LinkText.c_str(); -} - -std::string Client::TextLink::GetLinkString() -{ - if (m_Link.length() == 0) - return ""; - - return m_Link; -} - -std::string Client::TextLink::GetLinkBodyString() -{ - if (m_LinkBody.length() == 0) - return ""; - - return m_LinkBody; -} - -std::string Client::TextLink::GetLinkTextString() -{ - if (m_LinkText.length() == 0) - return ""; - - return m_LinkText; -} - void Client::TextLink::Reset() { m_LinkType = linkBlank; @@ -8348,81 +8304,72 @@ void Client::TextLink::Reset() m_Link.clear(); m_LinkBody.clear(); m_LinkText.clear(); - m_ClientVersion = EQClientUnknown; m_Error = false; } void Client::TextLink::generate_body() { - enum { field_0 = 0, field_1, field_2, field_3, field_4, field_5, field_6, field_7, field_8, field_9, field_10, field_11, field_12, field_13 }; - static const int field_count = 14; - static const bool field_use[_EQClientCount][field_count] = { - // 6.2: MakeAnyLenString(&ret_link, "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%1X" "%08X" - // SoF: MakeAnyLenString(&ret_link, "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%1X" "%05X" "%08X" - // RoF: MakeAnyLenString(&ret_link, "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%1X" "%05X" "%08X" - // RoF2: MakeAnyLenString(&ret_link, "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%1X" "%04X" "%1X" "%05X" "%08X" + /* + Current server mask: EQClientRoF2 + + RoF2: "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%1X" "%04X" "%1X" "%05X" "%08X" (56) + RoF: "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%1X" "%05X" "%08X" (55) + SoF: "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%1X" "%05X" "%08X" (50) + 6.2: "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%1X" "%08X" (45) + */ -//(RoF2) %01x %05x %05x %05x %05x %05x %05x %05x %01x %01x %04x %01x %05x %08x - { true, true, true, true, true, true, true, true, true, true, true, true, true, true }, // EQClientUnknown - { true, true, true, true, true, true, true, false, false, true, true, true, false, true }, // EQClient6.2 - { true, true, true, true, true, true, true, false, false, true, true, true, false, true }, // EQClientTitanium - { true, true, true, true, true, true, true, false, false, true, true, true, true, true }, // EQClientSoF - { true, true, true, true, true, true, true, false, false, true, true, true, true, true }, // EQClientSoD - { true, true, true, true, true, true, true, false, false, true, true, true, true, true }, // EQClientUnderfoot - { true, true, true, true, true, true, true, true, false, true, true, true, true, true }, // EQClientRoF - { true, true, true, true, true, true, true, true, true, true, true, true, true, true } // EQClientRoF2 - }; - -/*%01X*/ uint8 unknown_0 = NOT_USED; -/*%05X*/ uint32 item_id = NOT_USED; -/*%05X*/ uint32 augment_0 = NOT_USED; -/*%05X*/ uint32 augment_1 = NOT_USED; -/*%05X*/ uint32 augment_2 = NOT_USED; -/*%05X*/ uint32 augment_3 = NOT_USED; -/*%05X*/ uint32 augment_4 = NOT_USED; -/*%05X*/ uint32 augment_5 = NOT_USED; -/*%01X*/ uint8 unknown_8 = NOT_USED; -/*%01X*/ uint8 unknown_9 = NOT_USED; -/*%04X*/ uint32 unknown_10 = NOT_USED; -/*%01X*/ uint8 unknown_11 = NOT_USED; -/*%05X*/ uint32 unknown_12 = NOT_USED; -/*%08X*/ int hash = NOT_USED; + // could use a ##_cast(&this) with a memset to '0' since these properties are inherited + + unknown_1 = NOT_USED; /* field 1 */ + item_id = NOT_USED; /* field 2 */ + augment_1 = NOT_USED; /* field 3 */ + augment_2 = NOT_USED; /* field 4 */ + augment_3 = NOT_USED; /* field 5 */ + augment_4 = NOT_USED; /* field 6 */ + augment_5 = NOT_USED; /* field 7 */ + augment_6 = NOT_USED; /* field 8 */ + unknown_2 = NOT_USED; /* field 9 */ + unknown_3 = NOT_USED; /* field 10 */ + unknown_4 = NOT_USED; /* field 11 */ + unknown_5 = NOT_USED; /* field 12 */ + ornament_icon = NOT_USED; /* field 13 */ + hash = NOT_USED; /* field 14 */ + const Item_Struct* item_data = nullptr; + switch (m_LinkType) { case linkBlank: break; case linkItemData: - if (m_ItemData != nullptr) { - item_id = m_ItemData->ID; - // TODO: add hash call - } + if (m_ItemData == nullptr) { break; } + item_id = m_ItemData->ID; + // TODO: add hash call break; case linkLootItem: - if (m_LootData != nullptr) { - const Item_Struct* item_data = database.GetItem(m_LootData->item_id); - if (item_data == nullptr) { break; } - item_id = item_data->ID; - augment_0 = m_LootData->aug_1; - augment_1 = m_LootData->aug_2; - augment_2 = m_LootData->aug_3; - augment_3 = m_LootData->aug_4; - augment_4 = m_LootData->aug_5; - augment_5 = m_LootData->aug_6; - // TODO: add hash call - } + if (m_LootData == nullptr) { break; } + item_data = database.GetItem(m_LootData->item_id); + if (item_data == nullptr) { break; } + item_id = item_data->ID; + augment_1 = m_LootData->aug_1; + augment_2 = m_LootData->aug_2; + augment_3 = m_LootData->aug_3; + augment_4 = m_LootData->aug_4; + augment_5 = m_LootData->aug_5; + augment_6 = m_LootData->aug_6; + // TODO: add hash call break; case linkItemInst: - if (m_ItemInst != nullptr) { - if (m_ItemInst->GetItem() == nullptr) { break; } - item_id = m_ItemInst->GetItem()->ID; - augment_0 = m_ItemInst->GetAugmentItemID(0); - augment_1 = m_ItemInst->GetAugmentItemID(1); - augment_2 = m_ItemInst->GetAugmentItemID(2); - augment_3 = m_ItemInst->GetAugmentItemID(3); - augment_4 = m_ItemInst->GetAugmentItemID(4); - augment_5 = m_ItemInst->GetAugmentItemID(5); - // TODO: add hash call - } + if (m_ItemInst == nullptr) { break; } + if (m_ItemInst->GetItem() == nullptr) { break; } + item_id = m_ItemInst->GetItem()->ID; + augment_1 = m_ItemInst->GetAugmentItemID(0); + augment_2 = m_ItemInst->GetAugmentItemID(1); + augment_3 = m_ItemInst->GetAugmentItemID(2); + augment_4 = m_ItemInst->GetAugmentItemID(3); + augment_5 = m_ItemInst->GetAugmentItemID(4); + augment_6 = m_ItemInst->GetAugmentItemID(5); + ornament_icon = m_ItemInst->GetOrnamentationIcon(); + // TODO: add hash call break; default: break; @@ -8433,23 +8380,27 @@ void Client::TextLink::generate_body() } if (m_TaskUse) { - hash = 0x0000000014505DC2; + //hash = 0x0000000014505DC2; + hash = 0x14505DC2; } - if (field_use[m_ClientVersion][field_0]) { m_LinkBody.append(StringFormat("%01x", unknown_0)); } - if (field_use[m_ClientVersion][field_1]) { m_LinkBody.append(StringFormat("%05x", item_id)); } - if (field_use[m_ClientVersion][field_2]) { m_LinkBody.append(StringFormat("%05x", augment_0)); } - if (field_use[m_ClientVersion][field_3]) { m_LinkBody.append(StringFormat("%05x", augment_1)); } - if (field_use[m_ClientVersion][field_4]) { m_LinkBody.append(StringFormat("%05x", augment_2)); } - if (field_use[m_ClientVersion][field_5]) { m_LinkBody.append(StringFormat("%05x", augment_3)); } - if (field_use[m_ClientVersion][field_6]) { m_LinkBody.append(StringFormat("%05x", augment_4)); } - if (field_use[m_ClientVersion][field_7]) { m_LinkBody.append(StringFormat("%05x", augment_5)); } - if (field_use[m_ClientVersion][field_8]) { m_LinkBody.append(StringFormat("%01x", unknown_8)); } - if (field_use[m_ClientVersion][field_9]) { m_LinkBody.append(StringFormat("%01x", unknown_9)); } - if (field_use[m_ClientVersion][field_10]) { m_LinkBody.append(StringFormat("%04x", unknown_10)); } - if (field_use[m_ClientVersion][field_11]) { m_LinkBody.append(StringFormat("%01x", unknown_11)); } - if (field_use[m_ClientVersion][field_12]) { m_LinkBody.append(StringFormat("%05x", unknown_12)); } - if (field_use[m_ClientVersion][field_13]) { m_LinkBody.append(StringFormat("%08x", hash)); } + m_LinkBody = StringFormat( + "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%1X" "%04X" "%1X" "%05X" "%08X", + unknown_1, + item_id, + augment_1, + augment_2, + augment_3, + augment_4, + augment_5, + augment_6, + unknown_2, + unknown_3, + unknown_4, + unknown_5, + ornament_icon, + hash + ); } void Client::TextLink::generate_text() @@ -8459,35 +8410,76 @@ void Client::TextLink::generate_text() return; } + const Item_Struct* item_data = nullptr; + switch (m_LinkType) { case linkBlank: break; case linkItemData: - if (m_ItemData != nullptr) { - m_LinkText = m_ItemData->Name; - return; - } - break; + if (m_ItemData == nullptr) { break; } + m_LinkText = m_ItemData->Name; + return; case linkLootItem: - if (m_LootData != nullptr) { - const Item_Struct* item_data = database.GetItem(m_LootData->item_id); - if (item_data != nullptr) { - m_LinkText = item_data->Name; - return; - } - } - break; + if (m_LootData == nullptr) { break; } + item_data = database.GetItem(m_LootData->item_id); + if (item_data == nullptr) { break; } + m_LinkText = item_data->Name; + return; case linkItemInst: - if (m_ItemInst != nullptr) { - if (m_ItemInst->GetItem() != nullptr) { - m_LinkText = m_ItemInst->GetItem()->Name; - return; - } - } - break; + if (m_ItemInst == nullptr) { break; } + if (m_ItemInst->GetItem() == nullptr) { break; } + m_LinkText = m_ItemInst->GetItem()->Name; + return; default: break; } m_LinkText = "null"; } + +bool Client::TextLink::DegenerateLinkBody(TextLinkBody_Struct& textLinkBodyStruct, const std::string& textLinkBody) +{ + memset(&textLinkBodyStruct, 0, sizeof(TextLinkBody_Struct)); + if (textLinkBody.length() != EmuConstants::TEXT_LINK_BODY_LENGTH) { return false; } + + textLinkBodyStruct.unknown_1 = (uint8)strtol(textLinkBody.substr(0, 1).c_str(), nullptr, 16); + textLinkBodyStruct.item_id = (uint32)strtol(textLinkBody.substr(1, 5).c_str(), nullptr, 16); + textLinkBodyStruct.augment_1 = (uint32)strtol(textLinkBody.substr(6, 5).c_str(), nullptr, 16); + textLinkBodyStruct.augment_2 = (uint32)strtol(textLinkBody.substr(11, 5).c_str(), nullptr, 16); + textLinkBodyStruct.augment_3 = (uint32)strtol(textLinkBody.substr(16, 5).c_str(), nullptr, 16); + textLinkBodyStruct.augment_4 = (uint32)strtol(textLinkBody.substr(21, 5).c_str(), nullptr, 16); + textLinkBodyStruct.augment_5 = (uint32)strtol(textLinkBody.substr(26, 5).c_str(), nullptr, 16); + textLinkBodyStruct.augment_6 = (uint32)strtol(textLinkBody.substr(31, 5).c_str(), nullptr, 16); + textLinkBodyStruct.unknown_2 = (uint8)strtol(textLinkBody.substr(36, 1).c_str(), nullptr, 16); + textLinkBodyStruct.unknown_3 = (uint8)strtol(textLinkBody.substr(37, 1).c_str(), nullptr, 16); + textLinkBodyStruct.unknown_4 = (uint32)strtol(textLinkBody.substr(38, 4).c_str(), nullptr, 16); + textLinkBodyStruct.unknown_5 = (uint8)strtol(textLinkBody.substr(42, 1).c_str(), nullptr, 16); + textLinkBodyStruct.ornament_icon = (uint32)strtol(textLinkBody.substr(43, 5).c_str(), nullptr, 16); + textLinkBodyStruct.hash = (int)strtol(textLinkBody.substr(48, 8).c_str(), nullptr, 16); + + return true; +} + +bool Client::TextLink::GenerateLinkBody(std::string& textLinkBody, const TextLinkBody_Struct& textLinkBodyStruct) +{ + textLinkBody = StringFormat( + "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%1X" "%04X" "%1X" "%05X" "%08X", + textLinkBodyStruct.unknown_1, + textLinkBodyStruct.item_id, + textLinkBodyStruct.augment_1, + textLinkBodyStruct.augment_2, + textLinkBodyStruct.augment_3, + textLinkBodyStruct.augment_4, + textLinkBodyStruct.augment_5, + textLinkBodyStruct.augment_6, + textLinkBodyStruct.unknown_2, + textLinkBodyStruct.unknown_3, + textLinkBodyStruct.unknown_4, + textLinkBodyStruct.unknown_5, + textLinkBodyStruct.ornament_icon, + textLinkBodyStruct.hash + ); + + if (textLinkBody.length() != EmuConstants::TEXT_LINK_BODY_LENGTH) { return false; } + return true; +} diff --git a/zone/client.h b/zone/client.h index b7bb8f6a5..d6ef83523 100644 --- a/zone/client.h +++ b/zone/client.h @@ -819,7 +819,7 @@ public: // // class Client::TextLink // - class TextLink { + class TextLink : TextLinkBody_Struct { public: enum LinkType { linkBlank = 0, linkItemData, linkLootItem, linkItemInst }; @@ -832,20 +832,19 @@ public: void SetProxyItemID(uint32 proxyItemID) { m_ProxyItemID = proxyItemID; } // mainly for saylinks..but, not limited to void SetProxyText(const char* proxyText) { m_ProxyText = proxyText; } // overrides standard text use void SetTaskUse() { m_TaskUse = true; } - void SetClientVersion(EQClientVersion clientVersion) { m_ClientVersion = EQLimits::ValidateClientVersion(clientVersion); } std::string GenerateLink(); bool LinkError() { return m_Error; } - const char* GetLink(); // contains full format: '/12x' '' '' '/12x' - const char* GetLinkBody(); // contains format: '' - const char* GetLinkText(); // contains format: '' - std::string GetLinkString(); - std::string GetLinkBodyString(); - std::string GetLinkTextString(); + std::string GetLink() { return m_Link; } // contains full string format: '/12x' '' '' '/12x' + std::string GetLinkBody() { return m_LinkBody; } // contains string format: '' + std::string GetLinkText() { return m_LinkText; } // contains string format: '' void Reset(); + static bool DegenerateLinkBody(TextLinkBody_Struct& textLinkBodyStruct, const std::string& textLinkBody); + static bool GenerateLinkBody(std::string& textLinkBody, const TextLinkBody_Struct& textLinkBodyStruct); + private: void generate_body(); void generate_text(); @@ -860,7 +859,6 @@ public: std::string m_Link; std::string m_LinkBody; std::string m_LinkText; - EQClientVersion m_ClientVersion; bool m_Error; }; diff --git a/zone/command.cpp b/zone/command.cpp index d31ea97b7..b33a3ee6d 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -2621,7 +2621,6 @@ void command_peekinv(Client *c, const Seperator *sep) std::string item_link; Client::TextLink linker; linker.SetLinkType(linker.linkItemInst); - linker.SetClientVersion(c->GetClientVersion()); c->Message(0, "Displaying inventory for %s...", targetClient->GetName()); @@ -5574,7 +5573,6 @@ void command_itemsearch(Client *c, const Seperator *sep) std::string item_link; Client::TextLink linker; linker.SetLinkType(linker.linkItemData); - linker.SetClientVersion(c->GetClientVersion()); if (Seperator::IsNumber(search_criteria)) { item = database.GetItem(atoi(search_criteria)); diff --git a/zone/corpse.cpp b/zone/corpse.cpp index 380697eac..d5db03856 100644 --- a/zone/corpse.cpp +++ b/zone/corpse.cpp @@ -1258,23 +1258,16 @@ void Corpse::LootItem(Client* client, const EQApplicationPacket* app) { SetPlayerKillItemID(0); } - /* Send message with item link to groups and such */ - Client::TextLink linker; - linker.SetLinkType(linker.linkItemInst); - linker.SetItemInst(inst); - linker.SetClientVersion(client->GetClientVersion()); + /* Send message with item link to groups and such */ + Client::TextLink linker; + linker.SetLinkType(linker.linkItemInst); + linker.SetItemInst(inst); auto item_link = linker.GenerateLink(); client->Message_StringID(MT_LootMessages, LOOTED_MESSAGE, item_link.c_str()); - if(!IsPlayerCorpse()) { - // When sending to multiple/unknown client types, we set for the highest client.. - // ..which is processed when 'EQClientUnknown,' or default value, is selected. - // This should help with any current issues..or it may create more! O.o - linker.SetClientVersion(EQClientUnknown); - item_link = linker.GenerateLink(); - + if (!IsPlayerCorpse()) { Group *g = client->GetGroup(); if(g != nullptr) { g->GroupMessage_StringID(client, MT_LootMessages, OTHER_LOOTED_MESSAGE, client->GetName(), item_link.c_str()); diff --git a/zone/npc.cpp b/zone/npc.cpp index a478e3b51..d6cf9ed9f 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -515,7 +515,6 @@ void NPC::QueryLoot(Client* to) Client::TextLink linker; linker.SetLinkType(linker.linkItemData); linker.SetItemData(item); - linker.SetClientVersion(to->GetClientVersion()); auto item_link = linker.GenerateLink(); diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 3fb714d7e..53eed6787 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -1234,7 +1234,6 @@ void QuestManager::itemlink(int item_id) { Client::TextLink linker; linker.SetLinkType(linker.linkItemData); linker.SetItemData(item); - linker.SetClientVersion(initiator->GetClientVersion()); auto item_link = linker.GenerateLink(); @@ -2474,8 +2473,6 @@ const char* QuestManager::varlink(char* perltext, int item_id) { Client::TextLink linker; linker.SetLinkType(linker.linkItemData); linker.SetItemData(item); - if (initiator) - linker.SetClientVersion(initiator->GetClientVersion()); auto item_link = linker.GenerateLink(); strcpy(perltext, item_link.c_str()); // link length is currently ranged from 1 to 250 in TextLink::GenerateLink() @@ -2668,8 +2665,6 @@ const char* QuestManager::saylink(char* Phrase, bool silent, const char* LinkNam Client::TextLink linker; linker.SetProxyItemID(sayid); linker.SetProxyText(LinkName); - if (initiator) - linker.SetClientVersion(initiator->GetClientVersion()); auto say_link = linker.GenerateLink(); strcpy(Phrase, say_link.c_str()); // link length is currently ranged from 1 to 250 in TextLink::GenerateLink() diff --git a/zone/tasks.cpp b/zone/tasks.cpp index 79efb1115..1f6e381fd 100644 --- a/zone/tasks.cpp +++ b/zone/tasks.cpp @@ -2779,7 +2779,6 @@ void TaskManager::SendActiveTaskDescription(Client *c, int TaskID, int SequenceN Client::TextLink linker; linker.SetLinkType(linker.linkItemData); linker.SetItemData(reward_item); - linker.SetClientVersion(c->GetClientVersion()); linker.SetTaskUse(); if (strlen(Tasks[TaskID]->Reward) != 0) linker.SetProxyText(Tasks[TaskID]->Reward);