mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-11 21:01:29 +00:00
2023 lines
53 KiB
C++
2023 lines
53 KiB
C++
#include "../global_define.h"
|
|
#include "../eqemu_logsys.h"
|
|
#include "titanium.h"
|
|
#include "../opcodemgr.h"
|
|
|
|
#include "../eq_stream_ident.h"
|
|
#include "../crc32.h"
|
|
#include "../races.h"
|
|
|
|
#include "../eq_packet_structs.h"
|
|
#include "../misc_functions.h"
|
|
#include "../string_util.h"
|
|
#include "../item.h"
|
|
#include "titanium_structs.h"
|
|
#include <sstream>
|
|
|
|
namespace Titanium
|
|
{
|
|
static const char *name = "Titanium";
|
|
static OpcodeManager *opcodes = nullptr;
|
|
static Strategy struct_strategy;
|
|
|
|
char* SerializeItem(const ItemInst *inst, int16 slot_id_in, uint32 *length, uint8 depth);
|
|
|
|
// server to client inventory location converters
|
|
static inline int16 ServerToTitaniumSlot(uint32 ServerSlot);
|
|
static inline int16 ServerToTitaniumCorpseSlot(uint32 ServerCorpse);
|
|
|
|
// client to server inventory location converters
|
|
static inline uint32 TitaniumToServerSlot(int16 TitaniumSlot);
|
|
static inline uint32 TitaniumToServerCorpseSlot(int16 TitaniumCorpse);
|
|
|
|
// server to client text link converter
|
|
static inline void ServerToTitaniumTextLink(std::string& titaniumTextLink, const std::string& serverTextLink);
|
|
|
|
// client to server text link converter
|
|
static inline void TitaniumToServerTextLink(std::string& serverTextLink, const std::string& titaniumTextLink);
|
|
|
|
void Register(EQStreamIdentifier &into)
|
|
{
|
|
//create our opcode manager if we havent already
|
|
if (opcodes == nullptr) {
|
|
//TODO: get this file name from the config file
|
|
std::string opfile = "patch_";
|
|
opfile += name;
|
|
opfile += ".conf";
|
|
//load up the opcode manager.
|
|
//TODO: figure out how to support shared memory with multiple patches...
|
|
opcodes = new RegularOpcodeManager();
|
|
if (!opcodes->LoadOpcodes(opfile.c_str())) {
|
|
Log.Out(Logs::General, Logs::Netcode, "[OPCODES] Error loading opcodes file %s. Not registering patch %s.", opfile.c_str(), name);
|
|
return;
|
|
}
|
|
}
|
|
|
|
//ok, now we have what we need to register.
|
|
|
|
EQStream::Signature signature;
|
|
std::string pname;
|
|
|
|
//register our world signature.
|
|
pname = std::string(name) + "_world";
|
|
signature.ignore_eq_opcode = 0;
|
|
signature.first_length = sizeof(structs::LoginInfo_Struct);
|
|
signature.first_eq_opcode = opcodes->EmuToEQ(OP_SendLoginInfo);
|
|
into.RegisterPatch(signature, pname.c_str(), &opcodes, &struct_strategy);
|
|
|
|
//register our zone signature.
|
|
pname = std::string(name) + "_zone";
|
|
signature.ignore_eq_opcode = opcodes->EmuToEQ(OP_AckPacket);
|
|
signature.first_length = sizeof(structs::ClientZoneEntry_Struct);
|
|
signature.first_eq_opcode = opcodes->EmuToEQ(OP_ZoneEntry);
|
|
into.RegisterPatch(signature, pname.c_str(), &opcodes, &struct_strategy);
|
|
|
|
|
|
|
|
Log.Out(Logs::General, Logs::Netcode, "[IDENTIFY] Registered patch %s", name);
|
|
}
|
|
|
|
void Reload()
|
|
{
|
|
//we have a big problem to solve here when we switch back to shared memory
|
|
//opcode managers because we need to change the manager pointer, which means
|
|
//we need to go to every stream and replace it's manager.
|
|
|
|
if (opcodes != nullptr) {
|
|
//TODO: get this file name from the config file
|
|
std::string opfile = "patch_";
|
|
opfile += name;
|
|
opfile += ".conf";
|
|
if (!opcodes->ReloadOpcodes(opfile.c_str())) {
|
|
Log.Out(Logs::General, Logs::Netcode, "[OPCODES] Error reloading opcodes file %s for patch %s.", opfile.c_str(), name);
|
|
return;
|
|
}
|
|
Log.Out(Logs::General, Logs::Netcode, "[OPCODES] Reloaded opcodes for patch %s", name);
|
|
}
|
|
}
|
|
|
|
Strategy::Strategy() : StructStrategy()
|
|
{
|
|
//all opcodes default to passthrough.
|
|
#include "ss_register.h"
|
|
#include "titanium_ops.h"
|
|
}
|
|
|
|
std::string Strategy::Describe() const
|
|
{
|
|
std::string r;
|
|
r += "Patch ";
|
|
r += name;
|
|
return(r);
|
|
}
|
|
|
|
const EQClientVersion Strategy::ClientVersion() const
|
|
{
|
|
return EQClientTitanium;
|
|
}
|
|
|
|
#include "ss_define.h"
|
|
|
|
// ENCODE methods
|
|
EAT_ENCODE(OP_GuildMemberLevelUpdate); // added ;
|
|
|
|
EAT_ENCODE(OP_ZoneServerReady); // added ;
|
|
|
|
ENCODE(OP_Action)
|
|
{
|
|
ENCODE_LENGTH_EXACT(Action_Struct);
|
|
SETUP_DIRECT_ENCODE(Action_Struct, structs::Action_Struct);
|
|
|
|
OUT(target);
|
|
OUT(source);
|
|
OUT(level);
|
|
OUT(instrument_mod);
|
|
OUT(sequence);
|
|
OUT(type);
|
|
//OUT(damage);
|
|
OUT(spell);
|
|
OUT(buff_unknown); // if this is 4, a buff icon is made
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_AdventureMerchantSell)
|
|
{
|
|
ENCODE_LENGTH_EXACT(Adventure_Sell_Struct);
|
|
SETUP_DIRECT_ENCODE(Adventure_Sell_Struct, structs::Adventure_Sell_Struct);
|
|
|
|
eq->unknown000 = 1;
|
|
OUT(npcid);
|
|
eq->slot = ServerToTitaniumSlot(emu->slot);
|
|
OUT(charges);
|
|
OUT(sell_price);
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_ApplyPoison)
|
|
{
|
|
ENCODE_LENGTH_EXACT(ApplyPoison_Struct);
|
|
SETUP_DIRECT_ENCODE(ApplyPoison_Struct, structs::ApplyPoison_Struct);
|
|
|
|
eq->inventorySlot = ServerToTitaniumSlot(emu->inventorySlot);
|
|
OUT(success);
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_BazaarSearch)
|
|
{
|
|
if (((*p)->size == sizeof(BazaarReturnDone_Struct)) || ((*p)->size == sizeof(BazaarWelcome_Struct))) {
|
|
|
|
EQApplicationPacket *in = *p;
|
|
*p = nullptr;
|
|
dest->FastQueuePacket(&in, ack_req);
|
|
return;
|
|
}
|
|
|
|
//consume the packet
|
|
EQApplicationPacket *in = *p;
|
|
*p = nullptr;
|
|
|
|
//store away the emu struct
|
|
unsigned char *__emu_buffer = in->pBuffer;
|
|
BazaarSearchResults_Struct *emu = (BazaarSearchResults_Struct *)__emu_buffer;
|
|
|
|
//determine and verify length
|
|
int entrycount = in->size / sizeof(BazaarSearchResults_Struct);
|
|
if (entrycount == 0 || (in->size % sizeof(BazaarSearchResults_Struct)) != 0) {
|
|
Log.Out(Logs::General, Logs::Netcode, "[STRUCTS] Wrong size on outbound %s: Got %d, expected multiple of %d",
|
|
opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(BazaarSearchResults_Struct));
|
|
delete in;
|
|
return;
|
|
}
|
|
|
|
//make the EQ struct.
|
|
in->size = sizeof(structs::BazaarSearchResults_Struct)*entrycount;
|
|
in->pBuffer = new unsigned char[in->size];
|
|
structs::BazaarSearchResults_Struct *eq = (structs::BazaarSearchResults_Struct *) in->pBuffer;
|
|
|
|
//zero out the packet. We could avoid this memset by setting all fields (including unknowns) in the loop.
|
|
memset(in->pBuffer, 0, in->size);
|
|
|
|
for (int i = 0; i < entrycount; i++, eq++, emu++) {
|
|
eq->Beginning.Action = emu->Beginning.Action;
|
|
eq->Beginning.Unknown001 = emu->Beginning.Unknown001;
|
|
eq->Beginning.Unknown002 = emu->Beginning.Unknown002;
|
|
eq->NumItems = emu->NumItems;
|
|
eq->SerialNumber = emu->SerialNumber;
|
|
eq->SellerID = emu->SellerID;
|
|
eq->Cost = emu->Cost;
|
|
eq->ItemStat = emu->ItemStat;
|
|
strcpy(eq->ItemName, emu->ItemName);
|
|
}
|
|
|
|
delete[] __emu_buffer;
|
|
dest->FastQueuePacket(&in, ack_req);
|
|
}
|
|
|
|
ENCODE(OP_BecomeTrader)
|
|
{
|
|
ENCODE_LENGTH_EXACT(BecomeTrader_Struct);
|
|
SETUP_DIRECT_ENCODE(BecomeTrader_Struct, structs::BecomeTrader_Struct);
|
|
|
|
OUT(ID);
|
|
OUT(Code);
|
|
|
|
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;
|
|
ServerToTitaniumTextLink(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
|
|
EQApplicationPacket *in = *p;
|
|
*p = nullptr;
|
|
|
|
//store away the emu struct
|
|
unsigned char *__emu_buffer = in->pBuffer;
|
|
|
|
int itemcount = in->size / sizeof(InternalSerializedItem_Struct);
|
|
if (itemcount == 0 || (in->size % sizeof(InternalSerializedItem_Struct)) != 0) {
|
|
Log.Out(Logs::General, Logs::Netcode, "[STRUCTS] Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(InternalSerializedItem_Struct));
|
|
delete in;
|
|
return;
|
|
}
|
|
InternalSerializedItem_Struct *eq = (InternalSerializedItem_Struct *)in->pBuffer;
|
|
|
|
//do the transform...
|
|
int r;
|
|
std::string serial_string;
|
|
for (r = 0; r < itemcount; r++, eq++) {
|
|
uint32 length;
|
|
char *serialized = SerializeItem((const ItemInst*)eq->inst, eq->slot_id, &length, 0);
|
|
if (serialized) {
|
|
serial_string.append(serialized, length + 1);
|
|
safe_delete_array(serialized);
|
|
}
|
|
else {
|
|
Log.Out(Logs::General, Logs::Netcode, "[STRUCTS] Serialization failed on item slot %d during OP_CharInventory. Item skipped.", eq->slot_id);
|
|
}
|
|
|
|
}
|
|
|
|
in->size = serial_string.length();
|
|
in->pBuffer = new unsigned char[in->size];
|
|
memcpy(in->pBuffer, serial_string.c_str(), serial_string.length());
|
|
|
|
delete[] __emu_buffer;
|
|
|
|
dest->FastQueuePacket(&in, ack_req);
|
|
}
|
|
|
|
ENCODE(OP_DeleteCharge) { ENCODE_FORWARD(OP_MoveItem); }
|
|
|
|
ENCODE(OP_DeleteItem)
|
|
{
|
|
ENCODE_LENGTH_EXACT(DeleteItem_Struct);
|
|
SETUP_DIRECT_ENCODE(DeleteItem_Struct, structs::DeleteItem_Struct);
|
|
|
|
eq->from_slot = ServerToTitaniumSlot(emu->from_slot);
|
|
eq->to_slot = ServerToTitaniumSlot(emu->to_slot);
|
|
OUT(number_in_stack);
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_DeleteSpawn)
|
|
{
|
|
SETUP_DIRECT_ENCODE(DeleteSpawn_Struct, structs::DeleteSpawn_Struct);
|
|
|
|
OUT(spawn_id);
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_DzCompass)
|
|
{
|
|
SETUP_VAR_ENCODE(ExpeditionCompass_Struct);
|
|
ALLOC_VAR_ENCODE(structs::ExpeditionCompass_Struct, sizeof(structs::ExpeditionInfo_Struct) + sizeof(structs::ExpeditionCompassEntry_Struct) * emu->count);
|
|
|
|
OUT(count);
|
|
|
|
for (uint32 i = 0; i < emu->count; ++i)
|
|
{
|
|
OUT(entries[i].x);
|
|
OUT(entries[i].y);
|
|
OUT(entries[i].z);
|
|
}
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_DzExpeditionEndsWarning)
|
|
{
|
|
ENCODE_LENGTH_EXACT(ExpeditionExpireWarning);
|
|
SETUP_DIRECT_ENCODE(ExpeditionExpireWarning, structs::ExpeditionExpireWarning);
|
|
|
|
OUT(minutes_remaining);
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_DzExpeditionInfo)
|
|
{
|
|
ENCODE_LENGTH_EXACT(ExpeditionInfo_Struct);
|
|
SETUP_DIRECT_ENCODE(ExpeditionInfo_Struct, structs::ExpeditionInfo_Struct);
|
|
|
|
OUT(max_players);
|
|
eq->enabled_max = 1;
|
|
strcpy(eq->expedition_name, emu->expedition_name);
|
|
strcpy(eq->leader_name, emu->leader_name);
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_DzExpeditionList)
|
|
{
|
|
SETUP_VAR_ENCODE(ExpeditionLockoutList_Struct);
|
|
|
|
std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary);
|
|
uint32 client_id = 0;
|
|
uint8 null_term = 0;
|
|
ss.write((const char*)&client_id, sizeof(uint32));
|
|
ss.write((const char*)&emu->count, sizeof(uint32));
|
|
for (uint32 i = 0; i < emu->count; ++i)
|
|
{
|
|
ss.write(emu->entries[i].expedition, strlen(emu->entries[i].expedition));
|
|
ss.write((const char*)&null_term, sizeof(char));
|
|
ss.write((const char*)&emu->entries[i].time_left, sizeof(uint32));
|
|
ss.write((const char*)&client_id, sizeof(uint32));
|
|
ss.write(emu->entries[i].expedition_event, strlen(emu->entries[i].expedition_event));
|
|
ss.write((const char*)&null_term, sizeof(char));
|
|
}
|
|
|
|
__packet->size = ss.str().length();
|
|
__packet->pBuffer = new unsigned char[__packet->size];
|
|
memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size);
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_DzJoinExpeditionConfirm)
|
|
{
|
|
ENCODE_LENGTH_EXACT(ExpeditionJoinPrompt_Struct);
|
|
SETUP_DIRECT_ENCODE(ExpeditionJoinPrompt_Struct, structs::ExpeditionJoinPrompt_Struct);
|
|
|
|
strcpy(eq->expedition_name, emu->expedition_name);
|
|
strcpy(eq->player_name, emu->player_name);
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_DzLeaderStatus)
|
|
{
|
|
SETUP_VAR_ENCODE(ExpeditionLeaderSet_Struct);
|
|
|
|
std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary);
|
|
uint32 client_id = 0;
|
|
uint8 null_term = 0;
|
|
|
|
ss.write((const char*)&client_id, sizeof(uint32));
|
|
//ss.write((const char*)&client_id, sizeof(uint32));
|
|
ss.write(emu->leader_name, strlen(emu->leader_name));
|
|
ss.write((const char*)&null_term, sizeof(char));
|
|
ss.write((const char*)&client_id, sizeof(uint32));
|
|
ss.write((const char*)&client_id, sizeof(uint32));//0xffffffff
|
|
ss.write((const char*)&client_id, sizeof(uint32));
|
|
ss.write((const char*)&client_id, sizeof(uint32));
|
|
ss.write((const char*)&client_id, sizeof(uint32));
|
|
ss.write((const char*)&client_id, sizeof(uint32));
|
|
ss.write((const char*)&client_id, sizeof(uint32));
|
|
ss.write((const char*)&client_id, sizeof(uint32));
|
|
ss.write((const char*)&client_id, sizeof(uint32));
|
|
ss.write((const char*)&client_id, sizeof(uint32));
|
|
ss.write((const char*)&client_id, sizeof(uint32));
|
|
ss.write((const char*)&client_id, sizeof(uint32));//1
|
|
ss.write((const char*)&client_id, sizeof(uint32));
|
|
|
|
__packet->size = ss.str().length();
|
|
__packet->pBuffer = new unsigned char[__packet->size];
|
|
memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size);
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_DzMemberList)
|
|
{
|
|
SETUP_VAR_ENCODE(ExpeditionMemberList_Struct);
|
|
|
|
std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary);
|
|
|
|
uint32 client_id = 0;
|
|
uint8 null_term = 0;
|
|
ss.write((const char*)&client_id, sizeof(uint32));
|
|
ss.write((const char*)&emu->count, sizeof(uint32));
|
|
for (uint32 i = 0; i < emu->count; ++i)
|
|
{
|
|
ss.write(emu->entries[i].name, strlen(emu->entries[i].name));
|
|
ss.write((const char*)&null_term, sizeof(char));
|
|
ss.write((const char*)&emu->entries[i].status, sizeof(char));
|
|
}
|
|
|
|
__packet->size = ss.str().length();
|
|
__packet->pBuffer = new unsigned char[__packet->size];
|
|
memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size);
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_GuildMemberList)
|
|
{
|
|
//consume the packet
|
|
EQApplicationPacket *in = *p;
|
|
*p = nullptr;
|
|
|
|
//store away the emu struct
|
|
unsigned char *__emu_buffer = in->pBuffer;
|
|
Internal_GuildMembers_Struct *emu = (Internal_GuildMembers_Struct *)in->pBuffer;
|
|
|
|
//make a new EQ buffer.
|
|
uint32 pnl = strlen(emu->player_name);
|
|
uint32 length = sizeof(structs::GuildMembers_Struct) + pnl +
|
|
emu->count*sizeof(structs::GuildMemberEntry_Struct)
|
|
+ emu->name_length + emu->note_length;
|
|
in->pBuffer = new uint8[length];
|
|
in->size = length;
|
|
//no memset since we fill every byte.
|
|
|
|
uint8 *buffer;
|
|
buffer = in->pBuffer;
|
|
|
|
//easier way to setup GuildMembers_Struct
|
|
//set prefix name
|
|
strcpy((char *)buffer, emu->player_name);
|
|
buffer += pnl;
|
|
*buffer = '\0';
|
|
buffer++;
|
|
|
|
//add member count.
|
|
*((uint32 *)buffer) = htonl(emu->count);
|
|
buffer += sizeof(uint32);
|
|
|
|
if (emu->count > 0) {
|
|
Internal_GuildMemberEntry_Struct *emu_e = emu->member;
|
|
const char *emu_name = (const char *)(__emu_buffer +
|
|
sizeof(Internal_GuildMembers_Struct)+ //skip header
|
|
emu->count * sizeof(Internal_GuildMemberEntry_Struct) //skip static length member data
|
|
);
|
|
const char *emu_note = (emu_name +
|
|
emu->name_length + //skip name contents
|
|
emu->count //skip string terminators
|
|
);
|
|
|
|
structs::GuildMemberEntry_Struct *e = (structs::GuildMemberEntry_Struct *) buffer;
|
|
|
|
uint32 r;
|
|
for (r = 0; r < emu->count; r++, emu_e++) {
|
|
|
|
//the order we set things here must match the struct
|
|
|
|
//nice helper macro
|
|
/*#define SlideStructString(field, str) \
|
|
strcpy(e->field, str.c_str()); \
|
|
e = (GuildMemberEntry_Struct *) ( ((uint8 *)e) + str.length() )*/
|
|
#define SlideStructString(field, str) \
|
|
{ \
|
|
int sl = strlen(str); \
|
|
memcpy(e->field, str, sl+1); \
|
|
e = (structs::GuildMemberEntry_Struct *) ( ((uint8 *)e) + sl ); \
|
|
str += sl + 1; \
|
|
}
|
|
#define PutFieldN(field) e->field = htonl(emu_e->field)
|
|
|
|
SlideStructString(name, emu_name);
|
|
PutFieldN(level);
|
|
PutFieldN(banker);
|
|
PutFieldN(class_);
|
|
PutFieldN(rank);
|
|
PutFieldN(time_last_on);
|
|
PutFieldN(tribute_enable);
|
|
PutFieldN(total_tribute);
|
|
PutFieldN(last_tribute);
|
|
e->unknown_one = htonl(1);
|
|
SlideStructString(public_note, emu_note);
|
|
e->zoneinstance = 0;
|
|
e->zone_id = htons(emu_e->zone_id);
|
|
|
|
#undef SlideStructString
|
|
#undef PutFieldN
|
|
|
|
e++;
|
|
}
|
|
}
|
|
|
|
delete[] __emu_buffer;
|
|
dest->FastQueuePacket(&in, ack_req);
|
|
}
|
|
|
|
ENCODE(OP_Illusion)
|
|
{
|
|
ENCODE_LENGTH_EXACT(Illusion_Struct);
|
|
SETUP_DIRECT_ENCODE(Illusion_Struct, structs::Illusion_Struct);
|
|
|
|
OUT(spawnid);
|
|
OUT_str(charname);
|
|
|
|
if (emu->race > 473)
|
|
eq->race = 1;
|
|
else
|
|
OUT(race);
|
|
|
|
OUT(gender);
|
|
OUT(texture);
|
|
OUT(helmtexture);
|
|
OUT(face);
|
|
OUT(hairstyle);
|
|
OUT(haircolor);
|
|
OUT(beard);
|
|
OUT(beardcolor);
|
|
OUT(size);
|
|
/*
|
|
//Test code for identifying the structure
|
|
uint8 ofs;
|
|
uint8 val;
|
|
ofs = emu->texture;
|
|
val = emu->face;
|
|
((uint8*)eq)[ofs % 168] = val;
|
|
*/
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_InspectAnswer)
|
|
{
|
|
ENCODE_LENGTH_EXACT(InspectResponse_Struct);
|
|
SETUP_DIRECT_ENCODE(InspectResponse_Struct, structs::InspectResponse_Struct);
|
|
|
|
OUT(TargetID);
|
|
OUT(playerid);
|
|
|
|
int r;
|
|
for (r = 0; r <= 20; r++) {
|
|
strn0cpy(eq->itemnames[r], emu->itemnames[r], sizeof(eq->itemnames[r]));
|
|
}
|
|
|
|
// move arrow item down to last element in titanium array
|
|
strn0cpy(eq->itemnames[21], emu->itemnames[22], sizeof(eq->itemnames[21]));
|
|
|
|
int k;
|
|
for (k = 0; k <= 20; k++) {
|
|
OUT(itemicons[k]);
|
|
}
|
|
|
|
// move arrow icon down to last element in titanium array
|
|
eq->itemicons[21] = emu->itemicons[22];
|
|
strn0cpy(eq->text, emu->text, sizeof(eq->text));
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_InspectRequest)
|
|
{
|
|
ENCODE_LENGTH_EXACT(Inspect_Struct);
|
|
SETUP_DIRECT_ENCODE(Inspect_Struct, structs::Inspect_Struct);
|
|
|
|
OUT(TargetID);
|
|
OUT(PlayerID);
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_ItemLinkResponse) { ENCODE_FORWARD(OP_ItemPacket); }
|
|
|
|
ENCODE(OP_ItemPacket)
|
|
{
|
|
//consume the packet
|
|
EQApplicationPacket *in = *p;
|
|
*p = nullptr;
|
|
|
|
//store away the emu struct
|
|
unsigned char *__emu_buffer = in->pBuffer;
|
|
ItemPacket_Struct *old_item_pkt = (ItemPacket_Struct *)__emu_buffer;
|
|
InternalSerializedItem_Struct *int_struct = (InternalSerializedItem_Struct *)(old_item_pkt->SerializedItem);
|
|
|
|
uint32 length;
|
|
char *serialized = SerializeItem((ItemInst *)int_struct->inst, int_struct->slot_id, &length, 0);
|
|
|
|
if (!serialized) {
|
|
Log.Out(Logs::General, Logs::Netcode, "[STRUCTS] Serialization failed on item slot %d.", int_struct->slot_id);
|
|
delete in;
|
|
return;
|
|
}
|
|
in->size = length + 5; // ItemPacketType + Serialization + \0
|
|
in->pBuffer = new unsigned char[in->size];
|
|
ItemPacket_Struct *new_item_pkt = (ItemPacket_Struct *)in->pBuffer;
|
|
new_item_pkt->PacketType = old_item_pkt->PacketType;
|
|
memcpy(new_item_pkt->SerializedItem, serialized, length + 1);
|
|
|
|
delete[] __emu_buffer;
|
|
safe_delete_array(serialized);
|
|
|
|
dest->FastQueuePacket(&in, ack_req);
|
|
}
|
|
|
|
ENCODE(OP_LeadershipExpUpdate)
|
|
{
|
|
SETUP_DIRECT_ENCODE(LeadershipExpUpdate_Struct, structs::LeadershipExpUpdate_Struct);
|
|
|
|
OUT(group_leadership_exp);
|
|
OUT(group_leadership_points);
|
|
OUT(raid_leadership_exp);
|
|
OUT(raid_leadership_points);
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_LFGuild)
|
|
{
|
|
EQApplicationPacket *in = *p;
|
|
*p = nullptr;
|
|
|
|
uint32 Command = in->ReadUInt32();
|
|
|
|
if (Command != 0)
|
|
{
|
|
dest->FastQueuePacket(&in, ack_req);
|
|
return;
|
|
}
|
|
|
|
EQApplicationPacket *outapp = new EQApplicationPacket(OP_LFGuild, sizeof(structs::LFGuild_PlayerToggle_Struct));
|
|
|
|
memcpy(outapp->pBuffer, in->pBuffer, sizeof(structs::LFGuild_PlayerToggle_Struct));
|
|
|
|
dest->FastQueuePacket(&outapp, ack_req);
|
|
delete in;
|
|
}
|
|
|
|
ENCODE(OP_LootItem)
|
|
{
|
|
ENCODE_LENGTH_EXACT(LootingItem_Struct);
|
|
SETUP_DIRECT_ENCODE(LootingItem_Struct, structs::LootingItem_Struct);
|
|
|
|
OUT(lootee);
|
|
OUT(looter);
|
|
eq->slot_id = ServerToTitaniumCorpseSlot(emu->slot_id);
|
|
OUT(auto_loot);
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_MoveItem)
|
|
{
|
|
ENCODE_LENGTH_EXACT(MoveItem_Struct);
|
|
SETUP_DIRECT_ENCODE(MoveItem_Struct, structs::MoveItem_Struct);
|
|
|
|
eq->from_slot = ServerToTitaniumSlot(emu->from_slot);
|
|
eq->to_slot = ServerToTitaniumSlot(emu->to_slot);
|
|
OUT(number_in_stack);
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_NewSpawn) { ENCODE_FORWARD(OP_ZoneSpawns); }
|
|
|
|
ENCODE(OP_OnLevelMessage)
|
|
{
|
|
ENCODE_LENGTH_EXACT(OnLevelMessage_Struct);
|
|
SETUP_DIRECT_ENCODE(OnLevelMessage_Struct, structs::OnLevelMessage_Struct);
|
|
|
|
OUT_str(Title);
|
|
OUT_str(Text);
|
|
OUT(Buttons);
|
|
OUT(Duration);
|
|
OUT(PopupID);
|
|
|
|
eq->unknown4236 = 0x00000000;
|
|
eq->unknown4240 = 0xffffffff;
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_PetBuffWindow)
|
|
{
|
|
ENCODE_LENGTH_EXACT(PetBuff_Struct);
|
|
SETUP_DIRECT_ENCODE(PetBuff_Struct, PetBuff_Struct);
|
|
|
|
OUT(petid);
|
|
OUT(buffcount);
|
|
|
|
int EQBuffSlot = 0;
|
|
|
|
for (uint32 EmuBuffSlot = 0; EmuBuffSlot < BUFF_COUNT; ++EmuBuffSlot)
|
|
{
|
|
if (emu->spellid[EmuBuffSlot])
|
|
{
|
|
eq->spellid[EQBuffSlot] = emu->spellid[EmuBuffSlot];
|
|
eq->ticsremaining[EQBuffSlot++] = emu->ticsremaining[EmuBuffSlot];
|
|
}
|
|
}
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_PlayerProfile)
|
|
{
|
|
SETUP_DIRECT_ENCODE(PlayerProfile_Struct, structs::PlayerProfile_Struct);
|
|
|
|
uint32 r;
|
|
|
|
eq->available_slots = 0xffffffff;
|
|
memset(eq->unknown4184, 0xff, sizeof(eq->unknown4184));
|
|
memset(eq->unknown04396, 0xff, sizeof(eq->unknown04396));
|
|
|
|
// OUT(checksum);
|
|
OUT(gender);
|
|
OUT(race);
|
|
OUT(class_);
|
|
// OUT(unknown00016);
|
|
OUT(level);
|
|
eq->level1 = emu->level;
|
|
// OUT(unknown00022[2]);
|
|
for (r = 0; r < 5; r++) {
|
|
OUT(binds[r].zoneId);
|
|
OUT(binds[r].x);
|
|
OUT(binds[r].y);
|
|
OUT(binds[r].z);
|
|
OUT(binds[r].heading);
|
|
}
|
|
OUT(deity);
|
|
OUT(intoxication);
|
|
OUT_array(spellSlotRefresh, structs::MAX_PP_MEMSPELL);
|
|
OUT(abilitySlotRefresh);
|
|
OUT(haircolor);
|
|
OUT(beardcolor);
|
|
OUT(eyecolor1);
|
|
OUT(eyecolor2);
|
|
OUT(hairstyle);
|
|
OUT(beard);
|
|
// OUT(unknown00178[10]);
|
|
for (r = 0; r < 9; r++) {
|
|
OUT(item_material[r]);
|
|
OUT(item_tint[r].color);
|
|
}
|
|
// OUT(unknown00224[48]);
|
|
for (r = 0; r < structs::MAX_PP_AA_ARRAY; r++) {
|
|
OUT(aa_array[r].AA);
|
|
OUT(aa_array[r].value);
|
|
}
|
|
// OUT(unknown02220[4]);
|
|
OUT(points);
|
|
OUT(mana);
|
|
OUT(cur_hp);
|
|
OUT(STR);
|
|
OUT(STA);
|
|
OUT(CHA);
|
|
OUT(DEX);
|
|
OUT(INT);
|
|
OUT(AGI);
|
|
OUT(WIS);
|
|
OUT(face);
|
|
// OUT(unknown02264[47]);
|
|
OUT_array(spell_book, structs::MAX_PP_SPELLBOOK);
|
|
// OUT(unknown4184[448]);
|
|
OUT_array(mem_spells, structs::MAX_PP_MEMSPELL);
|
|
// OUT(unknown04396[32]);
|
|
OUT(platinum);
|
|
OUT(gold);
|
|
OUT(silver);
|
|
OUT(copper);
|
|
OUT(platinum_cursor);
|
|
OUT(gold_cursor);
|
|
OUT(silver_cursor);
|
|
OUT(copper_cursor);
|
|
|
|
OUT_array(skills, structs::MAX_PP_SKILL); // 1:1 direct copy (100 dword)
|
|
|
|
// OUT(unknown04760[236]);
|
|
OUT(toxicity);
|
|
OUT(thirst_level);
|
|
OUT(hunger_level);
|
|
for (r = 0; r < structs::BUFF_COUNT; r++) {
|
|
OUT(buffs[r].slotid);
|
|
OUT(buffs[r].level);
|
|
OUT(buffs[r].bard_modifier);
|
|
OUT(buffs[r].effect);
|
|
OUT(buffs[r].spellid);
|
|
OUT(buffs[r].duration);
|
|
OUT(buffs[r].counters);
|
|
OUT(buffs[r].player_id);
|
|
}
|
|
for (r = 0; r < structs::MAX_PP_DISCIPLINES; r++) {
|
|
OUT(disciplines.values[r]);
|
|
}
|
|
// OUT(unknown05008[360]);
|
|
// OUT_array(recastTimers, structs::MAX_RECAST_TYPES);
|
|
OUT(endurance);
|
|
OUT(aapoints_spent);
|
|
OUT(aapoints);
|
|
// OUT(unknown06160[4]);
|
|
for (r = 0; r < structs::MAX_PLAYER_BANDOLIER; r++) {
|
|
OUT_str(bandoliers[r].name);
|
|
uint32 k;
|
|
for (k = 0; k < structs::MAX_PLAYER_BANDOLIER_ITEMS; k++) {
|
|
OUT(bandoliers[r].items[k].item_id);
|
|
OUT(bandoliers[r].items[k].icon);
|
|
OUT_str(bandoliers[r].items[k].item_name);
|
|
}
|
|
}
|
|
// OUT(unknown07444[5120]);
|
|
for (r = 0; r < structs::MAX_PLAYER_BANDOLIER_ITEMS; r++) {
|
|
OUT(potionbelt.items[r].item_id);
|
|
OUT(potionbelt.items[r].icon);
|
|
OUT_str(potionbelt.items[r].item_name);
|
|
}
|
|
// OUT(unknown12852[8]);
|
|
// OUT(unknown12864[76]);
|
|
OUT_str(name);
|
|
OUT_str(last_name);
|
|
OUT(guild_id);
|
|
OUT(birthday);
|
|
OUT(lastlogin);
|
|
OUT(timePlayedMin);
|
|
OUT(pvp);
|
|
OUT(anon);
|
|
OUT(gm);
|
|
OUT(guildrank);
|
|
OUT(guildbanker);
|
|
// OUT(unknown13054[8]);
|
|
OUT(exp);
|
|
// OUT(unknown13072[12]);
|
|
OUT(timeentitledonaccount);
|
|
OUT_array(languages, structs::MAX_PP_LANGUAGE);
|
|
// OUT(unknown13109[7]);
|
|
OUT(x);
|
|
OUT(y);
|
|
OUT(z);
|
|
OUT(heading);
|
|
// OUT(unknown13132[4]);
|
|
OUT(platinum_bank);
|
|
OUT(gold_bank);
|
|
OUT(silver_bank);
|
|
OUT(copper_bank);
|
|
OUT(platinum_shared);
|
|
// OUT(unknown13156[84]);
|
|
OUT(expansions);
|
|
// OUT(unknown13244[12]);
|
|
OUT(autosplit);
|
|
// OUT(unknown13260[16]);
|
|
OUT(zone_id);
|
|
OUT(zoneInstance);
|
|
for (r = 0; r < structs::MAX_GROUP_MEMBERS; r++) {
|
|
OUT_str(groupMembers[r]);
|
|
}
|
|
strcpy(eq->groupLeader, emu->groupMembers[0]);
|
|
// OUT_str(groupLeader);
|
|
// OUT(unknown13728[660]);
|
|
OUT(entityid);
|
|
OUT(leadAAActive);
|
|
// OUT(unknown14392[4]);
|
|
OUT(ldon_points_guk);
|
|
OUT(ldon_points_mir);
|
|
OUT(ldon_points_mmc);
|
|
OUT(ldon_points_ruj);
|
|
OUT(ldon_points_tak);
|
|
OUT(ldon_points_available);
|
|
// OUT(unknown14420[132]);
|
|
OUT(tribute_time_remaining);
|
|
OUT(career_tribute_points);
|
|
// OUT(unknown7208);
|
|
OUT(tribute_points);
|
|
// OUT(unknown7216);
|
|
OUT(tribute_active);
|
|
for (r = 0; r < structs::MAX_PLAYER_TRIBUTES; r++) {
|
|
OUT(tributes[r].tribute);
|
|
OUT(tributes[r].tier);
|
|
}
|
|
// OUT(unknown14616[8]);
|
|
OUT(group_leadership_exp);
|
|
OUT(raid_leadership_exp);
|
|
OUT(group_leadership_points);
|
|
OUT(raid_leadership_points);
|
|
OUT_array(leader_abilities.ranks, structs::MAX_LEADERSHIP_AA_ARRAY);
|
|
// OUT(unknown14772[128]);
|
|
OUT(air_remaining);
|
|
OUT(PVPKills);
|
|
OUT(PVPDeaths);
|
|
OUT(PVPCurrentPoints);
|
|
OUT(PVPCareerPoints);
|
|
OUT(PVPBestKillStreak);
|
|
OUT(PVPWorstDeathStreak);
|
|
OUT(PVPCurrentKillStreak);
|
|
// OUT(unknown14932[4580]);
|
|
OUT(expAA);
|
|
// OUT(unknown19516[40]);
|
|
OUT(currentRadCrystals);
|
|
OUT(careerRadCrystals);
|
|
OUT(currentEbonCrystals);
|
|
OUT(careerEbonCrystals);
|
|
OUT(groupAutoconsent);
|
|
OUT(raidAutoconsent);
|
|
OUT(guildAutoconsent);
|
|
// OUT(unknown19575[5]);
|
|
eq->level3 = emu->level;
|
|
eq->showhelm = emu->showhelm;
|
|
// OUT(unknown19584[4]);
|
|
// OUT(unknown19588);
|
|
|
|
|
|
const uint8 bytes[] = {
|
|
0x78, 0x03, 0x00, 0x00, 0x1A, 0x04, 0x00, 0x00, 0x1A, 0x04, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00,
|
|
0x19, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00,
|
|
0x0F, 0x00, 0x00, 0x00, 0x1F, 0x85, 0xEB, 0x3E, 0x33, 0x33, 0x33, 0x3F, 0x09, 0x00, 0x00, 0x00,
|
|
0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14
|
|
};
|
|
memcpy(eq->unknown12864, bytes, sizeof(bytes));
|
|
|
|
//set the checksum...
|
|
CRC32::SetEQChecksum(__packet->pBuffer, sizeof(structs::PlayerProfile_Struct) - 4);
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_ReadBook)
|
|
{
|
|
// no apparent slot translation needed -U
|
|
EQApplicationPacket *in = *p;
|
|
*p = nullptr;
|
|
|
|
unsigned char *__emu_buffer = in->pBuffer;
|
|
|
|
BookText_Struct *emu_BookText_Struct = (BookText_Struct *)__emu_buffer;
|
|
|
|
in->size = sizeof(structs::BookText_Struct) + strlen(emu_BookText_Struct->booktext);
|
|
in->pBuffer = new unsigned char[in->size];
|
|
|
|
structs::BookText_Struct *eq_BookText_Struct = (structs::BookText_Struct*)in->pBuffer;
|
|
|
|
eq_BookText_Struct->window = emu_BookText_Struct->window;
|
|
eq_BookText_Struct->type = emu_BookText_Struct->type;
|
|
strcpy(eq_BookText_Struct->booktext, emu_BookText_Struct->booktext);
|
|
|
|
delete[] __emu_buffer;
|
|
dest->FastQueuePacket(&in, ack_req);
|
|
}
|
|
|
|
ENCODE(OP_RespondAA)
|
|
{
|
|
ENCODE_LENGTH_EXACT(AATable_Struct);
|
|
SETUP_DIRECT_ENCODE(AATable_Struct, structs::AATable_Struct);
|
|
|
|
unsigned int r;
|
|
for (r = 0; r < structs::MAX_PP_AA_ARRAY; r++) {
|
|
OUT(aa_list[r].aa_skill);
|
|
OUT(aa_list[r].aa_value);
|
|
}
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_SendAATable)
|
|
{
|
|
ENCODE_LENGTH_ATLEAST(SendAA_Struct);
|
|
|
|
SETUP_VAR_ENCODE(SendAA_Struct);
|
|
ALLOC_VAR_ENCODE(structs::SendAA_Struct, sizeof(structs::SendAA_Struct) + emu->total_abilities*sizeof(structs::AA_Ability));
|
|
|
|
// Check clientver field to verify this AA should be sent for Titanium
|
|
// clientver 1 is for all clients and 3 is for Titanium
|
|
if (emu->clientver <= 3)
|
|
{
|
|
OUT(id);
|
|
eq->unknown004 = 1;
|
|
eq->hotkey_sid = (emu->hotkey_sid == 4294967295UL) ? 0 : (emu->id - emu->current_level + 1);
|
|
eq->hotkey_sid2 = (emu->hotkey_sid2 == 4294967295UL) ? 0 : (emu->id - emu->current_level + 1);
|
|
eq->title_sid = emu->id - emu->current_level + 1;
|
|
eq->desc_sid = emu->id - emu->current_level + 1;
|
|
OUT(class_type);
|
|
OUT(cost);
|
|
OUT(seq);
|
|
OUT(current_level);
|
|
OUT(prereq_skill);
|
|
OUT(prereq_minpoints);
|
|
OUT(type);
|
|
OUT(spellid);
|
|
OUT(spell_type);
|
|
OUT(spell_refresh);
|
|
OUT(classes);
|
|
OUT(berserker);
|
|
OUT(max_level);
|
|
OUT(last_id);
|
|
OUT(next_id);
|
|
OUT(cost2);
|
|
OUT(unknown80[0]);
|
|
OUT(unknown80[1]);
|
|
OUT(total_abilities);
|
|
unsigned int r;
|
|
for (r = 0; r < emu->total_abilities; r++) {
|
|
OUT(abilities[r].skill_id);
|
|
OUT(abilities[r].base1);
|
|
OUT(abilities[r].base2);
|
|
OUT(abilities[r].slot);
|
|
}
|
|
}
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_SendCharInfo)
|
|
{
|
|
ENCODE_LENGTH_EXACT(CharacterSelect_Struct);
|
|
SETUP_DIRECT_ENCODE(CharacterSelect_Struct, structs::CharacterSelect_Struct);
|
|
|
|
int r;
|
|
for (r = 0; r < 10; r++) {
|
|
OUT(zone[r]);
|
|
OUT(eyecolor1[r]);
|
|
OUT(eyecolor2[r]);
|
|
OUT(hairstyle[r]);
|
|
OUT(primary[r]);
|
|
if (emu->race[r] > 473)
|
|
eq->race[r] = 1;
|
|
else
|
|
eq->race[r] = emu->race[r];
|
|
OUT(class_[r]);
|
|
OUT_str(name[r]);
|
|
OUT(gender[r]);
|
|
OUT(level[r]);
|
|
OUT(secondary[r]);
|
|
OUT(face[r]);
|
|
OUT(beard[r]);
|
|
int k;
|
|
for (k = 0; k < 9; k++) {
|
|
eq->equip[r][k] = emu->equip[r][k].material;
|
|
eq->cs_colors[r][k].color = emu->equip[r][k].color.color;
|
|
}
|
|
OUT(haircolor[r]);
|
|
OUT(gohome[r]);
|
|
OUT(tutorial[r]);
|
|
OUT(deity[r]);
|
|
OUT(beardcolor[r]);
|
|
eq->unknown820[r] = 0xFF;
|
|
eq->unknown902[r] = 0xFF;
|
|
}
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_ShopPlayerSell)
|
|
{
|
|
ENCODE_LENGTH_EXACT(Merchant_Purchase_Struct);
|
|
SETUP_DIRECT_ENCODE(Merchant_Purchase_Struct, structs::Merchant_Purchase_Struct);
|
|
|
|
OUT(npcid);
|
|
eq->itemslot = ServerToTitaniumSlot(emu->itemslot);
|
|
OUT(quantity);
|
|
OUT(price);
|
|
|
|
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;
|
|
ServerToTitaniumTextLink(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;
|
|
*p = nullptr;
|
|
|
|
unsigned char *__emu_buffer = in->pBuffer;
|
|
Track_Struct *emu = (Track_Struct *)__emu_buffer;
|
|
|
|
int EntryCount = in->size / sizeof(Track_Struct);
|
|
|
|
if (EntryCount == 0 || ((in->size % sizeof(Track_Struct))) != 0)
|
|
{
|
|
Log.Out(Logs::General, Logs::Netcode, "[STRUCTS] Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Track_Struct));
|
|
delete in;
|
|
return;
|
|
}
|
|
|
|
in->size = sizeof(structs::Track_Struct) * EntryCount;
|
|
in->pBuffer = new unsigned char[in->size];
|
|
structs::Track_Struct *eq = (structs::Track_Struct *) in->pBuffer;
|
|
|
|
for (int i = 0; i < EntryCount; ++i, ++eq, ++emu)
|
|
{
|
|
OUT(entityid);
|
|
//OUT(padding002);
|
|
OUT(distance);
|
|
}
|
|
|
|
delete[] __emu_buffer;
|
|
dest->FastQueuePacket(&in, ack_req);
|
|
}
|
|
|
|
ENCODE(OP_Trader)
|
|
{
|
|
if ((*p)->size != sizeof(TraderBuy_Struct)) {
|
|
EQApplicationPacket *in = *p;
|
|
*p = nullptr;
|
|
dest->FastQueuePacket(&in, ack_req);
|
|
return;
|
|
}
|
|
|
|
ENCODE_FORWARD(OP_TraderBuy);
|
|
}
|
|
|
|
ENCODE(OP_TraderBuy)
|
|
{
|
|
ENCODE_LENGTH_EXACT(TraderBuy_Struct);
|
|
SETUP_DIRECT_ENCODE(TraderBuy_Struct, structs::TraderBuy_Struct);
|
|
|
|
OUT(Action);
|
|
OUT(Price);
|
|
OUT(TraderID);
|
|
memcpy(eq->ItemName, emu->ItemName, sizeof(eq->ItemName));
|
|
OUT(ItemID);
|
|
OUT(Quantity);
|
|
OUT(AlreadySold);
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_TributeItem)
|
|
{
|
|
ENCODE_LENGTH_EXACT(TributeItem_Struct);
|
|
SETUP_DIRECT_ENCODE(TributeItem_Struct, structs::TributeItem_Struct);
|
|
|
|
eq->slot = ServerToTitaniumSlot(emu->slot);
|
|
OUT(quantity);
|
|
OUT(tribute_master_id);
|
|
OUT(tribute_points);
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_VetRewardsAvaliable)
|
|
{
|
|
EQApplicationPacket *inapp = *p;
|
|
unsigned char * __emu_buffer = inapp->pBuffer;
|
|
|
|
uint32 count = ((*p)->Size() / sizeof(InternalVeteranReward));
|
|
*p = nullptr;
|
|
|
|
EQApplicationPacket *outapp_create = new EQApplicationPacket(OP_VetRewardsAvaliable, (sizeof(structs::VeteranReward)*count));
|
|
uchar *old_data = __emu_buffer;
|
|
uchar *data = outapp_create->pBuffer;
|
|
for (uint32 i = 0; i < count; ++i)
|
|
{
|
|
structs::VeteranReward *vr = (structs::VeteranReward*)data;
|
|
InternalVeteranReward *ivr = (InternalVeteranReward*)old_data;
|
|
|
|
vr->claim_id = ivr->claim_id;
|
|
vr->item.item_id = ivr->items[0].item_id;
|
|
strcpy(vr->item.item_name, ivr->items[0].item_name);
|
|
|
|
old_data += sizeof(InternalVeteranReward);
|
|
data += sizeof(structs::VeteranReward);
|
|
}
|
|
|
|
dest->FastQueuePacket(&outapp_create);
|
|
delete inapp;
|
|
}
|
|
|
|
ENCODE(OP_WearChange)
|
|
{
|
|
ENCODE_LENGTH_EXACT(WearChange_Struct);
|
|
SETUP_DIRECT_ENCODE(WearChange_Struct, structs::WearChange_Struct);
|
|
|
|
OUT(spawn_id);
|
|
OUT(material);
|
|
OUT(color.color);
|
|
OUT(wear_slot_id);
|
|
|
|
FINISH_ENCODE();
|
|
}
|
|
|
|
ENCODE(OP_ZoneEntry) { ENCODE_FORWARD(OP_ZoneSpawns); }
|
|
|
|
ENCODE(OP_ZoneSpawns)
|
|
{
|
|
//consume the packet
|
|
EQApplicationPacket *in = *p;
|
|
*p = nullptr;
|
|
|
|
//store away the emu struct
|
|
unsigned char *__emu_buffer = in->pBuffer;
|
|
Spawn_Struct *emu = (Spawn_Struct *)__emu_buffer;
|
|
|
|
//determine and verify length
|
|
int entrycount = in->size / sizeof(Spawn_Struct);
|
|
if (entrycount == 0 || (in->size % sizeof(Spawn_Struct)) != 0) {
|
|
Log.Out(Logs::General, Logs::Netcode, "[STRUCTS] Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Spawn_Struct));
|
|
delete in;
|
|
return;
|
|
}
|
|
|
|
//make the EQ struct.
|
|
in->size = sizeof(structs::Spawn_Struct)*entrycount;
|
|
in->pBuffer = new unsigned char[in->size];
|
|
structs::Spawn_Struct *eq = (structs::Spawn_Struct *) in->pBuffer;
|
|
|
|
//zero out the packet. We could avoid this memset by setting all fields (including unknowns)
|
|
//in the loop.
|
|
memset(in->pBuffer, 0, in->size);
|
|
|
|
//do the transform...
|
|
int r;
|
|
int k;
|
|
for (r = 0; r < entrycount; r++, eq++, emu++) {
|
|
// eq->unknown0000 = emu->unknown0000;
|
|
eq->gm = emu->gm;
|
|
// eq->unknown0003 = emu->unknown0003;
|
|
eq->aaitle = emu->aaitle;
|
|
// eq->unknown0004 = emu->unknown0004;
|
|
eq->anon = emu->anon;
|
|
eq->face = emu->face;
|
|
strcpy(eq->name, emu->name);
|
|
eq->deity = emu->deity;
|
|
// eq->unknown0073 = emu->unknown0073;
|
|
eq->size = emu->size;
|
|
// eq->unknown0079 = emu->unknown0079;
|
|
eq->NPC = emu->NPC;
|
|
eq->invis = emu->invis;
|
|
eq->haircolor = emu->haircolor;
|
|
eq->curHp = emu->curHp;
|
|
eq->max_hp = emu->max_hp;
|
|
eq->findable = emu->findable;
|
|
// eq->unknown0089[5] = emu->unknown0089[5];
|
|
eq->deltaHeading = emu->deltaHeading;
|
|
eq->x = emu->x;
|
|
// eq->padding0054 = emu->padding0054;
|
|
eq->y = emu->y;
|
|
eq->animation = emu->animation;
|
|
// eq->padding0058 = emu->padding0058;
|
|
eq->z = emu->z;
|
|
eq->deltaY = emu->deltaY;
|
|
eq->deltaX = emu->deltaX;
|
|
eq->heading = emu->heading;
|
|
// eq->padding0066 = emu->padding0066;
|
|
eq->deltaZ = emu->deltaZ;
|
|
// eq->padding0070 = emu->padding0070;
|
|
eq->eyecolor1 = emu->eyecolor1;
|
|
// eq->unknown0115[24] = emu->unknown0115[24];
|
|
eq->showhelm = emu->showhelm;
|
|
// eq->unknown0140[4] = emu->unknown0140[4];
|
|
eq->is_npc = emu->is_npc;
|
|
eq->hairstyle = emu->hairstyle;
|
|
|
|
//if(emu->gender == 1){
|
|
// eq->hairstyle = eq->hairstyle == 0xFF ? 0 : eq->hairstyle;
|
|
//}
|
|
|
|
eq->beardcolor = emu->beardcolor;
|
|
// eq->unknown0147[4] = emu->unknown0147[4];
|
|
eq->level = emu->level;
|
|
// eq->unknown0259[4] = emu->unknown0259[4];
|
|
eq->beard = emu->beard;
|
|
strcpy(eq->suffix, emu->suffix);
|
|
eq->petOwnerId = emu->petOwnerId;
|
|
eq->guildrank = emu->guildrank;
|
|
// eq->unknown0194[3] = emu->unknown0194[3];
|
|
for (k = 0; k < 9; k++) {
|
|
eq->equipment[k] = emu->equipment[k].material;
|
|
eq->colors[k].color = emu->colors[k].color;
|
|
}
|
|
for (k = 0; k < 8; k++) {
|
|
eq->set_to_0xFF[k] = 0xFF;
|
|
}
|
|
|
|
eq->runspeed = emu->runspeed;
|
|
eq->afk = emu->afk;
|
|
eq->guildID = emu->guildID;
|
|
strcpy(eq->title, emu->title);
|
|
// eq->unknown0274 = emu->unknown0274;
|
|
eq->helm = emu->helm;
|
|
if (emu->race > 473)
|
|
eq->race = 1;
|
|
else
|
|
eq->race = emu->race;
|
|
// eq->unknown0288 = emu->unknown0288;
|
|
strcpy(eq->lastName, emu->lastName);
|
|
eq->walkspeed = emu->walkspeed;
|
|
// eq->unknown0328 = emu->unknown0328;
|
|
eq->is_pet = emu->is_pet;
|
|
eq->light = emu->light;
|
|
eq->class_ = emu->class_;
|
|
eq->eyecolor2 = emu->eyecolor2;
|
|
// eq->unknown0333 = emu->unknown0333;
|
|
eq->flymode = emu->flymode;
|
|
eq->gender = emu->gender;
|
|
eq->bodytype = emu->bodytype;
|
|
// eq->unknown0336[3] = emu->unknown0336[3];
|
|
eq->equip_chest2 = emu->equip_chest2;
|
|
eq->spawnId = emu->spawnId;
|
|
// eq->unknown0344[4] = emu->unknown0344[4];
|
|
eq->lfg = emu->lfg;
|
|
|
|
/*
|
|
if (emu->face == 99) {eq->face = 0;}
|
|
if (emu->eyecolor1 == 99) {eq->eyecolor1 = 0;}
|
|
if (emu->eyecolor2 == 99) {eq->eyecolor2 = 0;}
|
|
if (emu->hairstyle == 99) {eq->hairstyle = 0;}
|
|
if (emu->haircolor == 99) {eq->haircolor = 0;}
|
|
if (emu->beard == 99) {eq->beard = 0;}
|
|
if (emu->beardcolor == 99) {eq->beardcolor = 0;}
|
|
*/
|
|
|
|
}
|
|
|
|
//kill off the emu structure and send the eq packet.
|
|
delete[] __emu_buffer;
|
|
dest->FastQueuePacket(&in, ack_req);
|
|
}
|
|
|
|
// DECODE methods
|
|
DECODE(OP_AdventureMerchantSell)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::Adventure_Sell_Struct);
|
|
SETUP_DIRECT_DECODE(Adventure_Sell_Struct, structs::Adventure_Sell_Struct);
|
|
|
|
IN(npcid);
|
|
emu->slot = TitaniumToServerSlot(eq->slot);
|
|
IN(charges);
|
|
IN(sell_price);
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_ApplyPoison)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::ApplyPoison_Struct);
|
|
SETUP_DIRECT_DECODE(ApplyPoison_Struct, structs::ApplyPoison_Struct);
|
|
|
|
emu->inventorySlot = TitaniumToServerSlot(eq->inventorySlot);
|
|
IN(success);
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_AugmentItem)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::AugmentItem_Struct);
|
|
SETUP_DIRECT_DECODE(AugmentItem_Struct, structs::AugmentItem_Struct);
|
|
|
|
emu->container_slot = TitaniumToServerSlot(eq->container_slot);
|
|
emu->augment_slot = eq->augment_slot;
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_CastSpell)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::CastSpell_Struct);
|
|
SETUP_DIRECT_DECODE(CastSpell_Struct, structs::CastSpell_Struct);
|
|
|
|
IN(slot);
|
|
IN(spell_id);
|
|
emu->inventoryslot = TitaniumToServerSlot(eq->inventoryslot);
|
|
IN(target_id);
|
|
|
|
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;
|
|
TitaniumToServerTextLink(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);
|
|
SETUP_DIRECT_DECODE(CharCreate_Struct, structs::CharCreate_Struct);
|
|
|
|
IN(class_);
|
|
IN(beardcolor);
|
|
IN(beard);
|
|
IN(haircolor);
|
|
IN(gender);
|
|
IN(race);
|
|
IN(start_zone);
|
|
IN(hairstyle);
|
|
IN(deity);
|
|
IN(STR);
|
|
IN(STA);
|
|
IN(AGI);
|
|
IN(DEX);
|
|
IN(WIS);
|
|
IN(INT);
|
|
IN(CHA);
|
|
IN(face);
|
|
IN(eyecolor1);
|
|
IN(eyecolor2);
|
|
IN(tutorial);
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_Consume)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::Consume_Struct);
|
|
SETUP_DIRECT_DECODE(Consume_Struct, structs::Consume_Struct);
|
|
|
|
emu->slot = TitaniumToServerSlot(eq->slot);
|
|
IN(auto_consumed);
|
|
IN(type);
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_DeleteItem)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::DeleteItem_Struct);
|
|
SETUP_DIRECT_DECODE(DeleteItem_Struct, structs::DeleteItem_Struct);
|
|
|
|
emu->from_slot = TitaniumToServerSlot(eq->from_slot);
|
|
emu->to_slot =TitaniumToServerSlot(eq->to_slot);
|
|
IN(number_in_stack);
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_FaceChange)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::FaceChange_Struct);
|
|
SETUP_DIRECT_DECODE(FaceChange_Struct, structs::FaceChange_Struct);
|
|
IN(haircolor);
|
|
IN(beardcolor);
|
|
IN(eyecolor1);
|
|
IN(eyecolor2);
|
|
IN(hairstyle);
|
|
IN(beard);
|
|
IN(face);
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_InspectAnswer)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::InspectResponse_Struct);
|
|
SETUP_DIRECT_DECODE(InspectResponse_Struct, structs::InspectResponse_Struct);
|
|
|
|
IN(TargetID);
|
|
IN(playerid);
|
|
|
|
int r;
|
|
for (r = 0; r <= 20; r++) {
|
|
strn0cpy(emu->itemnames[r], eq->itemnames[r], sizeof(emu->itemnames[r]));
|
|
}
|
|
|
|
// move arrow item up to last element in server array
|
|
strn0cpy(emu->itemnames[21], "", sizeof(emu->itemnames[21]));
|
|
strn0cpy(emu->itemnames[22], eq->itemnames[21], sizeof(emu->itemnames[22]));
|
|
|
|
int k;
|
|
for (k = 0; k <= 20; k++) {
|
|
IN(itemicons[k]);
|
|
}
|
|
|
|
// move arrow icon up to last element in server array
|
|
emu->itemicons[21] = 0xFFFFFFFF;
|
|
emu->itemicons[22] = eq->itemicons[21];
|
|
|
|
strn0cpy(emu->text, eq->text, sizeof(emu->text));
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_InspectRequest)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::Inspect_Struct);
|
|
SETUP_DIRECT_DECODE(Inspect_Struct, structs::Inspect_Struct);
|
|
|
|
IN(TargetID);
|
|
IN(PlayerID);
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_ItemLinkClick)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::ItemViewRequest_Struct);
|
|
SETUP_DIRECT_DECODE(ItemViewRequest_Struct, structs::ItemViewRequest_Struct);
|
|
MEMSET_IN(ItemViewRequest_Struct);
|
|
|
|
IN(item_id);
|
|
int r;
|
|
for (r = 0; r < 5; r++) {
|
|
IN(augments[r]);
|
|
}
|
|
IN(link_hash);
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_LFGuild)
|
|
{
|
|
uint32 Command = __packet->ReadUInt32();
|
|
|
|
if (Command != 0)
|
|
return;
|
|
|
|
SETUP_DIRECT_DECODE(LFGuild_PlayerToggle_Struct, structs::LFGuild_PlayerToggle_Struct);
|
|
memcpy(emu, eq, sizeof(structs::LFGuild_PlayerToggle_Struct));
|
|
memset(emu->Unknown612, 0, sizeof(emu->Unknown612));
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_LootItem)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::LootingItem_Struct);
|
|
SETUP_DIRECT_DECODE(LootingItem_Struct, structs::LootingItem_Struct);
|
|
|
|
IN(lootee);
|
|
IN(looter);
|
|
emu->slot_id = TitaniumToServerCorpseSlot(eq->slot_id);
|
|
IN(auto_loot);
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_MoveItem)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::MoveItem_Struct);
|
|
SETUP_DIRECT_DECODE(MoveItem_Struct, structs::MoveItem_Struct);
|
|
|
|
Log.Out(Logs::General, Logs::Netcode, "[ERROR] Moved item from %u to %u", eq->from_slot, eq->to_slot);
|
|
|
|
emu->from_slot = TitaniumToServerSlot(eq->from_slot);
|
|
emu->to_slot = TitaniumToServerSlot(eq->to_slot);
|
|
IN(number_in_stack);
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_PetCommands)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::PetCommand_Struct);
|
|
SETUP_DIRECT_DECODE(PetCommand_Struct, structs::PetCommand_Struct);
|
|
|
|
switch (eq->command)
|
|
{
|
|
case 0x04:
|
|
emu->command = 0x00; // /pet health
|
|
break;
|
|
case 0x10:
|
|
emu->command = 0x01; // /pet leader
|
|
break;
|
|
case 0x07:
|
|
emu->command = 0x02; // /pet attack or Pet Window
|
|
break;
|
|
case 0x03: // Case Guessed
|
|
emu->command = 0x03; // /pet qattack
|
|
case 0x08:
|
|
emu->command = 0x04; // /pet follow or Pet Window
|
|
break;
|
|
case 0x05:
|
|
emu->command = 0x05; // /pet guard or Pet Window
|
|
break;
|
|
case 0x09:
|
|
emu->command = 0x07; // /pet sit or Pet Window
|
|
break;
|
|
case 0x0a:
|
|
emu->command = 0x08; // /pet stand or Pet Window
|
|
break;
|
|
case 0x06:
|
|
emu->command = 0x1e; // /pet guard me
|
|
break;
|
|
case 0x0f: // Case Made Up
|
|
emu->command = 0x09; // Stop?
|
|
break;
|
|
case 0x0b:
|
|
emu->command = 0x0d; // /pet taunt or Pet Window
|
|
break;
|
|
case 0x0e:
|
|
emu->command = 0x0e; // /pet notaunt or Pet Window
|
|
break;
|
|
case 0x0c:
|
|
emu->command = 0x0f; // /pet hold
|
|
break;
|
|
case 0x1b:
|
|
emu->command = 0x10; // /pet hold on
|
|
break;
|
|
case 0x1c:
|
|
emu->command = 0x11; // /pet hold off
|
|
break;
|
|
case 0x11:
|
|
emu->command = 0x12; // Slumber?
|
|
break;
|
|
case 0x12:
|
|
emu->command = 0x15; // /pet no cast
|
|
break;
|
|
case 0x0d: // Case Made Up
|
|
emu->command = 0x16; // Pet Window No Cast
|
|
break;
|
|
case 0x13:
|
|
emu->command = 0x18; // /pet focus
|
|
break;
|
|
case 0x19:
|
|
emu->command = 0x19; // /pet focus on
|
|
break;
|
|
case 0x1a:
|
|
emu->command = 0x1a; // /pet focus off
|
|
break;
|
|
case 0x01:
|
|
emu->command = 0x1c; // /pet back off
|
|
break;
|
|
case 0x02:
|
|
emu->command = 0x1d; // /pet get lost
|
|
break;
|
|
default:
|
|
emu->command = eq->command;
|
|
}
|
|
OUT(unknown);
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_ReadBook)
|
|
{
|
|
// no apparent slot translation needed -U
|
|
DECODE_LENGTH_ATLEAST(structs::BookRequest_Struct);
|
|
SETUP_DIRECT_DECODE(BookRequest_Struct, structs::BookRequest_Struct);
|
|
|
|
IN(window);
|
|
IN(type);
|
|
strn0cpy(emu->txtfile, eq->txtfile, sizeof(emu->txtfile));
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_SetServerFilter)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::SetServerFilter_Struct);
|
|
SETUP_DIRECT_DECODE(SetServerFilter_Struct, structs::SetServerFilter_Struct);
|
|
|
|
int r;
|
|
for (r = 0; r < 29; r++) {
|
|
IN(filters[r]);
|
|
}
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_ShopPlayerSell)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::Merchant_Purchase_Struct);
|
|
SETUP_DIRECT_DECODE(Merchant_Purchase_Struct, structs::Merchant_Purchase_Struct);
|
|
|
|
IN(npcid);
|
|
emu->itemslot = TitaniumToServerSlot(eq->itemslot);
|
|
IN(quantity);
|
|
IN(price);
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_TraderBuy)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::TraderBuy_Struct);
|
|
SETUP_DIRECT_DECODE(TraderBuy_Struct, structs::TraderBuy_Struct);
|
|
MEMSET_IN(TraderBuy_Struct);
|
|
|
|
IN(Action);
|
|
IN(Price);
|
|
IN(TraderID);
|
|
memcpy(emu->ItemName, eq->ItemName, sizeof(emu->ItemName));
|
|
IN(ItemID);
|
|
IN(Quantity);
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_TradeSkillCombine)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::NewCombine_Struct);
|
|
SETUP_DIRECT_DECODE(NewCombine_Struct, structs::NewCombine_Struct);
|
|
|
|
emu->container_slot = TitaniumToServerSlot(eq->container_slot);
|
|
IN(guildtribute_slot);
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_TributeItem)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::TributeItem_Struct);
|
|
SETUP_DIRECT_DECODE(TributeItem_Struct, structs::TributeItem_Struct);
|
|
|
|
emu->slot = TitaniumToServerSlot(eq->slot);
|
|
IN(quantity);
|
|
IN(tribute_master_id);
|
|
IN(tribute_points);
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_WearChange)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::WearChange_Struct);
|
|
SETUP_DIRECT_DECODE(WearChange_Struct, structs::WearChange_Struct);
|
|
|
|
IN(spawn_id);
|
|
IN(material);
|
|
IN(color.color);
|
|
IN(wear_slot_id);
|
|
emu->unknown06 = 0;
|
|
emu->elite_material = 0;
|
|
emu->hero_forge_model = 0;
|
|
emu->unknown18 = 0;
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
DECODE(OP_WhoAllRequest)
|
|
{
|
|
DECODE_LENGTH_EXACT(structs::Who_All_Struct);
|
|
SETUP_DIRECT_DECODE(Who_All_Struct, structs::Who_All_Struct);
|
|
|
|
memcpy(emu->whom, eq->whom, sizeof(emu->whom));
|
|
IN(wrace);
|
|
IN(wclass);
|
|
IN(lvllow);
|
|
IN(lvlhigh);
|
|
IN(gmlookup);
|
|
emu->type = 3;
|
|
|
|
FINISH_DIRECT_DECODE();
|
|
}
|
|
|
|
// file scope helper methods
|
|
char *SerializeItem(const ItemInst *inst, int16 slot_id_in, uint32 *length, uint8 depth)
|
|
{
|
|
char *serialization = nullptr;
|
|
char *instance = nullptr;
|
|
const char *protection = (const char *)"\\\\\\\\\\";
|
|
char *sub_items[10] = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr };
|
|
bool stackable = inst->IsStackable();
|
|
int16 slot_id = ServerToTitaniumSlot(slot_id_in);
|
|
uint32 merchant_slot = inst->GetMerchantSlot();
|
|
int16 charges = inst->GetCharges();
|
|
const Item_Struct *item = inst->GetUnscaledItem();
|
|
int i;
|
|
uint32 sub_length;
|
|
|
|
MakeAnyLenString(&instance,
|
|
"%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|",
|
|
stackable ? charges : 0,
|
|
0,
|
|
//(merchant_slot == 0) ? slot_id : merchant_slot, // change when translator activated
|
|
(merchant_slot == 0) ? slot_id_in : merchant_slot,
|
|
inst->GetPrice(),
|
|
(merchant_slot == 0) ? 1 : inst->GetMerchantCount(),
|
|
inst->IsScaling() ? inst->GetExp() / 100 : 0,
|
|
//merchant_slot, //instance ID, bullshit for now
|
|
(merchant_slot == 0) ? inst->GetSerialNumber() : merchant_slot,
|
|
0, // item recast timer timestamp field (aka..last_cast_time field in SoF+ clients)
|
|
(stackable ? ((inst->GetItem()->ItemType == ItemTypePotion) ? 1 : 0) : charges),
|
|
inst->IsAttuned() ? 1 : 0,
|
|
0
|
|
);
|
|
|
|
for (i = 0; i<10; i++) {
|
|
ItemInst *sub = inst->GetItem(i);
|
|
if (sub) {
|
|
sub_items[i] = SerializeItem(sub, 0, &sub_length, depth + 1);
|
|
}
|
|
}
|
|
|
|
*length = MakeAnyLenString(&serialization,
|
|
"%.*s%s" // For leading quotes (and protection) if a subitem;
|
|
"%s" // Instance data
|
|
"%.*s\"" // Quotes (and protection, if needed) around static data
|
|
"%i" // item->ItemClass so we can do |%s instead of %s|
|
|
#define I(field) "|%i"
|
|
#define C(field) "|%s"
|
|
#define S(field) "|%s"
|
|
#define F(field) "|%f"
|
|
#include "titanium_itemfields.h"
|
|
"%.*s\"" // Quotes (and protection, if needed) around static data
|
|
"|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s" // Sub items
|
|
"%.*s%s" // For trailing quotes (and protection) if a subitem;
|
|
, depth ? depth - 1 : 0, protection, (depth) ? "\"" : ""
|
|
, instance
|
|
, depth, protection
|
|
, item->ItemClass
|
|
#define I(field) ,item->field
|
|
#define C(field) ,field
|
|
#define S(field) ,item->field
|
|
#define F(field) ,item->field
|
|
#include "titanium_itemfields.h"
|
|
, depth, protection
|
|
, sub_items[0] ? sub_items[0] : ""
|
|
, sub_items[1] ? sub_items[1] : ""
|
|
, sub_items[2] ? sub_items[2] : ""
|
|
, sub_items[3] ? sub_items[3] : ""
|
|
, sub_items[4] ? sub_items[4] : ""
|
|
, sub_items[5] ? sub_items[5] : ""
|
|
, sub_items[6] ? sub_items[6] : ""
|
|
, sub_items[7] ? sub_items[7] : ""
|
|
, sub_items[8] ? sub_items[8] : ""
|
|
, sub_items[9] ? sub_items[9] : ""
|
|
, (depth) ? depth - 1 : 0, protection, (depth) ? "\"" : ""
|
|
);
|
|
|
|
for (i = 0; i<10; i++) {
|
|
if (sub_items[i])
|
|
safe_delete_array(sub_items[i]);
|
|
}
|
|
|
|
safe_delete_array(instance);
|
|
return serialization;
|
|
}
|
|
|
|
static inline int16 ServerToTitaniumSlot(uint32 ServerSlot)
|
|
{
|
|
//int16 TitaniumSlot;
|
|
if (ServerSlot == INVALID_INDEX)
|
|
return INVALID_INDEX;
|
|
|
|
return ServerSlot; // deprecated
|
|
}
|
|
|
|
static inline int16 ServerToTitaniumCorpseSlot(uint32 ServerCorpse)
|
|
{
|
|
//int16 TitaniumCorpse;
|
|
return ServerCorpse;
|
|
}
|
|
|
|
static inline uint32 TitaniumToServerSlot(int16 TitaniumSlot)
|
|
{
|
|
//uint32 ServerSlot;
|
|
if (TitaniumSlot == INVALID_INDEX)
|
|
return INVALID_INDEX;
|
|
|
|
return TitaniumSlot; // deprecated
|
|
}
|
|
|
|
static inline uint32 TitaniumToServerCorpseSlot(int16 TitaniumCorpse)
|
|
{
|
|
//uint32 ServerCorpse;
|
|
return TitaniumCorpse;
|
|
}
|
|
|
|
static inline void ServerToTitaniumTextLink(std::string& titaniumTextLink, const std::string& serverTextLink)
|
|
{
|
|
const char delimiter = 0x12;
|
|
|
|
if ((consts::TEXT_LINK_BODY_LENGTH == EmuConstants::TEXT_LINK_BODY_LENGTH) || (serverTextLink.find(delimiter) == std::string::npos)) {
|
|
titaniumTextLink = serverTextLink;
|
|
return;
|
|
}
|
|
|
|
auto segments = SplitString(serverTextLink, delimiter);
|
|
|
|
for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) {
|
|
if (segment_iter & 1) {
|
|
std::string new_segment;
|
|
|
|
// Idx: 0 1 6 11 16 21 26 31 36 37 41 43 48 (Source)
|
|
// RoF2: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX XX XXXXX XXXXXXXX (56)
|
|
// 6.2: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX X XXXXXXXX (45)
|
|
// Diff: ^^^^^ ^ ^^^^^
|
|
|
|
new_segment.append(segments[segment_iter].substr(0, 31).c_str());
|
|
new_segment.append(segments[segment_iter].substr(36, 5).c_str());
|
|
|
|
if (segments[segment_iter].substr(41, 1) == "0")
|
|
new_segment.append(segments[segment_iter].substr(42, 1).c_str());
|
|
else
|
|
new_segment.append("F");
|
|
|
|
new_segment.append(segments[segment_iter].substr(48).c_str());
|
|
|
|
titaniumTextLink.push_back(delimiter);
|
|
titaniumTextLink.append(new_segment.c_str());
|
|
titaniumTextLink.push_back(delimiter);
|
|
}
|
|
else {
|
|
titaniumTextLink.append(segments[segment_iter].c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
static inline void TitaniumToServerTextLink(std::string& serverTextLink, const std::string& titaniumTextLink)
|
|
{
|
|
const char delimiter = 0x12;
|
|
|
|
if ((EmuConstants::TEXT_LINK_BODY_LENGTH == consts::TEXT_LINK_BODY_LENGTH) || (titaniumTextLink.find(delimiter) == std::string::npos)) {
|
|
serverTextLink = titaniumTextLink;
|
|
return;
|
|
}
|
|
|
|
auto segments = SplitString(titaniumTextLink, delimiter);
|
|
|
|
for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) {
|
|
if (segment_iter & 1) {
|
|
std::string new_segment;
|
|
|
|
// Idx: 0 1 6 11 16 21 26 31 32 36 37 (Source)
|
|
// 6.2: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX X XXXXXXXX (45)
|
|
// RoF2: X XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX X XXXX XX XXXXX XXXXXXXX (56)
|
|
// Diff: ^^^^^ ^ ^^^^^
|
|
|
|
new_segment.append(segments[segment_iter].substr(0, 31).c_str());
|
|
new_segment.append("00000");
|
|
new_segment.append(segments[segment_iter].substr(31, 5).c_str());
|
|
new_segment.append("0");
|
|
new_segment.append(segments[segment_iter].substr(36, 1).c_str());
|
|
new_segment.append("00000");
|
|
new_segment.append(segments[segment_iter].substr(37).c_str());
|
|
|
|
serverTextLink.push_back(delimiter);
|
|
serverTextLink.append(new_segment.c_str());
|
|
serverTextLink.push_back(delimiter);
|
|
}
|
|
else {
|
|
serverTextLink.append(segments[segment_iter].c_str());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// end namespace Titanium
|