diff --git a/old/CRC16.cpp b/old/CRC16.cpp new file mode 100644 index 0000000..f7b43ad --- /dev/null +++ b/old/CRC16.cpp @@ -0,0 +1,328 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#include + +unsigned long IntArray[]={ +0x00000000, +0x77073096, +0xEE0E612C, +0x990951BA, +0x076DC419, +0x706AF48F, +0xE963A535, +0x9E6495A3, +0x0EDB8832, +0x79DCB8A4, +0xE0D5E91E, +0x97D2D988, +0x09B64C2B, +0x7EB17CBD, +0xE7B82D07, +0x90BF1D91, +0x1DB71064, +0x6AB020F2, +0xF3B97148, +0x84BE41DE, +0x1ADAD47D, +0x6DDDE4EB, +0xF4D4B551, +0x83D385C7, +0x136C9856, +0x646BA8C0, +0xFD62F97A, +0x8A65C9EC, +0x14015C4F, +0x63066CD9, +0xFA0F3D63, +0x8D080DF5, +0x3B6E20C8, +0x4C69105E, +0xD56041E4, +0xA2677172, +0x3C03E4D1, +0x4B04D447, +0xD20D85FD, +0xA50AB56B, +0x35B5A8FA, +0x42B2986C, +0xDBBBC9D6, +0xACBCF940, +0x32D86CE3, +0x45DF5C75, +0xDCD60DCF, +0xABD13D59, +0x26D930AC, +0x51DE003A, +0xC8D75180, +0xBFD06116, +0x21B4F4B5, +0x56B3C423, +0xCFBA9599, +0xB8BDA50F, +0x2802B89E, +0x5F058808, +0xC60CD9B2, +0xB10BE924, +0x2F6F7C87, +0x58684C11, +0xC1611DAB, +0xB6662D3D, +0x76DC4190, +0x01DB7106, +0x98D220BC, +0xEFD5102A, +0x71B18589, +0x06B6B51F, +0x9FBFE4A5, +0xE8B8D433, +0x7807C9A2, +0x0F00F934, +0x9609A88E, +0xE10E9818, +0x7F6A0DBB, +0x086D3D2D, +0x91646C97, +0xE6635C01, +0x6B6B51F4, +0x1C6C6162, +0x856530D8, +0xF262004E, +0x6C0695ED, +0x1B01A57B, +0x8208F4C1, +0xF50FC457, +0x65B0D9C6, +0x12B7E950, +0x8BBEB8EA, +0xFCB9887C, +0x62DD1DDF, +0x15DA2D49, +0x8CD37CF3, +0xFBD44C65, +0x4DB26158, +0x3AB551CE, +0xA3BC0074, +0xD4BB30E2, +0x4ADFA541, +0x3DD895D7, +0xA4D1C46D, +0xD3D6F4FB, +0x4369E96A, +0x346ED9FC, +0xAD678846, +0xDA60B8D0, +0x44042D73, +0x33031DE5, +0xAA0A4C5F, +0xDD0D7CC9, +0x5005713C, +0x270241AA, +0xBE0B1010, +0xC90C2086, +0x5768B525, +0x206F85B3, +0xB966D409, +0xCE61E49F, +0x5EDEF90E, +0x29D9C998, +0xB0D09822, +0xC7D7A8B4, +0x59B33D17, +0x2EB40D81, +0xB7BD5C3B, +0xC0BA6CAD, +0xEDB88320, +0x9ABFB3B6, +0x03B6E20C, +0x74B1D29A, +0xEAD54739, +0x9DD277AF, +0x04DB2615, +0x73DC1683, +0xE3630B12, +0x94643B84, +0x0D6D6A3E, +0x7A6A5AA8, +0xE40ECF0B, +0x9309FF9D, +0x0A00AE27, +0x7D079EB1, +0xF00F9344, +0x8708A3D2, +0x1E01F268, +0x6906C2FE, +0xF762575D, +0x806567CB, +0x196C3671, +0x6E6B06E7, +0xFED41B76, +0x89D32BE0, +0x10DA7A5A, +0x67DD4ACC, +0xF9B9DF6F, +0x8EBEEFF9, +0x17B7BE43, +0x60B08ED5, +0xD6D6A3E8, +0xA1D1937E, +0x38D8C2C4, +0x4FDFF252, +0xD1BB67F1, +0xA6BC5767, +0x3FB506DD, +0x48B2364B, +0xD80D2BDA, +0xAF0A1B4C, +0x36034AF6, +0x41047A60, +0xDF60EFC3, +0xA867DF55, +0x316E8EEF, +0x4669BE79, +0xCB61B38C, +0xBC66831A, +0x256FD2A0, +0x5268E236, +0xCC0C7795, +0xBB0B4703, +0x220216B9, +0x5505262F, +0xC5BA3BBE, +0xB2BD0B28, +0x2BB45A92, +0x5CB36A04, +0xC2D7FFA7, +0xB5D0CF31, +0x2CD99E8B, +0x5BDEAE1D, +0x9B64C2B0, +0xEC63F226, +0x756AA39C, +0x026D930A, +0x9C0906A9, +0xEB0E363F, +0x72076785, +0x05005713, +0x95BF4A82, +0xE2B87A14, +0x7BB12BAE, +0x0CB61B38, +0x92D28E9B, +0xE5D5BE0D, +0x7CDCEFB7, +0x0BDBDF21, +0x86D3D2D4, +0xF1D4E242, +0x68DDB3F8, +0x1FDA836E, +0x81BE16CD, +0xF6B9265B, +0x6FB077E1, +0x18B74777, +0x88085AE6, +0xFF0F6A70, +0x66063BCA, +0x11010B5C, +0x8F659EFF, +0xF862AE69, +0x616BFFD3, +0x166CCF45, +0xA00AE278, +0xD70DD2EE, +0x4E048354, +0x3903B3C2, +0xA7672661, +0xD06016F7, +0x4969474D, +0x3E6E77DB, +0xAED16A4A, +0xD9D65ADC, +0x40DF0B66, +0x37D83BF0, +0xA9BCAE53, +0xDEBB9EC5, +0x47B2CF7F, +0x30B5FFE9, +0xBDBDF21C, +0xCABAC28A, +0x53B39330, +0x24B4A3A6, +0xBAD03605, +0xCDD70693, +0x54DE5729, +0x23D967BF, +0xB3667A2E, +0xC4614AB8, +0x5D681B02, +0x2A6F2B94, +0xB40BBE37, +0xC30C8EA1, +0x5A05DF1B, +0x2D02EF8D, +}; + +unsigned long CRC16(const unsigned char *buf, int size, int key) +{ + unsigned long ecx = key; //mov ecx, [esp+arg_8] + unsigned long eax = ecx; //mov eax, ecx + unsigned long edi; + + eax = ~ eax; //not eax + eax&=0xFF; //and eax, 0FFh + eax=IntArray[eax]; //mov eax, dword_0_10115D38[eax*4] IntArray + eax ^= 0x00FFFFFF; //xor eax, 0FFFFFFh + int edx = ecx; //mov edx, ecx + edx = edx >> 8; //sar edx, 8 + edx = edx ^ eax; //xor edx, eax + eax = eax >> 8; //sar eax, 8 + edx &= 0xFF; //and edx, 0FFh + eax &= 0x00FFFFFF; //and eax, 0FFFFFFh + eax ^= IntArray[edx]; //xor eax, dword_0_10115D38[edx*4] + edx = ecx; //mov edx, ecx + edx = edx >> 0x10; //sar edx, 10h + edx ^= eax; //xor edx, eax + eax = eax >> 8; //sar eax, 8 + edx &= 0xFF; //and edx, 0FFh + int esi = IntArray[edx]; //mov esi, dword_0_10115D38[edx*4] + edx = size; //mov edx, [esp+4+arg_4] + eax &= 0x00FFFFFF; //and eax, 0FFFFFFh + eax ^= esi; //xor eax, esi + ecx = ecx >> 0x18; //sar ecx, 18h + ecx ^= eax; //xor ecx, eax + ecx &= 0xFF; //and ecx, 0FFh + esi = IntArray[ecx]; //mov esi, dword_0_10115D38[ecx*4] + ecx = (int)*buf; //mov ecx, [esp+4+arg_0] + eax = eax >> 8; //sar eax, 8 + eax &= 0x00FFFFFF; //and eax, 0FFFFFFh + eax ^= esi; //xor eax, esi + for(int x = 0; x < size; x++) + { //eax is the crc, ecx is the current part of the buffer + int edx = 0; //xor edx, edx + edx = buf[x] & 0x00FF; //mov dl, [ecx] + + edx ^= eax; //xor edx, eax + eax = eax >> 8; //sar eax, 8 + edx &= 0xFF; //and edx, 0FFh + edi = IntArray[edx]; //mov edi, dword_0_10115D38[edx*4] + eax &= 0x00FFFFFF; //and eax, 0FFFFFFh + eax ^= edi; //xor eax, edi + } + return ~eax; +} diff --git a/old/CRC16.h b/old/CRC16.h new file mode 100644 index 0000000..7aacd36 --- /dev/null +++ b/old/CRC16.h @@ -0,0 +1,25 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#ifndef _CRC16_H +#define _CRC16_H + +unsigned long CRC16(const unsigned char *buf, int size, int key); + +#endif diff --git a/old/Common_Defines.h b/old/Common_Defines.h new file mode 100644 index 0000000..4600d97 --- /dev/null +++ b/old/Common_Defines.h @@ -0,0 +1,32 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#define BASEDIR "./" + +#ifndef DB_INI_FILE + #ifdef LOGIN + #define DB_INI_FILE BASEDIR "login_db.ini" + #else + #define DB_INI_FILE BASEDIR "world_db.ini" + #endif +#endif + +#ifndef MAIN_CONFIG_FILE + #define MAIN_CONFIG_FILE BASEDIR "server_config.json" +#endif \ No newline at end of file diff --git a/old/Condition.cpp b/old/Condition.cpp new file mode 100644 index 0000000..348c90a --- /dev/null +++ b/old/Condition.cpp @@ -0,0 +1,133 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#include "debug.h" +#include "Condition.h" + +#ifdef WIN32 +#else +#include +#include +#include +#endif + +#ifdef WIN32 +/* + + Windows does not support condition variables by default. + So we use a simple hack of sleeping in wait() and doing + nothing anywhere else. + + some possible places to look for ways to do this: + http://www.cs.wustl.edu/~schmidt/win32-cv-1.html + + http://sources.redhat.com/pthreads-win32/ + http://sources.redhat.com/cgi-bin/cvsweb.cgi/pthreads/pthread_cond_signal.c?rev=1.7&content-type=text/x-cvsweb-markup&cvsroot=pthreads-win32 + +*/ + +#define CONDITION_HACK_GRANULARITY 4 + + +Condition::Condition() +{ +} + +void Condition::Signal() +{ +} + +void Condition::SignalAll() +{ +} + +void Condition::Wait() +{ + Sleep(CONDITION_HACK_GRANULARITY); +} + +Condition::~Condition() +{ +} + + +#else //!WIN32 + +Condition::Condition() +{ + pthread_cond_init(&cond,NULL); + pthread_mutex_init(&mutex,NULL); +} + +void Condition::Signal() +{ + pthread_mutex_lock(&mutex); + pthread_cond_signal(&cond); + pthread_mutex_unlock(&mutex); +} + +void Condition::SignalAll() +{ + pthread_mutex_lock(&mutex); + pthread_cond_broadcast(&cond); + pthread_mutex_unlock(&mutex); +} + +void Condition::Wait() +{ + pthread_mutex_lock(&mutex); + pthread_cond_wait(&cond,&mutex); + pthread_mutex_unlock(&mutex); +} + +/* +I commented this specifically because I think it might be very +difficult to write a windows counterpart to it, so I would like +to discourage its use until we can confirm that it can be reasonably +implemented on windows. + +bool Condition::TimedWait(unsigned long usec) +{ +struct timeval now; +struct timespec timeout; +int retcode=0; + pthread_mutex_lock(&mutex); + gettimeofday(&now,NULL); + now.tv_usec+=usec; + timeout.tv_sec = now.tv_sec + (now.tv_usec/1000000); + timeout.tv_nsec = (now.tv_usec%1000000) *1000; + //cout << "now=" << now.tv_sec << "."<. +*/ +#ifndef __CONDITION_H +#define __CONDITION_H + +#ifndef WIN32 +#include +#endif + +//Sombody, someday needs to figure out how to implement a condition +//system on windows... + + +class Condition { + private: +#ifndef WIN32 + pthread_cond_t cond; + pthread_mutex_t mutex; +#endif + public: + Condition(); + void Signal(); + void SignalAll(); + void Wait(); +// bool TimedWait(unsigned long usec); + ~Condition(); +}; + +#endif + + diff --git a/old/ConfigReader.cpp b/old/ConfigReader.cpp new file mode 100644 index 0000000..6264d59 --- /dev/null +++ b/old/ConfigReader.cpp @@ -0,0 +1,304 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#include "ConfigReader.h" +#include "Log.h" + +ConfigReader::~ConfigReader(){ + MStructs.lock(); + DestroyStructs(); + MStructs.unlock(); +} +PacketStruct* ConfigReader::getStructByVersion(const char* name, int16 version){ + PacketStruct* packet = 0; + PacketStruct* newpacket = 0; + MStructs.lock(); + vector* struct_versions = structs[string(name)]; + if(struct_versions){ + vector::iterator iter; + for(iter = struct_versions->begin(); iter != struct_versions->end(); iter++){ + packet = *iter; + if(packet && packet->GetVersion() == version){ + newpacket = new PacketStruct(packet, version); + break; + } + } + } + MStructs.unlock(); + if(!newpacket) + LogWrite(PACKET__ERROR, 0, "Packet", "Could not find struct named '%s' with version: %i", name, version); + return newpacket; +} +void ConfigReader::ReloadStructs(){ + MStructs.lock(); + DestroyStructs(); + for(int32 i=0;i*>::iterator struct_iterator; + for(struct_iterator=structs.begin();struct_iterator!=structs.end();struct_iterator++) { + vector* versions = struct_iterator->second; + vector::iterator version_iter; + if(versions){ + for(version_iter = versions->begin(); version_iter != versions->end(); version_iter++){ + safe_delete(*version_iter); + } + } + safe_delete(versions); + } + structs.clear(); +} +PacketStruct* ConfigReader::getStruct(const char* name, int16 version){ + PacketStruct* latest_version = 0; + PacketStruct* new_latest_version = 0; + MStructs.lock(); + vector* struct_versions = structs[string(name)]; + if(struct_versions){ + vector::iterator iter; + for(iter = struct_versions->begin(); iter != struct_versions->end(); iter++){ + if((*iter)->GetVersion() <= version && (!latest_version || (*iter)->GetVersion() > latest_version->GetVersion())) + latest_version = *iter; + } + if (latest_version) { + // TODO: Need to revisit this, if we have a packet struct with a version of 1, but no opcodes, we still go to the else if statement + // which results in sending corrupt data to client + if (latest_version->GetOpcode() != OP_Unknown && (latest_version->GetOpcodeValue(version) == 0xFFFF || latest_version->GetOpcodeValue(version)==0xCDCD)) { + LogWrite(PACKET__ERROR, 0, "Packet", "Could not find valid opcode for Packet Struct '%s' and client version %d", latest_version->GetName(), version); + } + else if(strlen(latest_version->GetOpcodeType()) == 0 || latest_version->GetOpcode() != OP_Unknown) + new_latest_version = new PacketStruct(latest_version, version); + } + + } + MStructs.unlock(); + if(!new_latest_version && !latest_version) + LogWrite(PACKET__ERROR, 0, "Packet", "Could not find struct named '%s' with version: %i", name, version); + return new_latest_version; +} +int16 ConfigReader::GetStructVersion(const char* name, int16 version){ + MStructs.lock(); + vector* struct_versions = structs[string(name)]; + int16 ret = 0; + if(struct_versions){ + vector::iterator iter; + PacketStruct* latest_version = 0; + for(iter = struct_versions->begin(); iter != struct_versions->end(); iter++){ + if(!latest_version || ( (*iter)->GetVersion() > latest_version->GetVersion() && (*iter)->GetVersion() <= version) ) + latest_version = *iter; + } + if(latest_version) + ret = latest_version->GetVersion(); + } + MStructs.unlock(); + return ret; +} +void ConfigReader::addStruct(const char* name, int16 version, PacketStruct* new_struct){ + string strname(name); + vector* struct_versions = structs[strname]; + if(struct_versions) + struct_versions->push_back(new_struct); + else{ + struct_versions = new vector; + struct_versions->push_back(new_struct); + structs[strname] = struct_versions; + } +} +bool ConfigReader::LoadFile(const char* name){ + load_files.push_back(name); + return processXML_Elements(name); +} +bool ConfigReader::processXML_Elements(const char* fileName){ + XMLNode xMainNode=XMLNode::openFileHelper(fileName,"EQ2Emulator"); + if(xMainNode.isEmpty()) + return false; + for(int i=0;iSetName(struct_name); + if(opcode_type) + new_struct->SetOpcodeType(opcode_type); + if(opcode_name){ + if(!new_struct->SetOpcode(opcode_name)){ + safe_delete(new_struct); + continue; + } + } + new_struct->SetVersion(version); + loadDataStruct(new_struct, xMainNode.getChildNode("Struct", i)); + addStruct(struct_name, version, new_struct); + } + return true; +} +void ConfigReader::loadDataStruct(PacketStruct* packet, XMLNode parentNode, bool array_packet){ + for(int x=0;xGetVersion()); + if(substruct_packet){ + vector::iterator itr; + vector* structs = substruct_packet->getStructs(); + DataStruct* ds = 0; + int i = 0; + char tmp[12] = {0}; + for(i=0;ibegin();itr!=structs->end();itr++) { + ds = *itr; + string new_name; + if(array_packet) + new_name = string(name).append("_").append(ds->GetStringName()); + else + new_name = string(name).append("_").append(ds->GetStringName()).append("_").append(tmp); + + DataStruct* ds2 = new DataStruct(new_name.c_str(), ds->GetType(),ds->GetLength(), ds->GetType2()); + + if(!array_packet && strlen(ds->GetArraySizeVariable()) > 1) + ds2->SetArraySizeVariable(string(name).append("_").append(ds->GetArraySizeVariable()).append("_").append(tmp).c_str()); + ds2->SetOversized(ds->GetOversized()); + ds2->SetOversizedByte(ds->GetOversizedByte()); + ds2->SetDefaultValue(ds->GetDefaultValue()); + ds2->SetMaxArraySize(ds->GetMaxArraySize()); + ds2->SetIfSetVariable(ds->GetIfSetVariable() ? ds->GetIfSetVariable() : if_variable); + ds2->SetIfNotSetVariable(ds->GetIfSetVariable() ? ds->GetIfNotSetVariable() : if_not_variable); + ds2->SetIfNotEqualsVariable(ds->GetIfNotEqualsVariable()); + ds2->SetIfFlagNotSetVariable(ds->GetIfFlagNotSetVariable()); + ds2->SetIfFlagSetVariable(ds->GetIfFlagSetVariable()); + ds2->SetIsOptional(ds->IsOptional()); + ds2->AddIfSetVariable(if_variable); //add this if the modifier is on the piece that is including the substruct + ds2->AddIfNotSetVariable(if_not_variable); //add this if the modifier is on the piece that is including the substruct + packet->add(ds2); + } + } + if(!array_packet){ + i--; + substruct_packet->renameSubstructArray(name, i); + //ds2->SetArraySizeVariable((char*)string(name).append("_").append(ds->GetArraySizeVariable()).append("_").append(tmp).c_str()); + packet->addPacketArrays(substruct_packet); + } + + safe_delete(substruct_packet); + } + continue; + } + else if(type && strncasecmp(type,"Array", 5)==0 && array_size){ + PacketStruct* new_packet = new PacketStruct; + new_packet->SetName(name); + new_packet->IsSubPacket(true); + new_packet->SetVersion(packet->GetVersion()); + loadDataStruct(new_packet, parentNode.getChildNode("Data", x), true); + packet->add(new_packet); + } + if(!name || !type) + { + LogWrite(MISC__WARNING, 0, "Misc", "Ignoring invalid Data Element, all elements must include at least an ElementName and Type!"); + LogWrite(MISC__WARNING, 0, "Misc", "\tStruct: '%s', version: %i", parentNode.getAttribute("Name"), parentNode.getAttribute("ClientVersion")); + continue; + } + DataStruct* ds = new DataStruct(name, type, num_size, type2); + int8 oversized_value = 0; + int8 oversized_byte_value = 255; + if(oversized){ + try{ + oversized_value = atoi(oversized); + } + catch(...){} + } + if(oversized_byte){ + try{ + oversized_byte_value = atoi(oversized_byte); + } + catch(...){} + } + ds->SetOversizedByte(oversized_byte_value); + ds->SetOversized(oversized_value); + ds->SetMaxArraySize(max_array_size); + if(array_size) + ds->SetArraySizeVariable(array_size); + ds->SetDefaultValue(byte_val); + ds->SetIfSetVariable(if_variable); + ds->SetIfNotSetVariable(if_not_variable); + ds->SetIfEqualsVariable(if_equals_variable); + ds->SetIfNotEqualsVariable(if_not_equals_variable); + ds->SetIfFlagNotSetVariable(if_flag_not_set_variable); + ds->SetIfFlagSetVariable(if_flag_set_variable); + if (optional && strlen(optional) > 0 && (strcmp("true", optional) == 0 || strcmp("TRUE", optional) == 0 || strcmp("True", optional) == 0)) + ds->SetIsOptional(true); + packet->add(ds); + } +} + diff --git a/old/ConfigReader.h b/old/ConfigReader.h new file mode 100644 index 0000000..cd34ae5 --- /dev/null +++ b/old/ConfigReader.h @@ -0,0 +1,52 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#ifndef __CONFIG_READER__ +#define __CONFIG_READER__ +#include +#include "PacketStruct.h" +#include +#include +#include +#include "xmlParser.h" +#include "Mutex.h" + +using namespace std; + +class ConfigReader{ +public: + ~ConfigReader(); + + void addStruct(const char* name, int16 version, PacketStruct* new_struct); + PacketStruct* getStruct(const char* name, int16 version); + PacketStruct* getStructByVersion(const char* name, int16 version); + void loadDataStruct(PacketStruct* packet, XMLNode parentNode, bool array_packet = false); + bool processXML_Elements(const char* fileName); + int16 GetStructVersion(const char* name, int16 version); + void DestroyStructs(); + void ReloadStructs(); + bool LoadFile(const char* name); +private: + Mutex MStructs; + vector load_files; + map*> structs; + //vector structs; +}; +#endif + diff --git a/old/Crypto.cpp b/old/Crypto.cpp new file mode 100644 index 0000000..369390e --- /dev/null +++ b/old/Crypto.cpp @@ -0,0 +1,47 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#include "Crypto.h" +#include +#include "../common/packet_dump.h" + +using namespace std; +void test(); +int64 Crypto::RSADecrypt(uchar* text, int16 size){ + int64 ret = 0; + uchar* buffer = new uchar[8]; + for(int i=7;i>=0;i--) + buffer[7-i] = text[i]; + memcpy(&ret, buffer, 8); + safe_delete_array(buffer); + return ret; +} + +void Crypto::RC4Decrypt(uchar* text, int32 size){ + MCrypto.lock(); + client->Cypher(text, size); + MCrypto.unlock(); +} + +void Crypto::RC4Encrypt(uchar* text, int32 size){ + MCrypto.lock(); + server->Cypher(text, size); + MCrypto.unlock(); +} + diff --git a/old/Crypto.h b/old/Crypto.h new file mode 100644 index 0000000..d2c478b --- /dev/null +++ b/old/Crypto.h @@ -0,0 +1,66 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#ifndef _CRYPTO_H +#define _CRYPTO_H +#include +#include +#include "RC4.h" +#include "../common/types.h" + +using namespace std; +class Crypto { +public: + ~Crypto(){ safe_delete(client); safe_delete(server); } + Crypto() { rc4_key = 0; encrypted = false; client = 0; server = 0; }; + + static int64 RSADecrypt(uchar* text, int16 size); + void RC4Encrypt(uchar* text, int32 size); + void RC4Decrypt(uchar* text, int32 size); + int64 getRC4Key() { return rc4_key; } + void setRC4Key(int64 key) { + rc4_key = key; + if(key > 0){ + encrypted = true; + client = new RC4(~key); + server = new RC4(key); + uchar temp[20]; + client->Cypher(temp, 20); + server->Cypher(temp, 20); + } + else{ + encrypted = false; + safe_delete(client); + safe_delete(server); + } + } + bool isEncrypted(){ return encrypted; } + void setEncrypted(bool in_val){ encrypted = in_val; } + + +private: + RC4* server; + RC4* client; + bool encrypted; + int64 rc4_key; + mutex MCrypto; +}; + +#endif + diff --git a/old/DataBuffer.h b/old/DataBuffer.h new file mode 100644 index 0000000..f40486d --- /dev/null +++ b/old/DataBuffer.h @@ -0,0 +1,207 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#ifndef __EQ2_DATABUFFER_ +#define __EQ2_DATABUFFER_ +#include +#include "../common/types.h" +#include "../common/EQPacket.h" +#include "../common/EQ2_Common_Structs.h" + +#ifdef WORLD + #include "../WorldServer/SpawnLists.h" +#endif + +using namespace std; + +class DataBuffer{ +public: + bool changed; + uchar* getData(){ return (uchar*)buffer.c_str(); } + int32 getDataSize(){ return buffer.length(); } + string* getDataString(){ return &buffer; } + void CreateEQ2Color(EQ2_Color& color){ + CreateEQ2Color(&color); + } + uchar* GetLoadBuffer(){ + return load_buffer; + } + int32 GetLoadPos(){ + return load_pos; + } + int32 GetLoadLen(){ + return load_len; + } + void SetLoadPos(int32 new_pos){ + load_pos = new_pos; + } + void CreateEQ2Color(EQ2_Color* color){ + int8 rgb[3]; + float* tmp = 0; + for(int i=0;i<3;i++){ + tmp = (float*)(load_buffer + load_pos); + rgb[i] = (int8)((*tmp)*255); + load_pos += sizeof(float); + } + color->red = rgb[0]; + color->green = rgb[1]; + color->blue = rgb[2]; + } + + template void MakeEQ2_Int8(Type& output){ + MakeEQ2_Int8(&output); + } + template void MakeEQ2_Int8(Type* output){ + float* tmp = (float*)(load_buffer + load_pos); + if(*tmp < 0) + *tmp *= -1; + sint8 result = (sint8)((*tmp)*100); + memcpy(output, &result, sizeof(sint8)); + load_pos += sizeof(float); + } + void InitializeGetData(){ + get_buffer = (uchar*)buffer.c_str(); + get_len = buffer.length(); + get_pos = 0; + } + void InitializeLoadData(uchar* input, int32 size){ + buffer = string((char*)input, size); + load_buffer = (uchar*)buffer.c_str(); + load_len = size; + load_pos = 0; + } + template void LoadDataString(String& output){ + LoadDataString(&output); + } + template void LoadDataString(String* output){ + if((sizeof(output->size) + load_pos) <= load_len){ + memcpy(&output->size, load_buffer + load_pos, sizeof(output->size)); + load_pos += sizeof(output->size); + } + if((output->size + load_pos) <= load_len){ + output->data = string((char*)(load_buffer + load_pos), output->size); + load_pos += output->size; + } + } + template void LoadData(Type& output){ + LoadData(&output); + } + template void LoadData(Type* output, int32 array_size){ + if(array_size<=1){ + LoadData(output); + } + else{ + for(int32 i=0;i void LoadData(Type* output){ + if((sizeof(Type) + load_pos) <= load_len){ + memcpy(output, load_buffer + load_pos, sizeof(Type)); + load_pos += sizeof(Type); + } + } + template void LoadData(Type& output, int32 array_size){ + LoadData(&output, array_size); + } + void LoadSkip(int8 bytes){ + load_pos += bytes; + } + template void LoadSkip(Type& skip){ + LoadSkip(&skip); + } + template void LoadSkip(Type* skip){ + load_pos += sizeof(Type); + } + template void GetData(Type* output){ + if((sizeof(Type) + get_pos) <= get_len){ + *output = (Type*)get_buffer; + get_pos += sizeof(output); + } + } + void AddZeros(int16 num){ + int8* data = new int8[num]; + memset(data, 0, num); + AddData(*data); + safe_delete_array(data); + } + template void StructAddData(Type input, int16 size, string* datastring){ + if(datastring) + datastring->append((char*)&input, size); + else + buffer.append((char*)&input, size); + } + template void StructAddData(Type input, int32 array_size, int16 size, string* datastring){ + if(array_size>0){ + for(int32 i=0;i void AddData(Type input, string* datastring = 0){ + if(!datastring) + datastring = &buffer; + datastring->append((char*)&input, sizeof(input)); + } + template void AddData(Type input, int32 array_size, string* datastring = 0){ + if(array_size>0){ + for(int32 i=0;i void AddDataString(String* input, string* datastring = 0){ + AddDataString(*input, datastring); + } + template void AddDataString(String input, string* datastring = 0){ + input.size = input.data.length(); + if(!datastring) + datastring = &buffer; + datastring->append((char*)&input.size, sizeof(input.size)); + datastring->append(input.data); + } + void AddCharArray(char* array, string* datastring = 0){ + if(!datastring) + datastring = &buffer; + datastring->append(array); + } + void AddCharArray(char* array, int16 size, string* datastring = 0){ + if(!datastring) + datastring = &buffer; + datastring->append(array, size); + } + void AddData(string data, string* datastring = 0){ + if(!datastring) + datastring = &buffer; + datastring->append(data); + } + void Clear() { buffer.clear(); } +private: + string buffer; + uchar* get_buffer; + uchar* load_buffer; + int32 get_len; + int32 get_pos; + int32 load_len; + int32 load_pos; +}; +#endif + diff --git a/old/DatabaseNew.cpp b/old/DatabaseNew.cpp new file mode 100644 index 0000000..e1e2450 --- /dev/null +++ b/old/DatabaseNew.cpp @@ -0,0 +1,422 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#include +#include +#include +#include +#include "Log.h" +#include "DatabaseNew.h" +#include + +//increase this if large queries are being run frequently to make less calls to malloc() +#define QUERY_INITIAL_SIZE 512 + +#if defined WORLD +#define DB_INI "world_db.ini" +#elif defined LOGIN +#define DB_INI "login_db.ini" +#elif defined PARSER +#define DB_INI "parser_db.ini" +#endif + +DatabaseNew::DatabaseNew() { + mysql_init(&mysql); + int timeout = 10; + mysql_options(&mysql, MYSQL_OPT_CONNECT_TIMEOUT, &timeout); + MMysql.SetName("DatabaseNew::mysql"); +} + +DatabaseNew::~DatabaseNew() { + mysql_close(&mysql); +#if MYSQL_VERSION_ID >= 50003 + mysql_library_end(); +#else + mysql_server_end(); +#endif +} + +bool DatabaseNew::Connect() { + char line[256], *key, *val; + char host[256], user[64], password[64], database[64], port[64]; + bool found_section = false; + FILE *f; + + if ((f = fopen(DB_INI, "r")) == NULL) { + LogWrite(DATABASE__ERROR, 0, "Database", "Unable to read %s\n", DB_INI); + return false; + } + + memset(host, 0, sizeof(host)); + memset(user, 0, sizeof(user)); + memset(password, 0, sizeof(password)); + memset(database, 0, sizeof(database)); + memset(port, 0, sizeof(port)); + + while (fgets(line, sizeof(line), f) != NULL) { + if (line[0] == '#' || line[0] == '\n' || line[0] == '\r') + continue; + + if (!found_section) { + if (strncasecmp(line, "[Database]", 10) == 0) + found_section = true; + } + else { + if ((key = strtok(line, "=")) != NULL) { + if ((val = strtok(NULL, "\r\n")) != NULL) { + if (strncasecmp(line, "host", 4) == 0) + strncpy(host, val, sizeof(host) - 1); + else if (strncasecmp(line, "user", 4) == 0) + strncpy(user, val, sizeof(user) - 1); + else if (strncasecmp(line, "password", 8) == 0) + strncpy(password, val, sizeof(password) - 1); + else if (strncasecmp(line, "database", 8) == 0) + strncpy(database, val, sizeof(database) - 1); + else if (strncasecmp(line, "port", 4) == 0) + strncpy(port, val, sizeof(port) - 1); + } + } + } + } + + fclose(f); + + if (host[0] == '\0') { + LogWrite(DATABASE__ERROR, 0, "Database", "Unknown 'host' in '%s'\n", DB_INI); + return false; + } + if (user[0] == '\0') { + LogWrite(DATABASE__ERROR, 0, "Database", "Unknown 'user' in '%s'\n", DB_INI); + return false; + } + if (password[0] == '\0') { + LogWrite(DATABASE__ERROR, 0, "Database", "Unknown 'password' in '%s'\n", DB_INI); + return false; + } + if (database[0] == '\0') { + LogWrite(DATABASE__ERROR, 0, "Database", "Unknown 'database' in '%s'\n", DB_INI); + return false; + } + + unsigned int portnum = atoul(port); + return Connect(host, user, password, database, portnum); +} + +bool DatabaseNew::Connect(const char *host, const char *user, const char *password, const char *database, unsigned int port) { + if (mysql_real_connect(&mysql, host, user, password, database, port, NULL, 0) == NULL) { + LogWrite(DATABASE__ERROR, 0, "Database", "Unable to connect to MySQL server at %s:%u: %s\n", host, port, mysql_error(&mysql)); + return false; + } + + return true; +} + +bool DatabaseNew::Query(const char *query, ...) { + char *buf; + size_t size = QUERY_INITIAL_SIZE; + int num_chars; + va_list args; + bool ret = true; + + MMysql.writelock(__FUNCTION__, __LINE__); + while (true) { + if ((buf = (char *)malloc(size)) == NULL) { + LogWrite(DATABASE__ERROR, 0, "Database", "Out of memory trying to allocate database query of %u bytes\n", size); + MMysql.releasewritelock(__FUNCTION__, __LINE__); + return false; + } + + va_start(args, query); + num_chars = vsnprintf(buf, size, query, args); + va_end(args); + + if (num_chars > -1 && (size_t)num_chars < size) + break; + + if (num_chars > -1) + size = num_chars + 1; + else + size *= 2; + + free(buf); + } + + if (mysql_real_query(&mysql, buf, num_chars) != 0) { + + if (mysql_errno(&mysql) == CR_SERVER_LOST || mysql_errno(&mysql) == CR_SERVER_GONE_ERROR) { + LogWrite(DATABASE__ERROR, 0, "Database", "Lost connection, attempting to recover and retry query..."); + Connect(); + + // retry attempt of previous query (1 try and we give up) + if (mysql_real_query(&mysql, buf, num_chars) != 0) { + ret = false; + } + } + else if (!IsIgnoredErrno(mysql_errno(&mysql))) { + LogWrite(DATABASE__ERROR, 0, "Database", "Error %i running MySQL query: %s\n%s\n", mysql_errno(&mysql), mysql_error(&mysql), buf); + ret = false; + } + } + free(buf); + + MMysql.releasewritelock(__FUNCTION__, __LINE__); + return ret; +} + +bool DatabaseNew::Select(DatabaseResult *result, const char *query, ...) { + char *buf; + size_t size = QUERY_INITIAL_SIZE; + int num_chars; + va_list args; + MYSQL_RES *res; + bool ret = true; + + MMysql.writelock(__FUNCTION__, __LINE__); + while (true) { + if ((buf = (char *)malloc(size)) == NULL) { + LogWrite(DATABASE__ERROR, 0, "Database", "Out of memory trying to allocate database query of %u bytes\n", size); + MMysql.releasewritelock(__FUNCTION__, __LINE__); + return false; + } + + va_start(args, query); + num_chars = vsnprintf(buf, size, query, args); + va_end(args); + + if (num_chars > -1 && (size_t)num_chars < size) + break; + + if (num_chars > -1) + size = num_chars + 1; + else + size *= 2; + + free(buf); + } + + if (mysql_real_query(&mysql, buf, (unsigned long)num_chars) != 0) { + + if (mysql_errno(&mysql) == CR_SERVER_LOST || mysql_errno(&mysql) == CR_SERVER_GONE_ERROR) { + LogWrite(DATABASE__ERROR, 0, "Database", "Lost connection, attempting to recover and retry query..."); + + mysql_close(&mysql); + Connect(); + + // retry attempt of previous query (1 try and we give up) + if (mysql_real_query(&mysql, buf, (unsigned long)num_chars) != 0) { + ret = false; + } + } + else if (!IsIgnoredErrno(mysql_errno(&mysql))) { + LogWrite(DATABASE__ERROR, 0, "Database", "Error %i running MySQL query: %s\n%s\n", mysql_errno(&mysql), mysql_error(&mysql), buf); + ret = false; + } + } + + if (ret && !IsIgnoredErrno(mysql_errno(&mysql))) { + res = mysql_store_result(&mysql); + + if (res != NULL) + { + // Grab number of rows and number of fields from the query + uint8 num_rows = mysql_affected_rows(&mysql); + uint8 num_fields = mysql_field_count(&mysql); + + ret = result->StoreResult(res, num_fields, num_rows); + } + else { + LogWrite(DATABASE__ERROR, 0, "Database", "Error storing MySql query result (%d): %s\n%s", mysql_errno(&mysql), mysql_error(&mysql), buf); + ret = false; + } + } + + free(buf); + + MMysql.releasewritelock(__FUNCTION__, __LINE__); + return ret; +} + +int32 DatabaseNew::LastInsertID() +{ + return (int32)mysql_insert_id(&mysql); +} + +long DatabaseNew::AffectedRows() +{ + return mysql_affected_rows(&mysql); +} + +char * DatabaseNew::Escape(const char *str, size_t len) { + char *buf = (char *)malloc(len * 2 + 1); + + if (buf == NULL) { + LogWrite(DATABASE__ERROR, 0, "Database", "Out of memory trying to allocate %u bytes in %s:%u\n", len * 2 + 1, __FUNCTION__, __LINE__); + return NULL; + } + + mysql_real_escape_string(&mysql, buf, str, len); + return buf; +} + +char * DatabaseNew::Escape(const char *str) { + return Escape(str, strlen(str)); +} + +string DatabaseNew::EscapeStr(const char *str, size_t len) { + char *buf = (char *)malloc(len * 2 + 1); + string ret; + + if (buf == NULL) { + LogWrite(DATABASE__ERROR, 0, "Database", "Out of memory trying to allocate %u bytes in %s:%u\n", len * 2 + 1, __FUNCTION__, __LINE__); + return NULL; + } + + mysql_real_escape_string(&mysql, buf, str, len); + ret.append(buf); + free(buf); + + return ret; +} + +string DatabaseNew::EscapeStr(const char *str) { + return EscapeStr(str, strlen(str)); +} + +string DatabaseNew::EscapeStr(string str) { + return EscapeStr(str.c_str(), str.length()); +} + +bool DatabaseNew::QueriesFromFile(const char * file) { + bool success = true; + long size; + char *buf; + int ret; + MYSQL_RES *res; + FILE *f; + + f = fopen(file, "rb"); + if (f == NULL) { + LogWrite(DATABASE__ERROR, 0, "Database", "Unable to open '%s' for reading: %s", file, strerror(errno)); + return false; + } + + fseek(f, 0, SEEK_END); + size = ftell(f); + fseek(f, 0, SEEK_SET); + + buf = (char *)malloc(size + 1); + if (buf == NULL) { + fclose(f); + LogWrite(DATABASE__ERROR, 0, "Database", "Out of memory trying to allocate %u bytes in %s:%u\n", size + 1, __FUNCTION__, __LINE__); + return false; + } + + if (fread(buf, sizeof(*buf), size, f) != (size_t)size) { + LogWrite(DATABASE__ERROR, 0, "Database", "Failed to read from '%s': %s", file, strerror(errno)); + fclose(f); + free(buf); + return false; + } + + buf[size] = '\0'; + fclose(f); + + mysql_set_server_option(&mysql, MYSQL_OPTION_MULTI_STATEMENTS_ON); + ret = mysql_real_query(&mysql, buf, size); + free(buf); + + if (ret != 0) { + LogWrite(DATABASE__ERROR, 0, "Database", "Error running MySQL queries from file '%s' (%d): %s", file, mysql_errno(&mysql), mysql_error(&mysql)); + success = false; + } + else { + //all results must be processed + do { + res = mysql_store_result(&mysql); + if (res != NULL) + mysql_free_result(res); + ret = mysql_next_result(&mysql); + + if (ret > 0) { + LogWrite(DATABASE__ERROR, 0, "Database", "Error running MySQL queries from file '%s' (%d): %s", file, mysql_errno(&mysql), mysql_error(&mysql)); + success = false; + } + + } while (ret == 0); + } + mysql_set_server_option(&mysql, MYSQL_OPTION_MULTI_STATEMENTS_OFF); + + return success; +} + +void DatabaseNew::SetIgnoredErrno(unsigned int db_errno) { + vector::iterator itr; + + for (itr = ignored_errnos.begin(); itr != ignored_errnos.end(); itr++) { + if ((*itr) == db_errno) + return; + } + + ignored_errnos.push_back(db_errno); +} + +void DatabaseNew::RemoveIgnoredErrno(unsigned int db_errno) { + vector::iterator itr; + + for (itr = ignored_errnos.begin(); itr != ignored_errnos.end(); itr++) { + if ((*itr) == db_errno) { + ignored_errnos.erase(itr); + break; + } + } +} + +bool DatabaseNew::IsIgnoredErrno(unsigned int db_errno) { + vector::iterator itr; + + for (itr = ignored_errnos.begin(); itr != ignored_errnos.end(); itr++) { + if ((*itr) == db_errno) + return true; + } + + return false; +} + +// Sends the MySQL server a keepalive +void DatabaseNew::PingNewDB() { + MMysql.writelock(__FUNCTION__, __LINE__); + mysql_ping(&mysql); + + int32* errnum = new int32; + *errnum = mysql_errno(&mysql); + + switch (*errnum) + { + case CR_COMMANDS_OUT_OF_SYNC: + case CR_SERVER_GONE_ERROR: + case CR_UNKNOWN_ERROR: + { + LogWrite(DATABASE__ERROR, 0, "Database", "[Database] We lost connection to the database., errno: %i", errno); + break; + } + } + + safe_delete(errnum); + MMysql.releasewritelock(__FUNCTION__, __LINE__); +} \ No newline at end of file diff --git a/old/DatabaseNew.h b/old/DatabaseNew.h new file mode 100644 index 0000000..2e2e002 --- /dev/null +++ b/old/DatabaseNew.h @@ -0,0 +1,48 @@ +#ifndef COMMON_DATABASE_H_ +#define COMMON_DATABASE_H_ + +#include +#include "DatabaseResult.h" + +using namespace std; + +class DatabaseNew { +public: + DatabaseNew(); + virtual ~DatabaseNew(); + + unsigned int GetError() {return mysql_errno(&mysql);} + const char * GetErrorMsg() {return mysql_error(&mysql);} + + bool Connect(); + bool Connect(const char *host, const char *user, const char *password, const char *database, unsigned int port = 3306); + + bool Query(const char *query, ...); + bool Select(DatabaseResult *result, const char *query, ...); + + int32 LastInsertID(); + long AffectedRows(); + + //these two must free() the return char* after it's used in a query + char * Escape(const char *str, size_t len); + char * Escape(const char *str); + + //does not need free() + string EscapeStr(const char *str, size_t len); + string EscapeStr(const char *str); + string EscapeStr(string str); + + bool QueriesFromFile(const char *file); + void SetIgnoredErrno(unsigned int db_errno); + void RemoveIgnoredErrno(unsigned int db_errno); + bool IsIgnoredErrno(unsigned int db_errno); + + void PingNewDB(); +private: + MYSQL mysql; + Mutex MMysql; + + vector ignored_errnos; +}; + +#endif diff --git a/old/DatabaseResult.cpp b/old/DatabaseResult.cpp new file mode 100644 index 0000000..05df1a8 --- /dev/null +++ b/old/DatabaseResult.cpp @@ -0,0 +1,234 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#include +#include +#include "Log.h" +#include "DatabaseResult.h" + +//enforced by MySQL...couldn't find a #define in their headers though +#define FIELD_NAME_MAX 64 + +//return this instead of NULL for certain functions to prevent crashes from coding errors +static const char *empty_str = ""; + +DatabaseResult::DatabaseResult(): field_map(), result(0), num_fields(0), row(0) { +} + +DatabaseResult::~DatabaseResult() { + unsigned int i; + + if (result != NULL) + mysql_free_result(result); + + if (field_map.size()) { + field_map.clear(); + } +} + +bool DatabaseResult::StoreResult(MYSQL_RES* res, uint8 field_count, uint8 row_count) { + + //clear any previously stored result + if (result != NULL) + mysql_free_result(result); + + //clear any field names from a previous result + if (field_map.size()) { + field_map.clear(); + } + + result = res; + num_rows = row_count; + num_fields = field_count; + + // No rows or fields then we don't care + if (!num_rows || !num_fields) { + mysql_free_result(res); + result = NULL; + return false; + } + + + const MYSQL_FIELD* fields = mysql_fetch_fields(result); + + for (uint8 i = 0; i < num_fields; ++i) { + field_map.emplace(std::make_pair(std::string_view(fields[i].name), i)); + } + + return true; +} + +const char * DatabaseResult::GetFieldValue(unsigned int index) { + if (index >= num_fields) { + LogWrite(DATABASE__ERROR, 0, "Database Result", "Attempt to access field at index %u but there %s only %u field%s", index, num_fields == 1 ? "is" : "are", num_fields, num_fields == 1 ? "" : "s"); + return NULL; + } + + return row[index]; +} + +const char * DatabaseResult::GetFieldValueStr(const char *field_name) { + const auto& map_iterator = field_map.find(std::string_view(field_name)); + if (map_iterator != field_map.end()) { + return row[map_iterator->second]; + } + + LogWrite(DATABASE__ERROR, 0, "Database Result", "Unknown field name '%s'", field_name); + return NULL; +} + +bool DatabaseResult::Next() { + return (result != NULL && (row = mysql_fetch_row(result)) != NULL); +} + +bool DatabaseResult::IsNull(unsigned int index) { + const char *value = GetFieldValue(index); + return value == NULL; +} + +bool DatabaseResult::IsNullStr(const char *field_name) { + const char *value = GetFieldValueStr(field_name); + return value == NULL; +} + +int8 DatabaseResult::GetInt8(unsigned int index) { + const char *value = GetFieldValue(index); + return value == NULL ? 0 : atoi(value); +} + +int8 DatabaseResult::GetInt8Str(const char *field_name) { + const char *value = GetFieldValueStr(field_name); + return value == NULL ? 0 : atoi(value); +} + +sint8 DatabaseResult::GetSInt8(unsigned int index) { + const char *value = GetFieldValue(index); + return value == NULL ? 0 : atoi(value); +} + +sint8 DatabaseResult::GetSInt8Str(const char *field_name) { + const char *value = GetFieldValueStr(field_name); + return value == NULL ? 0 : atoi(value); +} + +int16 DatabaseResult::GetInt16(unsigned int index) { + const char *value = GetFieldValue(index); + return value == NULL ? 0 : atoi(value); +} + +int16 DatabaseResult::GetInt16Str(const char *field_name) { + const char *value = GetFieldValueStr(field_name); + return value == NULL ? 0 : atoi(value); +} + +sint16 DatabaseResult::GetSInt16(unsigned int index) { + const char *value = GetFieldValue(index); + return value == NULL ? 0 : atoi(value); +} + +sint16 DatabaseResult::GetSInt16Str(const char *field_name) { + const char *value = GetFieldValueStr(field_name); + return value == NULL ? 0 : atoi(value); +} + +int32 DatabaseResult::GetInt32(unsigned int index) { + const char *value = GetFieldValue(index); + return value == NULL ? 0U : strtoul(value, NULL, 10); +} + +int32 DatabaseResult::GetInt32Str(const char *field_name) { + const char *value = GetFieldValueStr(field_name); + return value == NULL ? 0U : strtoul(value, NULL, 10); +} + +sint32 DatabaseResult::GetSInt32(unsigned int index) { + const char *value = GetFieldValue(index); + return value == NULL ? 0 : atoi(value); +} + +sint32 DatabaseResult::GetSInt32Str(const char *field_name) { + const char *value = GetFieldValueStr(field_name); + return value == NULL ? 0 : atoi(value); +} + +uint64 DatabaseResult::GetInt64(unsigned int index) { + const char *value = GetFieldValue(index); +#ifdef _WIN32 + return value == NULL ? 0UL : _strtoui64(value, NULL, 10); +#else + return value == NULL ? 0UL : strtoull(value, NULL, 10); +#endif +} + +uint64 DatabaseResult::GetInt64Str(const char *field_name) { + const char *value = GetFieldValueStr(field_name); +#ifdef _WIN32 + return value == NULL ? 0UL : _strtoui64(value, NULL, 10); +#else + return value == NULL ? 0UL : strtoull(value, NULL, 10); +#endif +} + +sint64 DatabaseResult::GetSInt64(unsigned int index) { + const char *value = GetFieldValue(index); +#ifdef _WIN32 + return value == NULL ? 0L : _strtoi64(value, NULL, 10); +#else + return value == NULL ? 0L : strtoll(value, NULL, 10); +#endif +} + +sint64 DatabaseResult::GetSInt64Str(const char *field_name) { + const char *value = GetFieldValueStr(field_name); +#ifdef _WIN32 + return value == NULL ? 0L : _strtoi64(value, NULL, 10); +#else + return value == NULL ? 0L : strtoll(value, NULL, 10); +#endif +} + +float DatabaseResult::GetFloat(unsigned int index) { + const char *value = GetFieldValue(index); + return value == NULL ? 0.0F : atof(value); +} + +float DatabaseResult::GetFloatStr(const char *field_name) { + const char *value = GetFieldValueStr(field_name); + return value == NULL ? 0.0F : atof(value); +} + +char DatabaseResult::GetChar(unsigned int index) { + const char *value = GetFieldValue(index); + return value == NULL ? '\0' : value[0]; +} + +char DatabaseResult::GetCharStr(const char *field_name) { + const char *value = GetFieldValueStr(field_name); + return value == NULL ? '\0' : value[0]; +} + +const char * DatabaseResult::GetString(unsigned int index) { + const char *value = GetFieldValue(index); + return value == NULL ? empty_str : value; +} + +const char * DatabaseResult::GetStringStr(const char *field_name) { + const char *value = GetFieldValueStr(field_name); + return value == NULL ? empty_str : value; +} diff --git a/old/DatabaseResult.h b/old/DatabaseResult.h new file mode 100644 index 0000000..36b3c3e --- /dev/null +++ b/old/DatabaseResult.h @@ -0,0 +1,57 @@ +#ifndef COMMON_DATABASERESULT_H_ +#define COMMON_DATABASERESULT_H_ + +#include "types.h" +#ifdef _WIN32 +#include //#include when we/if we go to winsock2 :/ +#endif +#include +#include + +class DatabaseResult { +public: + DatabaseResult(); + virtual ~DatabaseResult(); + + bool StoreResult(MYSQL_RES* result, uint8 field_count, uint8 row_count); + bool Next(); + + bool IsNull(unsigned int index); + bool IsNullStr(const char *field_name); + int8 GetInt8(unsigned int index); + int8 GetInt8Str(const char *field_name); + sint8 GetSInt8(unsigned int index); + sint8 GetSInt8Str(const char *field_name); + int16 GetInt16(unsigned int index); + int16 GetInt16Str(const char *field_name); + sint16 GetSInt16(unsigned int index); + sint16 GetSInt16Str(const char *field_name); + int32 GetInt32(unsigned int index); + int32 GetInt32Str(const char *field_name); + sint32 GetSInt32(unsigned int index); + sint32 GetSInt32Str(const char *field_name); + int64 GetInt64(unsigned int index); + int64 GetInt64Str(const char *field_name); + sint64 GetSInt64(unsigned int index); + sint64 GetSInt64Str(const char *field_name); + float GetFloat(unsigned int index); + float GetFloatStr(const char *field_name); + char GetChar(unsigned int index); + char GetCharStr(const char *field_name); + const char * GetString(unsigned int index); + const char * GetStringStr(const char *field_name); + + const unsigned int GetNumRows() { return num_rows; } + + const char * GetFieldValue(unsigned int index); + const char * GetFieldValueStr(const char *field_name); +private: + MYSQL_RES *result; + MYSQL_ROW row; + unsigned int num_rows; + unsigned int num_fields; + + std::map field_map; +}; + +#endif diff --git a/old/EQ2_Common_Structs.h b/old/EQ2_Common_Structs.h new file mode 100644 index 0000000..e6db286 --- /dev/null +++ b/old/EQ2_Common_Structs.h @@ -0,0 +1,361 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#ifndef _EQ2COMMON_STRUCTS_ +#define _EQ2COMMON_STRUCTS_ + +#define SPAWN_PACKET_SIZE 895 +#define EQUIPMENT_L_WEAPON_INDEX 0 //chars left hand weapon +#define EQUIPMENT_R_WEAPON_INDEX 1 //chars right hand weapon +#define EQUIPMENT_HELMET 2 + +#pragma pack(1) +struct KeyGen_Struct{ + int32 size; +}; +struct KeyGen_End_Struct{ + int32 exponent_len; + int8 exponent; +}; +struct LoginByNumRequest_Struct{ + int32 account_id; + int32 access_code; + int16 version; + int32 unknown2[5]; +}; +struct LS_LoginResponse{ + int8 reply_code; // 0 granted, 1 denied + int16 unknown01; + int8 unknown02; + sint32 unknown03; // -1 denied, 0 granted + sint32 unknown04; + sint32 unknown05; + sint32 unknown06; + int8 unknown07; + int8 unknown08; + int8 unknown09; + int8 unknown10; + sint32 unknown11; + int32 accountid; + int16 unknown12; +}; +#pragma pack() +enum EQ2_EquipmentSlot { + slot_primary=0, + slot_secondary=1, + slot_head=2, + slot_chest=3, + slot_shoulders=4, + slot_forearms=5, + slot_hands=6, + slot_legs=7, + slot_feet=8, + slot_left_ring=9, + slot_right_ring=10, + slot_ears=11, + slot_neck=12, + slot_left_wrist=13, + slot_right_wrist=14, + slot_ranged=15, + slot_ammo=16, + slot_waist=17, + slot_activate1=18, + slot_activate2=19, + slot_textures=20, + slot_hair=21, + slot_beard=22, + slot_naked_chest=23, + slot_naked_legs=24 +}; +struct EQ2_EquipmentItem{ + int16 type; + EQ2_Color color; + EQ2_Color highlight; +}; +struct EQ2_Equipment{ + int16 equip_id[25]; + EQ2_Color color[25]; + EQ2_Color highlight[25]; +}; +#pragma pack(1) +struct CharFeatures{ + int16 hair_type; + int16 hair_face_type; + int16 wing_type; + int16 chest_type; + int16 legs_type; + sint8 eye_type[3]; + sint8 ear_type[3]; + sint8 eye_brow_type[3]; + sint8 cheek_type[3]; + sint8 lip_type[3]; + sint8 chin_type[3]; + sint8 nose_type[3]; + sint8 body_size; + sint8 body_age; + sint8 soga_eye_type[3]; + sint8 soga_ear_type[3]; + sint8 soga_eye_brow_type[3]; + sint8 soga_cheek_type[3]; + int16 soga_chest_type; + int16 soga_legs_type; + sint8 soga_lip_type[3]; + sint8 soga_chin_type[3]; + sint8 soga_nose_type[3]; + sint8 soga_body_size; + sint8 soga_body_age; + int16 soga_hair_type; + int16 soga_hair_face_type; + int16 combat_voice; + int16 emote_voice; + int16 mount_model_type; + + EQ2_Color mount_saddle_color; + EQ2_Color mount_color; + EQ2_Color skin_color; + EQ2_Color eye_color; + EQ2_Color hair_type_color; + EQ2_Color hair_type_highlight_color; + EQ2_Color hair_face_color; + EQ2_Color hair_face_highlight_color; + EQ2_Color hair_highlight_color; + EQ2_Color wing_color1; + EQ2_Color wing_color2; + EQ2_Color shirt_color; + EQ2_Color pants_color; + EQ2_Color hair_color1; + EQ2_Color hair_color2; + EQ2_Color soga_skin_color; + EQ2_Color soga_eye_color; + EQ2_Color soga_hair_color1; + EQ2_Color soga_hair_color2; + EQ2_Color soga_hair_type_color; + EQ2_Color soga_hair_type_highlight_color; + EQ2_Color soga_hair_face_color; + EQ2_Color soga_hair_face_highlight_color; + EQ2_Color soga_hair_highlight_color; + + EQ2_Color model_color; + EQ2_Color soga_model_color; +}; +struct PositionData{ + int32 grid_id; + int32 bad_grid_id; + sint8 Speed1; + sint8 Speed2; + sint16 Dir1; + sint16 Dir2; + sint16 Pitch1; + sint16 Pitch2; + sint16 Roll; + float X; + float Y; + float Z; + float X2; + float Y2; + float Z2; + float X3; + float Y3; + float Z3; + float SpawnOrigX; + float SpawnOrigY; + float SpawnOrigZ; + float SpawnOrigHeading; + float SpawnOrigPitch; + float SpawnOrigRoll; + float SpeedX; + float SpeedY; + float SpeedZ; + float SideSpeed; + float VertSpeed; + float ClientHeading1; + float ClientHeading2; + float ClientPitch; + int16 collision_radius; + int16 state; +}; +struct AppearanceData { + PositionData pos; + int16 model_type; + int16 soga_model_type; + int16 activity_status; + int16 visual_state; + int16 action_state; + int16 mood_state; + int16 emote_state; + int8 attackable; + int8 icon; + int8 hide_hood; + int8 show_level; + + int8 locked_no_loot; + int8 quest_flag; + int8 heroic_flag; + int8 show_command_icon; + int8 display_hand_icon; + int8 player_flag; + int8 targetable; + int8 display_name; + char sub_title[255]; //Guild + int32 display_hp;//0 = 100 percent + int32 power_left; //bar not shown if >=100 + int8 adventure_class; + int8 tradeskill_class; + int8 level; + int8 tradeskill_level; + int8 min_level; + int8 max_level; + int8 difficulty; + int16 visible; // 02 = normal, 15 = shadow + char name[128]; //size around here somewhere + char last_name[64]; + char prefix_title[128]; + char suffix_title[128]; + int8 race; + int8 gender; + int32 randomize; + int8 lua_race_id; +}; +struct Player_Update{ +/*0000*/ int32 activity; +/*0004*/ float unknown2; // 1 +/*0008*/ float direction1; +/*0012*/ float unknown3[8]; +/*0044*/ float speed; +/*0048*/ float side_speed; +/*0052*/ float vert_speed; +/*0056*/ float orig_x; +/*0060*/ float orig_y; +/*0064*/ float orig_z; +/*0068*/ float orig_x2; +/*0072*/ float orig_y2; +/*0076*/ float orig_z2; +/*0080*/ float unknown5[3]; +/*0092*/ int32 unknown6; +/*0096*/ float unknown7[3]; +/*0108*/ int32 unknown8; +/*0112*/ int32 grid_location; +/*0116*/ float x; +/*0120*/ float y; +/*0124*/ float z; +/*0128*/ float direction2; +/*0132*/ float pitch; +/*0136*/ float unknown10; +/*0140*/ float speed_x; +/*0144*/ float speed_y; +/*0148*/ float speed_z; +}; +struct Player_Update283 { + /*0000*/ int32 activity; + /*0004*/ int32 movement_mode; // 1 + /*0008*/ float direction1; + /*0012*/ float desiredpitch; + /*0016*/ float desired_heading_speed; + /*0020*/ float desired_pitch_speed; + /*0024*/ float collision_radius; + /*0028*/ float collision_scale; + /*0032*/ float temp_scale; + /*0036*/ float speed_modifier; + /*0040*/ float swim_speed_modifier; + /*0044*/ float speed; + /*0048*/ float side_speed; + /*0052*/ float vert_speed; + /*0056*/ float orig_x; + /*0060*/ float orig_y; + /*0064*/ float orig_z; + /*0068*/ float orig_x2; + /*0072*/ float orig_y2; + /*0076*/ float orig_z2; + /*0080*/ int32 face_actor_id; + /*0084*/ int32 face_actor_range; + /*0088*/ int32 grid_location; + /*0092*/ float x; + /*0096*/ float y; + /*0100*/ float z; + /*0104*/ float direction2; + /*0108*/ float pitch; + /*0112*/ float roll; + /*0116*/ float speed_x; + /*0120*/ float speed_y; + /*0124*/ float speed_z; +};//0128 +struct Player_Update1096{ +/*0000*/ int32 activity; +/*0004*/ float unknown2; // 1 +/*0008*/ float direction1; +/*0012*/ float unknown3[8]; +/*0044*/ float unk_speed; +/*0048*/ float speed; +/*0052*/ float side_speed; +/*0056*/ float vert_speed; +/*0060*/ float orig_x; +/*0064*/ float orig_y; +/*0068*/ float orig_z; +/*0072*/ float orig_x2; +/*0076*/ float orig_y2; +/*0080*/ float orig_z2; +/*0092*/ float unknown5[3]; +/*0096*/ int32 unknown6; +/*0108*/ float unknown7[3]; +/*0112*/ int32 unknown8; +/*0116*/ int32 grid_location; +/*0120*/ float x; +/*0124*/ float y; +/*0128*/ float z; +/*0132*/ float direction2; +/*0136*/ float pitch; +/*0140*/ float unknown10; +/*0144*/ float speed_x; +/*0148*/ float speed_y; +/*0152*/ float speed_z; +}; + +struct Player_Update1144{ +/*0000*/ int32 activity; +/*0004*/ float unknown2; // 1 +/*0008*/ float direction1; +/*0012*/ float unknown3[12]; +/*0044*/ float unk_speed; +/*0048*/ float speed; +/*0052*/ float side_speed; +/*0056*/ float vert_speed; +/*0060*/ float orig_x; +/*0064*/ float orig_y; +/*0068*/ float orig_z; +/*0072*/ float orig_x2; +/*0076*/ float orig_y2; +/*0080*/ float orig_z2; +/*0092*/ float unknown5[3]; +/*0096*/ int32 unknown6; +/*0108*/ float unknown7[3]; +/*0112*/ int32 unknown8; +/*0116*/ int32 grid_location; +/*0120*/ float x; +/*0124*/ float y; +/*0128*/ float z; +/*0132*/ float direction2; +/*0136*/ float pitch; +/*0140*/ float unknown10; +/*0144*/ float speed_x; +/*0148*/ float speed_y; +/*0152*/ float speed_z; +}; +#pragma pack() +#endif + diff --git a/old/EQEMuError.cpp b/old/EQEMuError.cpp new file mode 100644 index 0000000..323463a --- /dev/null +++ b/old/EQEMuError.cpp @@ -0,0 +1,131 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#ifdef WIN32 +#include +#include +#endif +#include "EQEMuError.h" +#include "linked_list.h" +#include "Mutex.h" +#include "MiscFunctions.h" +#include +#include +#ifdef WIN32 + #include +#endif + +void CatchSignal(int sig_num); + +const char* EQEMuErrorText[EQEMuError_MaxErrorID] = { "ErrorID# 0, No Error", + "MySQL Error #1405 or #2001 means your mysql server rejected the username and password you presented it.", + "MySQL Error #2003 means you were unable to connect to the mysql server.", + "MySQL Error #2005 means you there are too many connections on the mysql server. The server is overloaded.", + "MySQL Error #2007 means you the server is out of memory. The server is overloaded.", + }; + +LinkedList* EQEMuErrorList; +Mutex* MEQEMuErrorList; +AutoDelete< LinkedList > ADEQEMuErrorList(&EQEMuErrorList); +AutoDelete ADMEQEMuErrorList(&MEQEMuErrorList); + +const char* GetErrorText(int32 iError) { + if (iError >= EQEMuError_MaxErrorID) + return "ErrorID# out of range"; + else + return EQEMuErrorText[iError]; +} + +void AddEQEMuError(eEQEMuError iError, bool iExitNow) { + if (!iError) + return; + if (!EQEMuErrorList) { + EQEMuErrorList = new LinkedList; + MEQEMuErrorList = new Mutex; + } + LockMutex lock(MEQEMuErrorList); + + LinkedListIterator iterator(*EQEMuErrorList); + iterator.Reset(); + while (iterator.MoreElements()) { + if (iterator.GetData()[0] == 1) { + if (*((eEQEMuError*) &(iterator.GetData()[1])) == iError) + return; + } + iterator.Advance(); + } + + char* tmp = new char[6]; + tmp[0] = 1; + tmp[5] = 0; + *((int32*) &tmp[1]) = iError; + EQEMuErrorList->Append(tmp); + + if (iExitNow) + CatchSignal(2); +} + +void AddEQEMuError(char* iError, bool iExitNow) { + if (!iError) + return; + if (!EQEMuErrorList) { + EQEMuErrorList = new LinkedList; + MEQEMuErrorList = new Mutex; + } + LockMutex lock(MEQEMuErrorList); + char* tmp = strcpy(new char[strlen(iError) + 1], iError); + EQEMuErrorList->Append(tmp); + + if (iExitNow) + CatchSignal(2); +} + +int32 CheckEQEMuError() { + if (!EQEMuErrorList) + return 0; + int32 ret = 0; + char* tmp = 0; + bool HeaderPrinted = false; + LockMutex lock(MEQEMuErrorList); + + while ((tmp = EQEMuErrorList->Pop() )) { + if (!HeaderPrinted) { + fprintf(stdout, "===============================\nRuntime errors:\n\n"); + HeaderPrinted = true; + } + if (tmp[0] == 1) { + fprintf(stdout, "%s\n", GetErrorText(*((int32*) &tmp[1]))); + } + else { + fprintf(stdout, "%s\n\n", tmp); + } + safe_delete(tmp); + ret++; + } + return ret; +} + +void CheckEQEMuErrorAndPause() { + if (CheckEQEMuError()) { + fprintf(stdout, "Hit any key to exit\n"); + getchar(); + } +} + + diff --git a/old/EQEMuError.h b/old/EQEMuError.h new file mode 100644 index 0000000..03b364c --- /dev/null +++ b/old/EQEMuError.h @@ -0,0 +1,39 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#ifndef EQEMuError_H +#define EQEMuError_H + +#include "../common/types.h" + +enum eEQEMuError { EQEMuError_NoError, + EQEMuError_Mysql_1405, + EQEMuError_Mysql_2003, + EQEMuError_Mysql_2005, + EQEMuError_Mysql_2007, + EQEMuError_MaxErrorID }; + +void AddEQEMuError(eEQEMuError iError, bool iExitNow = false); +void AddEQEMuError(char* iError, bool iExitNow = false); +int32 CheckEQEMuError(); +void CheckEQEMuErrorAndPause(); + +#endif + + diff --git a/old/EQPacket.cpp b/old/EQPacket.cpp new file mode 100644 index 0000000..c2f9e1f --- /dev/null +++ b/old/EQPacket.cpp @@ -0,0 +1,661 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#include "debug.h" +#include +#include +#include +#include +#include "EQPacket.h" +#include "misc.h" +#include "op_codes.h" +#include "CRC16.h" +#include "opcodemgr.h" +#include "packet_dump.h" +#include +#include "Log.h" +#include + +using namespace std; +extern mapEQOpcodeManager; + +uint8 EQApplicationPacket::default_opcode_size=2; + +EQPacket::EQPacket(const uint16 op, const unsigned char *buf, uint32 len) +{ + this->opcode=op; + this->pBuffer=NULL; + this->size=0; + version = 0; + setTimeInfo(0,0); + if (len>0) { + this->size=len; + pBuffer= new unsigned char[this->size]; + if (buf) { + memcpy(this->pBuffer,buf,this->size); + } else { + memset(this->pBuffer,0,this->size); + } + } +} + +const char* EQ2Packet::GetOpcodeName() { + int16 OpcodeVersion = GetOpcodeVersion(version); + if (EQOpcodeManager.count(OpcodeVersion) > 0) + return EQOpcodeManager[OpcodeVersion]->EmuToName(login_op); + else + return NULL; +} + +int8 EQ2Packet::PreparePacket(int16 MaxLen) { + int16 OpcodeVersion = GetOpcodeVersion(version); + + // stops a crash for incorrect version + if (EQOpcodeManager.count(OpcodeVersion) == 0) + { + LogWrite(PACKET__ERROR, 0, "Packet", "Version %i is not listed in the opcodes table.", version); + return -1; + } + + packet_prepared = true; + + int16 login_opcode = EQOpcodeManager[OpcodeVersion]->EmuToEQ(login_op); + if (login_opcode == 0xcdcd) + { + LogWrite(PACKET__ERROR, 0, "Packet", "Version %i is not listed in the opcodes table for opcode %s", version, EQOpcodeManager[OpcodeVersion]->EmuToName(login_op)); + return -1; + } + + int16 orig_opcode = login_opcode; + int8 offset = 0; + //one of the int16s is for the seq, other is for the EQ2 opcode and compressed flag (OP_Packet is the header, not the opcode) + int32 new_size = size + sizeof(int16) + sizeof(int8); + bool oversized = false; + if (login_opcode != 2) { + new_size += sizeof(int8); //for opcode + if (login_opcode >= 255) { + new_size += sizeof(int16); + oversized = true; + } + else + login_opcode = ntohs(login_opcode); + } + uchar* new_buffer = new uchar[new_size]; + memset(new_buffer, 0, new_size); + uchar* ptr = new_buffer + sizeof(int16); // sequence is first + if (login_opcode != 2) { + if (oversized) { + ptr += sizeof(int8); //compressed flag + int8 addon = 0xff; + memcpy(ptr, &addon, sizeof(int8)); + ptr += sizeof(int8); + } + memcpy(ptr, &login_opcode, sizeof(int16)); + ptr += sizeof(int16); + } + else { + memcpy(ptr, &login_opcode, sizeof(int8)); + ptr += sizeof(int8); + } + memcpy(ptr, pBuffer, size); + + safe_delete_array(pBuffer); + pBuffer = new_buffer; + offset = new_size - size - 1; + size = new_size; + + return offset; +} + +uint32 EQProtocolPacket::serialize(unsigned char *dest, int8 offset) const +{ + if (opcode>0xff) { + *(uint16 *)dest=opcode; + } else { + *(dest)=0; + *(dest+1)=opcode; + } + memcpy(dest+2,pBuffer+offset,size-offset); + + return size+2; +} + +uint32 EQApplicationPacket::serialize(unsigned char *dest) const +{ + uint8 OpCodeBytes = app_opcode_size; + + if (app_opcode_size==1) + *(unsigned char *)dest=opcode; + else + { + // Application opcodes with a low order byte of 0x00 require an extra 0x00 byte inserting prior to the opcode. + if ((opcode & 0x00ff) == 0) + { + *(uint8*)dest = 0; + *(uint16*)(dest + 1) = opcode; + ++OpCodeBytes; + } + else + *(uint16*)dest = opcode; + } + + memcpy(dest+app_opcode_size,pBuffer,size); + + return size+ OpCodeBytes; +} + +EQPacket::~EQPacket() +{ + safe_delete_array(pBuffer); + pBuffer=NULL; +} + + +void EQPacket::DumpRawHeader(uint16 seq, FILE* to) const +{ + /*if (timestamp.tv_sec) { + char temp[20]; + tm t; + const time_t sec = timestamp.tv_sec; + localtime_s(&t, &sec); + strftime(temp, 20, "%F %T", &t); + fprintf(to, "%s.%06lu ", temp, timestamp.tv_usec); + }*/ + + DumpRawHeaderNoTime(seq, to); +} + +const char* EQPacket::GetOpcodeName(){ + int16 OpcodeVersion = GetOpcodeVersion(version); + if(EQOpcodeManager.count(OpcodeVersion) > 0) + return EQOpcodeManager[OpcodeVersion]->EQToName(opcode); + else + return NULL; +} +void EQPacket::DumpRawHeaderNoTime(uint16 seq, FILE *to) const +{ + if (src_ip) { + string sIP,dIP;; + sIP=long2ip(src_ip); + dIP=long2ip(dst_ip); + fprintf(to, "[%s:%d->%s:%d] ",sIP.c_str(),src_port,dIP.c_str(),dst_port); + } + if (seq != 0xffff) + fprintf(to, "[Seq=%u] ",seq); + + string name; + int16 OpcodeVersion = GetOpcodeVersion(version); + if(EQOpcodeManager.count(OpcodeVersion) > 0) + name = EQOpcodeManager[OpcodeVersion]->EQToName(opcode); + + fprintf(to, "[OpCode 0x%04x (%s) Size=%u]\n",opcode,name.c_str(),size); +} + +void EQPacket::DumpRaw(FILE *to) const +{ + DumpRawHeader(); + if (pBuffer && size) + dump_message_column(pBuffer, size, " ", to); + fprintf(to, "\n"); +} + +EQProtocolPacket::EQProtocolPacket(const unsigned char *buf, uint32 len, int in_opcode) +{ + uint32 offset = 0; + if(in_opcode>=0) { + opcode = in_opcode; + } + else { + // Ensure there are at least 2 bytes for the opcode + if (len < 2 || buf == nullptr) { + // Not enough data to read opcode; set defaults or handle error appropriately + opcode = 0; // or set to a designated invalid opcode + offset = len; // no payload available + } else { + offset = 2; + opcode = ntohs(*(const uint16 *)buf); + } + } + + // Check that there is payload data after the header + if (len > offset) { + size = len - offset; + pBuffer = new unsigned char[size]; + if(buf) + memcpy(pBuffer, buf + offset, size); + else + memset(pBuffer, 0, size); + } else { + pBuffer = nullptr; + size = 0; + } + + version = 0; + eq2_compressed = false; + packet_prepared = false; + packet_encrypted = false; + sent_time = 0; + attempt_count = 0; + sequence = 0; +} + +bool EQ2Packet::AppCombine(EQ2Packet* rhs){ + bool result = false; + uchar* tmpbuffer = 0; + bool over_sized_packet = false; + int32 new_size = 0; + //bool whee = false; +// DumpPacket(this); +// DumpPacket(rhs); + /*if(rhs->size >= 255){ + DumpPacket(this); + DumpPacket(rhs); + whee = true; + }*/ + if (opcode==OP_AppCombined && ((size + rhs->size + 3) < 255)){ + int16 tmp_size = rhs->size - 2; + if(tmp_size >= 255){ + new_size = size+tmp_size+3; + over_sized_packet = true; + } + else + new_size = size+tmp_size+1; + tmpbuffer = new uchar[new_size]; + uchar* ptr = tmpbuffer; + memcpy(ptr, pBuffer, size); + ptr += size; + if(over_sized_packet){ + memset(ptr, 255, sizeof(int8)); + ptr += sizeof(int8); + tmp_size = htons(tmp_size); + memcpy(ptr, &tmp_size, sizeof(int16)); + ptr += sizeof(int16); + } + else{ + memcpy(ptr, &tmp_size, sizeof(int8)); + ptr += sizeof(int8); + } + memcpy(ptr, rhs->pBuffer+2, rhs->size-2); + delete[] pBuffer; + size = new_size; + pBuffer=tmpbuffer; + safe_delete(rhs); + result=true; + } + else if (rhs->size > 2 && size > 2 && (size + rhs->size + 6) < 255) { + int32 tmp_size = size - 2; + int32 tmp_size2 = rhs->size - 2; + opcode=OP_AppCombined; + bool over_sized_packet2 = false; + new_size = size; + if(tmp_size >= 255){ + new_size += 5; + over_sized_packet = true; + } + else + new_size += 3; + if(tmp_size2 >= 255){ + new_size += tmp_size2+3; + over_sized_packet2 = true; + } + else + new_size += tmp_size2+1; + tmpbuffer = new uchar[new_size]; + tmpbuffer[2]=0; + tmpbuffer[3]=0x19; + uchar* ptr = tmpbuffer+4; + if(over_sized_packet){ + memset(ptr, 255, sizeof(int8)); + ptr += sizeof(int8); + tmp_size = htons(tmp_size); + memcpy(ptr, &tmp_size, sizeof(int16)); + ptr += sizeof(int16); + } + else{ + memcpy(ptr, &tmp_size, sizeof(int8)); + ptr += sizeof(int8); + } + memcpy(ptr, pBuffer+2, size-2); + ptr += (size-2); + if(over_sized_packet2){ + memset(ptr, 255, sizeof(int8)); + ptr += sizeof(int8); + tmp_size2 = htons(tmp_size2); + memcpy(ptr, &tmp_size2, sizeof(int16)); + ptr += sizeof(int16); + } + else{ + memcpy(ptr, &tmp_size2, sizeof(int8)); + ptr += sizeof(int8); + } + memcpy(ptr, rhs->pBuffer+2, rhs->size-2); + size = new_size; + delete[] pBuffer; + pBuffer=tmpbuffer; + safe_delete(rhs); + result=true; + } + /*if(whee){ + DumpPacket(this); + cout << "fsdfsdf"; + }*/ + //DumpPacket(this); + return result; +} + +bool EQProtocolPacket::combine(const EQProtocolPacket *rhs) +{ + bool result=false; + //if(dont_combine) + // return false; + //if (opcode==OP_Combined && size+rhs->size+5<256) { + if (opcode == OP_Combined && size + rhs->size + 5 < 256) { + auto tmpbuffer = new unsigned char[size + rhs->size + 3]; + memcpy(tmpbuffer, pBuffer, size); + uint32 offset = size; + tmpbuffer[offset++] = rhs->Size(); + offset += rhs->serialize(tmpbuffer + offset); + size = offset; + delete[] pBuffer; + pBuffer = tmpbuffer; + result = true; + } + else if (size + rhs->size + 7 < 256) { + auto tmpbuffer = new unsigned char[size + rhs->size + 6]; + uint32 offset = 0; + tmpbuffer[offset++] = Size(); + offset += serialize(tmpbuffer + offset); + tmpbuffer[offset++] = rhs->Size(); + offset += rhs->serialize(tmpbuffer + offset); + size = offset; + delete[] pBuffer; + pBuffer = tmpbuffer; + opcode = OP_Combined; + result = true; + } + return result; +} + +EQApplicationPacket::EQApplicationPacket(const unsigned char *buf, uint32 len, uint8 opcode_size) +{ +uint32 offset=0; + app_opcode_size=(opcode_size==0) ? EQApplicationPacket::default_opcode_size : opcode_size; + + if (app_opcode_size==1) { + opcode=*(const unsigned char *)buf; + offset++; + } else { + opcode=*(const uint16 *)buf; + offset+=2; + } + + if ((len-offset)>0) { + pBuffer=new unsigned char[len-offset]; + memcpy(pBuffer,buf+offset,len-offset); + size=len-offset; + } else { + pBuffer=NULL; + size=0; + } + + emu_opcode = OP_Unknown; +} + +bool EQApplicationPacket::combine(const EQApplicationPacket *rhs) +{ +cout << "CALLED AP COMBINE!!!!\n"; + return false; +} + +void EQApplicationPacket::SetOpcode(EmuOpcode emu_op) { + if(emu_op == OP_Unknown) { + opcode = 0; + emu_opcode = OP_Unknown; + return; + } + + opcode = EQOpcodeManager[GetOpcodeVersion(version)]->EmuToEQ(emu_op); + + if(opcode == OP_Unknown) { + LogWrite(PACKET__DEBUG, 0, "Packet", "Unable to convert Emu opcode %s (%d) into an EQ opcode.", OpcodeNames[emu_op], emu_op); + } + + //save the emu opcode we just set. + emu_opcode = emu_op; +} + +const EmuOpcode EQApplicationPacket::GetOpcodeConst() const { + if(emu_opcode != OP_Unknown) { + return(emu_opcode); + } + if(opcode == 10000) { + return(OP_Unknown); + } + + EmuOpcode emu_op; + emu_op = EQOpcodeManager[GetOpcodeVersion(version)]->EQToEmu(opcode); + if(emu_op == OP_Unknown) { + LogWrite(PACKET__DEBUG, 1, "Packet", "Unable to convert EQ opcode 0x%.4X (%i) to an emu opcode (%s)", opcode, opcode, __FUNCTION__); + } + + return(emu_op); +} + +EQApplicationPacket *EQProtocolPacket::MakeApplicationPacket(uint8 opcode_size) const { + EQApplicationPacket *res = new EQApplicationPacket; + res->app_opcode_size=(opcode_size==0) ? EQApplicationPacket::default_opcode_size : opcode_size; + if (res->app_opcode_size==1) { + res->pBuffer= new unsigned char[size+1]; + memcpy(res->pBuffer+1,pBuffer,size); + *(res->pBuffer)=htons(opcode)&0xff; + res->opcode=opcode&0xff; + res->size=size+1; + } else { + res->pBuffer= new unsigned char[size]; + memcpy(res->pBuffer,pBuffer,size); + res->opcode=opcode; + res->size=size; + } + res->copyInfo(this); + return(res); +} +bool EQProtocolPacket::ValidateCRC(const unsigned char *buffer, int length, uint32 Key) +{ +bool valid=false; + // OP_SessionRequest, OP_SessionResponse, OP_OutOfSession are not CRC'd + if (buffer[0]==0x00 && (buffer[1]==OP_SessionRequest || buffer[1]==OP_SessionResponse || buffer[1]==OP_OutOfSession)) { + valid=true; + } else if(buffer[2] == 0x00 && buffer[3] == 0x19){ + valid = true; + } + else { + uint16 comp_crc=CRC16(buffer,length-2,Key); + uint16 packet_crc=ntohs(*(const uint16 *)(buffer+length-2)); +#ifdef EQN_DEBUG + if (packet_crc && comp_crc != packet_crc) { + cout << "CRC mismatch: comp=" << hex << comp_crc << ", packet=" << packet_crc << dec << endl; + } +#endif + valid = (!packet_crc || comp_crc == packet_crc); + } + return valid; +} + +uint32 EQProtocolPacket::Decompress(const unsigned char *buffer, const uint32 length, unsigned char *newbuf, uint32 newbufsize) +{ +uint32 newlen=0; +uint32 flag_offset=0; + newbuf[0]=buffer[0]; + if (buffer[0]==0x00) { + flag_offset=2; + newbuf[1]=buffer[1]; + } else + flag_offset=1; + + if (length>2 && buffer[flag_offset]==0x5a) { + LogWrite(PACKET__DEBUG, 0, "Packet", "In Decompress 1"); + newlen=Inflate(const_cast(buffer+flag_offset+1),length-(flag_offset+1)-2,newbuf+flag_offset,newbufsize-flag_offset)+2; + + // something went bad with zlib + if (newlen == -1) + { + LogWrite(PACKET__ERROR, 0, "Packet", "Debug Bad Inflate!"); + DumpPacket(buffer, length); + memcpy(newbuf, buffer, length); + return length; + } + + newbuf[newlen++]=buffer[length-2]; + newbuf[newlen++]=buffer[length-1]; + } else if (length>2 && buffer[flag_offset]==0xa5) { + LogWrite(PACKET__DEBUG, 0, "Packet", "In Decompress 2"); + memcpy(newbuf+flag_offset,buffer+flag_offset+1,length-(flag_offset+1)); + newlen=length-1; + } else { + memcpy(newbuf,buffer,length); + newlen=length; + } + + return newlen; +} + +uint32 EQProtocolPacket::Compress(const unsigned char *buffer, const uint32 length, unsigned char *newbuf, uint32 newbufsize) { +uint32 flag_offset=1,newlength; + //dump_message_column(buffer,length,"Before: "); + newbuf[0]=buffer[0]; + if (buffer[0]==0) { + flag_offset=2; + newbuf[1]=buffer[1]; + } + if (length>30) { + newlength=Deflate(const_cast(buffer+flag_offset),length-flag_offset,newbuf+flag_offset+1,newbufsize); + *(newbuf+flag_offset)=0x5a; + newlength+=flag_offset+1; + } else { + memmove(newbuf+flag_offset+1,buffer+flag_offset,length-flag_offset); + *(newbuf+flag_offset)=0xa5; + newlength=length+1; + } + //dump_message_column(newbuf,length,"After: "); + + return newlength; +} + +void EQProtocolPacket::ChatDecode(unsigned char *buffer, int size, int DecodeKey) +{ + if (buffer[1]!=0x01 && buffer[0]!=0x02 && buffer[0]!=0x1d) { + int Key=DecodeKey; + unsigned char *test=(unsigned char *)malloc(size); + buffer+=2; + size-=2; + + int i; + for (i = 0 ; i+4 <= size ; i+=4) + { + int pt = (*(int*)&buffer[i])^(Key); + Key = (*(int*)&buffer[i]); + *(int*)&test[i]=pt; + } + unsigned char KC=Key&0xFF; + for ( ; i < size ; i++) + { + test[i]=buffer[i]^KC; + } + memcpy(buffer,test,size); + free(test); + } +} + +void EQProtocolPacket::ChatEncode(unsigned char *buffer, int size, int EncodeKey) +{ + if (buffer[1]!=0x01 && buffer[0]!=0x02 && buffer[0]!=0x1d) { + int Key=EncodeKey; + char *test=(char*)malloc(size); + int i; + buffer+=2; + size-=2; + for ( i = 0 ; i+4 <= size ; i+=4) + { + int pt = (*(int*)&buffer[i])^(Key); + Key = pt; + *(int*)&test[i]=pt; + } + unsigned char KC=Key&0xFF; + for ( ; i < size ; i++) + { + test[i]=buffer[i]^KC; + } + memcpy(buffer,test,size); + free(test); + } +} + +bool EQProtocolPacket::IsProtocolPacket(const unsigned char* in_buff, uint32_t len, bool bTrimCRC) { + bool ret = false; + uint16_t opcode = ntohs(*(uint16_t*)in_buff); + uint32_t offset = 2; + + switch (opcode) { + case OP_SessionRequest: + case OP_SessionDisconnect: + case OP_KeepAlive: + case OP_SessionStatResponse: + case OP_Packet: + case OP_Combined: + case OP_Fragment: + case OP_Ack: + case OP_OutOfOrderAck: + case OP_OutOfSession: + { + ret = true; + break; + } + } + + return ret; +} + + + +void DumpPacketHex(const EQApplicationPacket* app) +{ + DumpPacketHex(app->pBuffer, app->size); +} + +void DumpPacketAscii(const EQApplicationPacket* app) +{ + DumpPacketAscii(app->pBuffer, app->size); +} +void DumpPacket(const EQProtocolPacket* app) { + DumpPacketHex(app->pBuffer, app->size); +} +void DumpPacket(const EQApplicationPacket* app, bool iShowInfo) { + if (iShowInfo) { + cout << "Dumping Applayer: 0x" << hex << setfill('0') << setw(4) << app->GetOpcode() << dec; + cout << " size:" << app->size << endl; + } + DumpPacketHex(app->pBuffer, app->size); +// DumpPacketAscii(app->pBuffer, app->size); +} + +void DumpPacketBin(const EQApplicationPacket* app) { + DumpPacketBin(app->pBuffer, app->size); +} + + diff --git a/old/EQPacket.h b/old/EQPacket.h new file mode 100644 index 0000000..455a72c --- /dev/null +++ b/old/EQPacket.h @@ -0,0 +1,209 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#ifndef _EQPACKET_H +#define _EQPACKET_H + +#include "types.h" +#include +#include + +#ifdef WIN32 + #include + #include +#else + #include + #include +#endif + +#include "emu_opcodes.h" +#include "op_codes.h" +#include "packet_dump.h" + +class OpcodeManager; + +class EQStream; + +class EQPacket { + friend class EQStream; +public: + unsigned char *pBuffer; + uint32 size; + uint32 src_ip,dst_ip; + uint16 src_port,dst_port; + uint32 priority; + timeval timestamp; + int16 version; + ~EQPacket(); + void DumpRawHeader(uint16 seq=0xffff, FILE *to = stdout) const; + void DumpRawHeaderNoTime(uint16 seq=0xffff, FILE *to = stdout) const; + void DumpRaw(FILE *to = stdout) const; + const char* GetOpcodeName(); + + void setVersion(int16 new_version){ version = new_version; } + void setSrcInfo(uint32 sip, uint16 sport) { src_ip=sip; src_port=sport; } + void setDstInfo(uint32 dip, uint16 dport) { dst_ip=dip; dst_port=dport; } + void setTimeInfo(uint32 ts_sec, uint32 ts_usec) { timestamp.tv_sec=ts_sec; timestamp.tv_usec=ts_usec; } + void copyInfo(const EQPacket *p) { src_ip=p->src_ip; src_port=p->src_port; dst_ip=p->dst_ip; dst_port=p->dst_port; timestamp.tv_sec=p->timestamp.tv_sec; timestamp.tv_usec=p->timestamp.tv_usec; } + uint32 Size() const { return size+2; } + +//no reason to have this method in zone or world + + uint16 GetRawOpcode() const { return(opcode); } + + + inline bool operator<(const EQPacket &rhs) { + return (timestamp.tv_sec < rhs.timestamp.tv_sec || (timestamp.tv_sec==rhs.timestamp.tv_sec && timestamp.tv_usec < rhs.timestamp.tv_usec)); + } + void SetProtocolOpcode(int16 new_opcode){ + opcode = new_opcode; + } + +protected: + uint16 opcode; + + EQPacket(const uint16 op, const unsigned char *buf, const uint32 len); + EQPacket(const EQPacket &p) { version = 0; } + EQPacket() { opcode=0; pBuffer=NULL; size=0; version = 0; setTimeInfo(0, 0); } + +}; + +class EQApplicationPacket; + +class EQProtocolPacket : public EQPacket { +public: + EQProtocolPacket(uint16 op, const unsigned char *buf, uint32 len) : EQPacket(op,buf,len) { + eq2_compressed = false; + packet_prepared = false; + packet_encrypted = false; + sequence = 0; + sent_time = 0; + attempt_count = 0; + acked = false; + } + EQProtocolPacket(const unsigned char *buf, uint32 len, int in_opcode = -1); + bool combine(const EQProtocolPacket *rhs); + uint32 serialize (unsigned char *dest, int8 offset = 0) const; + static bool ValidateCRC(const unsigned char *buffer, int length, uint32 Key); + static uint32 Decompress(const unsigned char *buffer, const uint32 length, unsigned char *newbuf, uint32 newbufsize); + static uint32 Compress(const unsigned char *buffer, const uint32 length, unsigned char *newbuf, uint32 newbufsize); + static void ChatDecode(unsigned char *buffer, int size, int DecodeKey); + static void ChatEncode(unsigned char *buffer, int size, int EncodeKey); + static bool IsProtocolPacket(const unsigned char* in_buff, uint32_t len, bool bTrimCRC); + + EQProtocolPacket *Copy() { + EQProtocolPacket* new_packet = new EQProtocolPacket(opcode,pBuffer,size); + new_packet->eq2_compressed = this->eq2_compressed; + new_packet->packet_prepared = this->packet_prepared; + new_packet->packet_encrypted = this->packet_encrypted; + return new_packet; + } + EQApplicationPacket *MakeApplicationPacket(uint8 opcode_size=0) const; + bool eq2_compressed; + bool packet_prepared; + bool packet_encrypted; + bool acked; + int32 sent_time; + int8 attempt_count; + int32 sequence; + +private: + EQProtocolPacket(const EQProtocolPacket &p) { } + //bool dont_combine; +}; +class EQ2Packet : public EQProtocolPacket { +public: + EQ2Packet(const EmuOpcode in_login_op, const unsigned char *buf, uint32 len) : EQProtocolPacket(OP_Packet,buf,len){ + login_op = in_login_op; + eq2_compressed = false; + packet_prepared = false; + packet_encrypted = false; + } + bool AppCombine(EQ2Packet* rhs); + EQ2Packet* Copy() { + EQ2Packet* new_packet = new EQ2Packet(login_op,pBuffer,size); + new_packet->eq2_compressed = this->eq2_compressed; + new_packet->packet_prepared = this->packet_prepared; + new_packet->packet_encrypted = this->packet_encrypted; + return new_packet; + } + int8 PreparePacket(int16 MaxLen); + const char* GetOpcodeName(); + EmuOpcode login_op; +}; +class EQApplicationPacket : public EQPacket { + friend class EQProtocolPacket; + friend class EQStream; +public: + EQApplicationPacket() : EQPacket(0,NULL,0) { emu_opcode = OP_Unknown; app_opcode_size=default_opcode_size; } + EQApplicationPacket(const EmuOpcode op) : EQPacket(0,NULL,0) { SetOpcode(op); app_opcode_size=default_opcode_size; } + EQApplicationPacket(const EmuOpcode op, const uint32 len) : EQPacket(0,NULL,len) { SetOpcode(op); app_opcode_size=default_opcode_size; } + EQApplicationPacket(const EmuOpcode op, const unsigned char *buf, const uint32 len) : EQPacket(0,buf,len) { SetOpcode(op); app_opcode_size=default_opcode_size; } + bool combine(const EQApplicationPacket *rhs); + uint32 serialize (unsigned char *dest) const; + uint32 Size() const { return size+app_opcode_size; } + EQApplicationPacket *Copy() const { + EQApplicationPacket *it = new EQApplicationPacket; + try { + it->pBuffer= new unsigned char[size]; + memcpy(it->pBuffer,pBuffer,size); + it->size=size; + it->opcode = opcode; + it->emu_opcode = emu_opcode; + it->version = version; + return(it); + } + catch( bad_alloc &ba ) + { + cout << ba.what() << endl; + if( NULL != it ) + delete it; + } + return NULL; + } + + void SetOpcodeSize(uint8 s) { app_opcode_size=s; } + void SetOpcode(EmuOpcode op); + const EmuOpcode GetOpcodeConst() const; + inline const EmuOpcode GetOpcode() const { return(GetOpcodeConst()); } + //caching version of get + inline const EmuOpcode GetOpcode() { EmuOpcode r = GetOpcodeConst(); emu_opcode = r; return(r); } + + static uint8 default_opcode_size; + +protected: + //this is just a cache so we dont look it up several times on Get() + EmuOpcode emu_opcode; + +private: + //this constructor should only be used by EQProtocolPacket, as it + //assumes the first two bytes of buf are the opcode. + EQApplicationPacket(const unsigned char *buf, uint32 len, uint8 opcode_size=0); + EQApplicationPacket(const EQApplicationPacket &p) { emu_opcode = OP_Unknown; app_opcode_size=default_opcode_size; } + + uint8 app_opcode_size; +}; + +void DumpPacketHex(const EQApplicationPacket* app); +void DumpPacket(const EQProtocolPacket* app); +void DumpPacketAscii(const EQApplicationPacket* app); +void DumpPacket(const EQApplicationPacket* app, bool iShowInfo = false); +void DumpPacketBin(const EQApplicationPacket* app); + +#endif diff --git a/old/EQStream.cpp b/old/EQStream.cpp new file mode 100644 index 0000000..fef52ad --- /dev/null +++ b/old/EQStream.cpp @@ -0,0 +1,1921 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#ifdef WIN32 +#include + #include +#endif +#include "debug.h" +#include +#include +#include +#include +#include +#include +#ifdef WIN32 + #include +#else + #include + #include + #include + #include + #include + #include + #include +#endif +#include "EQPacket.h" +#include "EQStream.h" +#include "EQStreamFactory.h" +#include "misc.h" +#include "Mutex.h" +#include "op_codes.h" +#include "CRC16.h" +#include "packet_dump.h" +#ifdef LOGIN + #include "../LoginServer/login_structs.h" +#endif +#include "EQ2_Common_Structs.h" +#include "Log.h" + + +//#define DEBUG_EMBEDDED_PACKETS 1 +uint16 EQStream::MaxWindowSize=2048; + +void EQStream::init(bool resetSession) { + if (resetSession) + { + streamactive = false; + sessionAttempts = 0; + } + + timeout_delays = 0; + + MInUse.lock(); + active_users = 0; + MInUse.unlock(); + + Session=0; + Key=0; + MaxLen=0; + NextInSeq=0; + NextOutSeq=0; + CombinedAppPacket=NULL; + + MAcks.lock(); + MaxAckReceived = -1; + NextAckToSend = -1; + LastAckSent = -1; + MAcks.unlock(); + + LastSeqSent=-1; + MaxSends=5; + LastPacket=Timer::GetCurrentTime2(); + oversize_buffer=NULL; + oversize_length=0; + oversize_offset=0; + Factory = NULL; + + rogue_buffer=NULL; + roguebuf_offset=0; + roguebuf_size=0; + + MRate.lock(); + RateThreshold=RATEBASE/250; + DecayRate=DECAYBASE/250; + MRate.unlock(); + + BytesWritten=0; + SequencedBase = 0; + AverageDelta = 500; + + crypto->setRC4Key(0); + + retransmittimer = Timer::GetCurrentTime2(); + retransmittimeout = 500 * RETRANSMIT_TIMEOUT_MULT; + + reconnectAttempt = 0; + if (uint16(SequencedBase + SequencedQueue.size()) != NextOutSeq) { + LogWrite(PACKET__DEBUG, 9, "Packet", "init Invalid Sequenced queue: BS %u + SQ %u != NOS %u", SequencedBase, SequencedQueue.size(), NextOutSeq); + } +} + +EQStream::EQStream(sockaddr_in addr){ + crypto = new Crypto(); + resend_que_timer = new Timer(1000); + combine_timer = new Timer(250); //250 milliseconds + combine_timer->Start(); + resend_que_timer->Start(); + init(); + remote_ip=addr.sin_addr.s_addr; + remote_port=addr.sin_port; + State=CLOSED; + StreamType=UnknownStream; + compressed=true; + encoded=false; + app_opcode_size=2; + #ifdef WIN32 + ZeroMemory(&stream, sizeof(z_stream)); + #else + bzero(&stream, sizeof(z_stream)); + #endif + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + stream.opaque = (voidpf)0; + deflateInit2(&stream, 9, Z_DEFLATED, 13, 9, Z_DEFAULT_STRATEGY); + //deflateInit(&stream, 5); + compressed_offset = 0; + client_version = 0; + received_packets = 0; + sent_packets = 0; + +#ifdef WRITE_PACKETS + write_packets = 0; + char write_packets_filename[64]; + snprintf(write_packets_filename, sizeof(write_packets_filename), "PacketLog%i.log", Timer::GetCurrentTime2()); + write_packets = fopen(write_packets_filename, "w+"); +#endif +} + +EQProtocolPacket* EQStream::ProcessEncryptedData(uchar* data, int32 size, int16 opcode){ + //cout << "B4:\n"; + //DumpPacket(data, size); + /*if(size >= 2 && data[0] == 0 && data[1] == 0){ + cout << "Attempting to fix packet!\n"; + //Have to fix bad packet from client or it will screw up encryption :P + size--; + data++; + }*/ + crypto->RC4Decrypt(data,size); + int8 offset = 0; + if(data[0] == 0xFF && size > 2){ + offset = 3; + memcpy(&opcode, data+sizeof(int8), sizeof(int16)); + } + else{ + offset = 1; + memcpy(&opcode, data, sizeof(int8)); + } + //cout << "After:\n"; + //DumpPacket(data, size); + return new EQProtocolPacket(opcode, data+offset, size - offset); +} + +EQProtocolPacket* EQStream::ProcessEncryptedPacket(EQProtocolPacket *p){ + EQProtocolPacket* ret = NULL; + if(p->opcode == OP_Packet && p->size > 2) + ret = ProcessEncryptedData(p->pBuffer+2, p->size-2, p->opcode); + else + ret = ProcessEncryptedData(p->pBuffer, p->size, p->opcode); + return ret; +} + +bool EQStream::ProcessEmbeddedPacket(uchar* pBuffer, int16 length,int8 opcode) { + if(!pBuffer || !crypto->isEncrypted()) + return false; + + MCombineQueueLock.lock(); + EQProtocolPacket* newpacket = ProcessEncryptedData(pBuffer, length, opcode); + MCombineQueueLock.unlock(); + + if (newpacket) { +#ifdef DEBUG_EMBEDDED_PACKETS + printf("Opcode: %u\n", newpacket->opcode); + DumpPacket(newpacket->pBuffer, newpacket->size); +#endif + + EQApplicationPacket* ap = newpacket->MakeApplicationPacket(2); + if (ap->version == 0) + ap->version = client_version; + InboundQueuePush(ap); +#ifdef WRITE_PACKETS + WritePackets(ap->GetOpcodeName(), pBuffer, length, false); +#endif + safe_delete(newpacket); + return true; + } + + return false; +} + +bool EQStream::HandleEmbeddedPacket(EQProtocolPacket *p, int16 offset, int16 length){ + if(!p) + return false; + +#ifdef DEBUG_EMBEDDED_PACKETS + // printf works better with DumpPacket + printf( "Start Packet with offset %u, length %u, p->size %u\n", offset, length, p->size); +#endif + + if(p->size >= ((uint32)(offset+2))){ + if(p->pBuffer[offset] == 0 && p->pBuffer[offset+1] == 0x19){ + uint32 data_length = 0; + if(length == 0) { + // Ensure there are at least 2 bytes after offset. + if(p->size < offset + 2) { + return false; // Not enough data. + } + data_length = p->size - offset - 2; + } else { + // Ensure provided length is at least 2. + if(length < 2) { + return false; // Provided length too short. + } + data_length = length - 2; + } +#ifdef DEBUG_EMBEDDED_PACKETS + printf( "Creating OP_AppCombined Packet with offset %u, length %u, p->size %u\n", offset, length, p->size); + DumpPacket(p->pBuffer, p->size); +#endif + // Verify that offset + 2 + data_length does not exceed p->size. + if(offset + 2 + data_length > p->size) { + return false; // Out-of-bounds. + } + EQProtocolPacket *subp = new EQProtocolPacket(OP_AppCombined, p->pBuffer + offset + 2, data_length); + subp->copyInfo(p); + ProcessPacket(subp, p); + safe_delete(subp); + return true; + } + else if (p->pBuffer[offset] == 0 && p->pBuffer[offset + 1] == 0) { + if (length == 0) + length = p->size - 1 - offset; + else + length--; + +#ifdef DEBUG_EMBEDDED_PACKETS + printf( "Creating Opcode 0 Packet!"); + DumpPacket(p->pBuffer + 1 + offset, length); +#endif + uchar* buffer = (p->pBuffer + 1 + offset); + bool valid = ProcessEmbeddedPacket(buffer, length); + + if(valid) + return true; + } + else if(offset+4 < p->size && ntohl(*(uint32 *)(p->pBuffer+offset)) != 0xffffffff) { +#ifdef DEBUG_EMBEDDED_PACKETS + uint16 seq = NextInSeq-1; + sint8 check = 0; + + if(offset == 2) { + seq=ntohs(*(uint16 *)(p->pBuffer)); + check=CompareSequence(NextInSeq,seq); + } + printf( "Unhandled Packet with offset %u, length %u, p->size %u, check: %i, nextinseq: %u, seq: %u\n", offset, length, p->size, check, NextInSeq, seq); + DumpPacket(p->pBuffer, p->size); +#endif + + if(length == 0) + length = p->size - offset; + + + uchar* buffer = (p->pBuffer + offset); + + bool valid = ProcessEmbeddedPacket(buffer, length); + + if(valid) + return true; + } + else if(p->pBuffer[offset] != 0xff && p->pBuffer[offset+1] == 0xff && p->size >= offset + 3) { + // Read the first byte into a wider type to avoid underflow. + uint16 total_length = p->pBuffer[offset]; // promote to uint16 + // Check that there is enough data: we expect offset+2+total_length == p->size. + if(total_length + offset + 2 == p->size && total_length >= 2) { + uint32 data_length = total_length - 2; + // No additional bounds check needed because equality condition ensures it. + EQProtocolPacket *subp = new EQProtocolPacket(p->pBuffer + offset + 2, data_length, OP_Packet); + subp->copyInfo(p); + ProcessPacket(subp, p); + delete subp; + return true; + } + } + } + return false; +} + +void EQStream::ProcessPacket(EQProtocolPacket *p, EQProtocolPacket* lastp) +{ + uint32 processed=0,subpacket_length=0; + + if (p) { + + if (p->opcode!=OP_SessionRequest && p->opcode!=OP_SessionResponse && !Session) { +#ifdef EQN_DEBUG + LogWrite(PACKET__ERROR, 0, "Packet", "*** Session not initialized, packet ignored "); + //p->DumpRaw(); +#endif + return; + } + + //cout << "Received " << (int)p->opcode << ":\n"; + //DumpPacket(p->pBuffer, p->size); + switch (p->opcode) { + case OP_Combined: { + processed=0; + int8 offset = 0; + int count = 0; +#ifdef LE_DEBUG + printf( "OP_Combined:\n"); + DumpPacket(p); +#endif + while(processedsize) { + if ((subpacket_length=(unsigned char)*(p->pBuffer+processed))==0xff) { + subpacket_length = ntohs(*(uint16*)(p->pBuffer + processed + 1)); + //printf("OP_Combined subpacket_length %u\n",subpacket_length); + offset = 3; + } + else { + offset = 1; + } + + //printf("OP_Combined processed %u p->size %u subpacket length %u count %i\n",processed, p->size, subpacket_length, count); + count++; +#ifdef LE_DEBUG + printf( "OP_Combined Packet %i (%u) (%u):\n", count, subpacket_length, processed); +#endif + bool isSubPacket = EQProtocolPacket::IsProtocolPacket(p->pBuffer + processed + offset, subpacket_length, false); + if (isSubPacket) { + EQProtocolPacket* subp = new EQProtocolPacket(p->pBuffer + processed + offset, subpacket_length); + subp->copyInfo(p); +#ifdef LE_DEBUG + printf( "Opcode %i:\n", subp->opcode); + DumpPacket(subp); +#endif + ProcessPacket(subp, p); +#ifdef LE_DEBUG + DumpPacket(subp); +#endif + delete subp; + } + else { + offset = 1; // 0xFF in this case means it is actually 255 bytes of encrypted data after a 00 09 packet + //Garbage packet? + if(ntohs(*reinterpret_cast(p->pBuffer + processed + offset)) <= 0x1e) { + subpacket_length=(unsigned char)*(p->pBuffer+processed); + LogWrite(PACKET__ERROR, 0, "Packet", "!!!!!!!!!Garbage Packet Unknown Process as OP_Packet!!!!!!!!!!!!!\n"); + DumpPacket(p->pBuffer + processed + offset, subpacket_length); + uchar* newbuf = p->pBuffer; + newbuf += processed + offset; + EQProtocolPacket *subp=new EQProtocolPacket(newbuf,subpacket_length); + subp->copyInfo(p); + ProcessPacket(subp, p); + delete subp; + } + else { + crypto->RC4Decrypt(p->pBuffer + processed + offset, subpacket_length); + LogWrite(PACKET__ERROR, 0, "Packet", "!!!!!!!!!Garbage Packet!!!!!!!!!!!!! processed: %u, offset: %u, count: %i, subpacket_length: %u, offset_pos_1: %u, oversized_buffer_present: %u, offset size: %u, offset length: %u\n", + processed, offset, count, subpacket_length, p->pBuffer[processed + offset], oversize_buffer ? 1 : 0, oversize_offset, oversize_length); + if(p->pBuffer[processed + offset] == 0xff) + { + uchar* newbuf = p->pBuffer; + newbuf += processed + offset + 1; + + DumpPacket(p->pBuffer + processed + offset, subpacket_length); + EQProtocolPacket *subp=new EQProtocolPacket(newbuf, subpacket_length, OP_Packet); + subp->copyInfo(p); + ProcessPacket(subp, p); + delete subp; + } + else + break; // bad packet + } + } + processed+=subpacket_length+offset; + } + break; + } + case OP_AppCombined: { + processed=0; + EQProtocolPacket* newpacket = 0; + int8 offset = 0; +#ifdef DEBUG_EMBEDDED_PACKETS + printf( "OP_AppCombined: \n"); + DumpPacket(p); +#endif + int count = 0; + while(processedsize) { + count++; + if ((subpacket_length=(unsigned char)*(p->pBuffer+processed))==0xff) { + subpacket_length=ntohs(*(uint16 *)(p->pBuffer+processed+1)); + offset = 3; + } else + offset = 1; + + if(crypto->getRC4Key()==0 && p && subpacket_length > 8+offset){ + #ifdef DEBUG_EMBEDDED_PACKETS + DumpPacket(p->pBuffer, p->size); + #endif + p->pBuffer += offset; + processRSAKey(p, subpacket_length); + p->pBuffer -= offset; + } + else if(crypto->isEncrypted()){ +#ifdef DEBUG_EMBEDDED_PACKETS + printf( "OP_AppCombined Packet %i (%u) (%u): \n", count, subpacket_length, processed); + DumpPacket(p->pBuffer+processed+offset, subpacket_length); +#endif + if(!HandleEmbeddedPacket(p, processed + offset, subpacket_length)){ + uchar* buffer = (p->pBuffer + processed + offset); + if(!ProcessEmbeddedPacket(buffer, subpacket_length, OP_AppCombined)) { + LogWrite(PACKET__ERROR, 0, "Packet", "*** This is bad, ProcessEmbeddedPacket failed, report to Image!"); + } + } + } + processed+=subpacket_length+offset; + } + } + break; + case OP_Packet: { + if (!p->pBuffer || (p->Size() < 4)) + { + break; + } + + uint16 seq=ntohs(*(uint16 *)(p->pBuffer)); + sint8 check=CompareSequence(NextInSeq,seq); + if (check == SeqFuture) { +#ifdef EQN_DEBUG + LogWrite(PACKET__DEBUG, 1, "Packet", "*** Future packet: Expecting Seq=%i, but got Seq=%i", NextInSeq, seq); + LogWrite(PACKET__DEBUG, 1, "Packet", "[Start]"); + p->DumpRawHeader(seq); + LogWrite(PACKET__DEBUG, 1, "Packet", "[End]"); +#endif + OutOfOrderpackets[seq] = p->Copy(); + + // Image (2020): Removed as this is bad contributes to infinite loop + //SendOutOfOrderAck(seq); + } else if (check == SeqPast) { +#ifdef EQN_DEBUG + LogWrite(PACKET__DEBUG, 1, "Packet", "*** Duplicate packet: Expecting Seq=%i, but got Seq=%i", NextInSeq, seq); + LogWrite(PACKET__DEBUG, 1, "Packet", "[Start]"); + p->DumpRawHeader(seq); + LogWrite(PACKET__DEBUG, 1, "Packet", "[End]"); +#endif + // Image (2020): Removed as this is bad contributes to infinite loop + //OutOfOrderpackets[seq] = p->Copy(); + SendOutOfOrderAck(seq); + } else { + EQProtocolPacket* qp = RemoveQueue(seq); + if (qp) { + LogWrite(PACKET__DEBUG, 1, "Packet", "OP_Fragment: Removing older queued packet with sequence %i", seq); + delete qp; + } + + SetNextAckToSend(seq); + NextInSeq++; + + if(HandleEmbeddedPacket(p)) + break; + if(crypto->getRC4Key()==0 && p && p->size >= 69){ + #ifdef DEBUG_EMBEDDED_PACKETS + DumpPacket(p->pBuffer, p->size); + #endif + processRSAKey(p); + } + else if(crypto->isEncrypted() && p){ + MCombineQueueLock.lock(); + EQProtocolPacket* newpacket = ProcessEncryptedPacket(p); + MCombineQueueLock.unlock(); + if(newpacket){ + EQApplicationPacket* ap = newpacket->MakeApplicationPacket(2); + if (ap->version == 0) + ap->version = client_version; +#ifdef WRITE_PACKETS + WritePackets(ap->GetOpcodeName(), p->pBuffer, p->size, false); +#endif + InboundQueuePush(ap); + safe_delete(newpacket); + } + } + } + } + break; + case OP_Fragment: { + if (!p->pBuffer || (p->Size() < 4)) + { + break; + } + + uint16 seq=ntohs(*(uint16 *)(p->pBuffer)); + sint8 check=CompareSequence(NextInSeq,seq); + if (check == SeqFuture) { +#ifdef EQN_DEBUG + LogWrite(PACKET__DEBUG, 1, "Packet", "*** Future packet2: Expecting Seq=%i, but got Seq=%i", NextInSeq, seq); + LogWrite(PACKET__DEBUG, 1, "Packet", "[Start]"); + //p->DumpRawHeader(seq); + LogWrite(PACKET__DEBUG, 1, "Packet", "[End]"); +#endif + OutOfOrderpackets[seq] = p->Copy(); + //SendOutOfOrderAck(seq); + } else if (check == SeqPast) { +#ifdef EQN_DEBUG + LogWrite(PACKET__DEBUG, 1, "Packet", "*** Duplicate packet2: Expecting Seq=%i, but got Seq=%i", NextInSeq, seq); + LogWrite(PACKET__DEBUG, 1, "Packet", "[Start]"); + //p->DumpRawHeader(seq); + LogWrite(PACKET__DEBUG, 1, "Packet", "[End]"); +#endif + //OutOfOrderpackets[seq] = p->Copy(); + SendOutOfOrderAck(seq); + } else { + // In case we did queue one before as well. + EQProtocolPacket* qp = RemoveQueue(seq); + if (qp) { + LogWrite(PACKET__DEBUG, 1, "Packet", "OP_Fragment: Removing older queued packet with sequence %i", seq); + delete qp; + } + + SetNextAckToSend(seq); + NextInSeq++; + if (oversize_buffer) { + memcpy(oversize_buffer+oversize_offset,p->pBuffer+2,p->size-2); + oversize_offset+=p->size-2; + //cout << "Oversized is " << oversize_offset << "/" << oversize_length << " (" << (p->size-2) << ") Seq=" << seq << endl; + if (oversize_offset==oversize_length) { + if (*(p->pBuffer+2)==0x00 && *(p->pBuffer+3)==0x19) { + EQProtocolPacket *subp=new EQProtocolPacket(oversize_buffer,oversize_offset); + subp->copyInfo(p); + ProcessPacket(subp, p); + delete subp; + } else { + + if(crypto->isEncrypted() && p && p->size > 2){ + MCombineQueueLock.lock(); + EQProtocolPacket* p2 = ProcessEncryptedData(oversize_buffer, oversize_offset, p->opcode); + MCombineQueueLock.unlock(); + EQApplicationPacket* ap = p2->MakeApplicationPacket(2); + ap->copyInfo(p); + if (ap->version == 0) + ap->version = client_version; +#ifdef WRITE_PACKETS + WritePackets(ap->GetOpcodeName(), oversize_buffer, oversize_offset, false); +#endif + ap->copyInfo(p); + InboundQueuePush(ap); + safe_delete(p2); + } + } + delete[] oversize_buffer; + oversize_buffer=NULL; + oversize_offset=0; + } + } else if (!oversize_buffer) { + oversize_length=ntohl(*(uint32 *)(p->pBuffer+2)); + oversize_buffer=new unsigned char[oversize_length]; + memcpy(oversize_buffer,p->pBuffer+6,p->size-6); + oversize_offset=p->size-6; + //cout << "Oversized is " << oversize_offset << "/" << oversize_length << " (" << (p->size-6) << ") Seq=" << seq << endl; + } + } + } + break; + case OP_KeepAlive: { +#ifndef COLLECTOR + NonSequencedPush(new EQProtocolPacket(p->opcode,p->pBuffer,p->size)); +#endif + } + break; + case OP_Ack: { + if (!p->pBuffer || (p->Size() < 4)) + { + LogWrite(PACKET__DEBUG, 9, "Packet", "Received OP_Ack that was of malformed size"); + break; + } + uint16 seq = ntohs(*(uint16*)(p->pBuffer)); + AckPackets(seq); + retransmittimer = Timer::GetCurrentTime2(); + } + break; + case OP_SessionRequest: { + if (p->Size() < sizeof(SessionRequest)) + { + break; + } + + if (GetState() == ESTABLISHED) { + //_log(NET__ERROR, _L "Received OP_SessionRequest in ESTABLISHED state (%d) streamactive (%i) attempt (%i)" __L, GetState(), streamactive, sessionAttempts); + + // client seems to try a max of 4 times (initial +3 retries) then gives up, giving it a few more attempts just in case + // streamactive means we identified the opcode, we cannot re-establish this connection + if (streamactive || (sessionAttempts > 30)) + { + SendDisconnect(false); + SetState(CLOSED); + break; + } + } + + sessionAttempts++; + if(GetState() == WAIT_CLOSE) { + printf("WAIT_CLOSE Reconnect with streamactive %u, sessionAttempts %u\n", streamactive, sessionAttempts); + reconnectAttempt++; + } + init(GetState() != ESTABLISHED); + OutboundQueueClear(); + SessionRequest *Request=(SessionRequest *)p->pBuffer; + Session=ntohl(Request->Session); + SetMaxLen(ntohl(Request->MaxLength)); +#ifndef COLLECTOR + NextInSeq=0; + Key=0x33624702; + SendSessionResponse(); +#endif + SetState(ESTABLISHED); + } + break; + case OP_SessionResponse: { + if (p->Size() < sizeof(SessionResponse)) + { + break; + } + init(); + OutboundQueueClear(); + SetActive(true); + SessionResponse *Response=(SessionResponse *)p->pBuffer; + SetMaxLen(ntohl(Response->MaxLength)); + Key=ntohl(Response->Key); + NextInSeq=0; + SetState(ESTABLISHED); + if (!Session) + Session=ntohl(Response->Session); + compressed=(Response->Format&FLAG_COMPRESSED); + encoded=(Response->Format&FLAG_ENCODED); + + // Kinda kludgy, but trie for now + if (compressed) { + if (remote_port==9000 || (remote_port==0 && p->src_port==9000)) + SetStreamType(WorldStream); + else + SetStreamType(ZoneStream); + } else if (encoded) + SetStreamType(ChatOrMailStream); + else + SetStreamType(LoginStream); + } + break; + case OP_SessionDisconnect: { + //NextInSeq=0; + SendDisconnect(); + //SetState(CLOSED); + } + break; + case OP_OutOfOrderAck: { + if (!p->pBuffer || (p->Size() < 4)) + { + LogWrite(PACKET__DEBUG, 9, "Packet", "Received OP_OutOfOrderAck that was of malformed size"); + break; + } + uint16 seq = ntohs(*(uint16*)(p->pBuffer)); + MOutboundQueue.lock(); + + if (uint16(SequencedBase + SequencedQueue.size()) != NextOutSeq) { + LogWrite(PACKET__DEBUG, 9, "Packet", "Pre-OOA Invalid Sequenced queue: BS %u + SQ %u != NOS %u", SequencedBase, SequencedQueue.size(), NextOutSeq); + } + + //if the packet they got out of order is between our last acked packet and the last sent packet, then its valid. + if (CompareSequence(SequencedBase, seq) != SeqPast && CompareSequence(NextOutSeq, seq) == SeqPast) { + uint16 sqsize = SequencedQueue.size(); + uint16 index = seq - SequencedBase; + LogWrite(PACKET__DEBUG, 9, "Packet", "OP_OutOfOrderAck marking packet acked in queue (queue index = %u, queue size = %u)", index, sqsize); + if (index < sqsize) { + SequencedQueue[index]->acked = true; + // flag packets for a resend + uint16 count = 0; + uint32 timeout = AverageDelta * 2 + 100; + for (auto sitr = SequencedQueue.begin(); sitr != SequencedQueue.end() && count < index; ++sitr, ++count) { + if (!(*sitr)->acked && (*sitr)->sent_time > 0 && (((*sitr)->sent_time + timeout) < Timer::GetCurrentTime2())) { + (*sitr)->sent_time = 0; + LogWrite(PACKET__DEBUG, 9, "Packet", "OP_OutOfOrderAck Flagging packet %u for retransmission", SequencedBase + count); + } + } + } + + if (RETRANSMIT_TIMEOUT_MULT) { + retransmittimer = Timer::GetCurrentTime2(); + } + } + else { + LogWrite(PACKET__DEBUG, 9, "Packet", "Received OP_OutOfOrderAck for out-of-window %u. Window (%u->%u)", seq, SequencedBase, NextOutSeq); + } + + if (uint16(SequencedBase + SequencedQueue.size()) != NextOutSeq) { + LogWrite(PACKET__DEBUG, 9, "Packet", "Post-OOA Invalid Sequenced queue: BS %u + SQ %u != NOS %u", SequencedBase, SequencedQueue.size(), NextOutSeq); + } + + MOutboundQueue.unlock(); + } + break; + case OP_ServerKeyRequest:{ + if (p->Size() < sizeof(ClientSessionStats)) + { + //_log(NET__ERROR, _L "Received OP_SessionStatRequest that was of malformed size" __L); + break; + } + + ClientSessionStats* Stats = (ClientSessionStats*)p->pBuffer; + int16 request_id = Stats->RequestID; + AdjustRates(ntohl(Stats->average_delta)); + ServerSessionStats* stats=(ServerSessionStats*)p->pBuffer; + memset(stats, 0, sizeof(ServerSessionStats)); + stats->RequestID = request_id; + stats->current_time = ntohl(Timer::GetCurrentTime2()); + stats->sent_packets = ntohl(sent_packets); + stats->sent_packets2 = ntohl(sent_packets); + stats->received_packets = ntohl(received_packets); + stats->received_packets2 = ntohl(received_packets); + NonSequencedPush(new EQProtocolPacket(OP_SessionStatResponse,p->pBuffer,p->size)); + if(!crypto->isEncrypted()) + SendKeyRequest(); + else + SendSessionResponse(); + } + break; + case OP_SessionStatResponse: { + LogWrite(PACKET__INFO, 0, "Packet", "OP_SessionStatResponse"); + } + break; + case OP_OutOfSession: { + LogWrite(PACKET__INFO, 0, "Packet", "OP_OutOfSession"); + SendDisconnect(); + SetState(CLOSED); + } + break; + default: + //EQApplicationPacket *ap = p->MakeApplicationPacket(app_opcode_size); + //InboundQueuePush(ap); + + cout << "Orig Packet: " << p->opcode << endl; + DumpPacket(p->pBuffer, p->size); + if(p && p->size >= 69){ + processRSAKey(p); + } + MCombineQueueLock.lock(); + EQProtocolPacket* p2 = ProcessEncryptedData(p->pBuffer, p->size, OP_Packet); + MCombineQueueLock.unlock(); + cout << "Decrypted Packet: " << p2->opcode << endl; + DumpPacket(p2->pBuffer, p2->size); + + safe_delete(p2); + /* if(p2) + { + EQApplicationPacket* ap = p2->MakeApplicationPacket(2); + if (ap->version == 0) + ap->version = client_version; + InboundQueuePush(ap); + safe_delete(p2); + }*/ + + //EQProtocolPacket* puse = p2; + /* if (!rogue_buffer) { + roguebuf_size=puse->size; + rogue_buffer=new unsigned char[roguebuf_size]; + memcpy(rogue_buffer,puse->pBuffer,puse->size); + roguebuf_offset=puse->size; + cout << "RogueBuf is " << roguebuf_offset << "/" << roguebuf_size << " (" << (p->size-6) << ") NextInSeq=" << NextInSeq << endl; + } + else { + int32 new_size = roguebuf_size + puse->size; + uchar* tmp_buffer = new unsigned char[new_size]; + uchar* ptr = tmp_buffer; + + memcpy(ptr,rogue_buffer,roguebuf_size); + ptr += roguebuf_size; + memcpy(ptr,puse->pBuffer,puse->size); + roguebuf_offset=puse->size; + + safe_delete_array(rogue_buffer); + + rogue_buffer = tmp_buffer; + roguebuf_size = new_size; + roguebuf_offset = new_size; + cout << "RogueBuf is " << roguebuf_offset << "/" << roguebuf_size << " (" << (p->size-6) << ") NextInSeq=" << NextInSeq << endl; + }*/ +#ifdef WRITE_PACKETS + WritePackets(ap->GetOpcodeName(), p->pBuffer, p->size, false); +#endif + //InboundQueuePush(ap); + LogWrite(PACKET__INFO, 0, "Packet", "Received unknown packet type, not adding to inbound queue"); + //safe_delete(p2); + //SendDisconnect(); + break; + } + } +} + +int8 EQStream::EQ2_Compress(EQ2Packet* app, int8 offset){ + +#ifdef LE_DEBUG + printf( "Before Compress in %s, line %i:\n", __FUNCTION__, __LINE__); + DumpPacket(app); +#endif + + + uchar* pDataPtr = app->pBuffer + offset; + int xpandSize = app->size * 2; + uchar* deflate_buff = new uchar[xpandSize]; + MCompressData.lock(); + stream.next_in = pDataPtr; + stream.avail_in = app->size - offset; + stream.next_out = deflate_buff; + stream.avail_out = xpandSize; + + int ret = deflate(&stream, Z_SYNC_FLUSH); + + if (ret != Z_OK) + { + printf("ZLIB COMPRESSION RETFAIL: %i, %i (Ret: %i)\n", app->size, stream.avail_out, ret); + MCompressData.unlock(); + safe_delete_array(deflate_buff); + return 0; + } + + int32 newsize = xpandSize - stream.avail_out; + safe_delete_array(app->pBuffer); + app->size = newsize + offset; + app->pBuffer = new uchar[app->size]; + app->pBuffer[(offset - 1)] = 1; + memcpy(app->pBuffer + offset, deflate_buff, newsize); + MCompressData.unlock(); + safe_delete_array(deflate_buff); + +#ifdef LE_DEBUG + printf( "After Compress in %s, line %i:\n", __FUNCTION__, __LINE__); + DumpPacket(app); +#endif + + return offset - 1; +} + +int16 EQStream::processRSAKey(EQProtocolPacket *p, uint16 subpacket_length){ + /*int16 limit = 0; + int8 offset = 13; + int8 offset2 = 0; + if(p->pBuffer[2] == 0) + limit = p->pBuffer[9]; + else{ + limit = p->pBuffer[5]; + offset2 = 5; + offset-=1; + } + crypto->setRC4Key(Crypto::RSADecrypt(p->pBuffer + offset + (limit-8), 8)); + return (limit + offset +1) - offset2;*/ + if(subpacket_length) + crypto->setRC4Key(Crypto::RSADecrypt(p->pBuffer + subpacket_length - 8, 8)); + else + crypto->setRC4Key(Crypto::RSADecrypt(p->pBuffer + p->size - 8, 8)); + + return 0; +} + +void EQStream::SendKeyRequest(){ + int32 crypto_key_size = 60; + int16 size = sizeof(KeyGen_Struct) + sizeof(KeyGen_End_Struct) + crypto_key_size; + EQ2Packet *outapp=new EQ2Packet(OP_WSLoginRequestMsg,NULL,size); + memcpy(&outapp->pBuffer[0], &crypto_key_size, sizeof(int32)); + memset(&outapp->pBuffer[4], 0xFF, crypto_key_size); + memset(&outapp->pBuffer[size-5], 1, 1); + memset(&outapp->pBuffer[size-1], 1, 1); + EQ2QueuePacket(outapp, true); +} + +void EQStream::EncryptPacket(EQ2Packet* app, int8 compress_offset, int8 offset){ + if(app->size>2 && crypto->isEncrypted()){ + app->packet_encrypted = true; + uchar* crypt_buff = app->pBuffer; + if(app->eq2_compressed) + crypto->RC4Encrypt(crypt_buff + compress_offset, app->size - compress_offset); + else + crypto->RC4Encrypt(crypt_buff + 2 + offset, app->size - 2 - offset); + } +} + +void EQStream::EQ2QueuePacket(EQ2Packet* app, bool attempted_combine){ + if(CheckActive()){ + if(!attempted_combine){ + MCombineQueueLock.lock(); + combine_queue.push_back(app); + MCombineQueueLock.unlock(); + } + else{ + MCombineQueueLock.lock(); + PreparePacket(app); + MCombineQueueLock.unlock(); +#ifdef LE_DEBUG + printf( "After B in %s, line %i:\n", __FUNCTION__, __LINE__); + DumpPacket(app); +#endif + SendPacket(app); + } + } +} + +void EQStream::UnPreparePacket(EQ2Packet* app){ + if(app->pBuffer[2] == 0 && app->pBuffer[3] == 19){ + uchar* new_buffer = new uchar[app->size-3]; + memcpy(new_buffer+2, app->pBuffer+5, app->size-3); + delete[] app->pBuffer; + app->size-=3; + app->pBuffer = new_buffer; + } +} + +#ifdef WRITE_PACKETS +char EQStream::GetChar(uchar in) +{ + if (in < ' ' || in > '~') + return '.'; + return (char)in; +} +void EQStream::WriteToFile(char* pFormat, ...) { + va_list args; + va_start(args, pFormat); + vfprintf(write_packets, pFormat, args); + va_end(args); +} + +void EQStream::WritePackets(const char* opcodeName, uchar* data, int32 size, bool outgoing) { + MWritePackets.lock(); + struct in_addr ip_addr; + ip_addr.s_addr = remote_ip; + char timebuffer[80]; + time_t rawtime; + struct tm* timeinfo; + time(&rawtime); + timeinfo = localtime(&rawtime); + strftime(timebuffer, 80, "%m/%d/%Y %H:%M:%S", timeinfo); + if (outgoing) + WriteToFile("-- %s --\n%s\nSERVER -> %s\n", opcodeName, timebuffer, inet_ntoa(ip_addr)); + else + WriteToFile("-- %s --\n%s\n%s -> SERVER\n", opcodeName, timebuffer, inet_ntoa(ip_addr)); + int i; + int nLines = size / 16; + int nExtra = size % 16; + uchar* pPtr = data; + for (i = 0; i < nLines; i++) + { + WriteToFile("%4.4X:\t%2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c\n", i * 16, pPtr[0], pPtr[1], pPtr[2], pPtr[3], pPtr[4], pPtr[5], pPtr[6], pPtr[7], pPtr[8], pPtr[9], pPtr[10], pPtr[11], pPtr[12], pPtr[13], pPtr[14], pPtr[15], GetChar(pPtr[0]), GetChar(pPtr[1]), GetChar(pPtr[2]), GetChar(pPtr[3]), GetChar(pPtr[4]), GetChar(pPtr[5]), GetChar(pPtr[6]), GetChar(pPtr[7]), GetChar(pPtr[8]), GetChar(pPtr[9]), GetChar(pPtr[10]), GetChar(pPtr[11]), GetChar(pPtr[12]), GetChar(pPtr[13]), GetChar(pPtr[14]), GetChar(pPtr[15])); + pPtr += 16; + } + if (nExtra) + { + WriteToFile("%4.4X\t", nLines * 16); + for (i = 0; i < nExtra; i++) + { + WriteToFile("%2.2X ", pPtr[i]); + } + for (i; i < 16; i++) + WriteToFile(" "); + for (i = 0; i < nExtra; i++) + { + WriteToFile("%c", GetChar(pPtr[i])); + } + WriteToFile("\n"); + } + WriteToFile("\n\n"); + fflush(write_packets); + MWritePackets.unlock(); +} + +void EQStream::WritePackets(EQ2Packet* app, bool outgoing) { + if (app->version == 0) + app->version = client_version; + WritePackets(app->GetOpcodeName(), app->pBuffer, app->size, outgoing); +} +#endif + +void EQStream::PreparePacket(EQ2Packet* app, int8 offset){ + app->setVersion(client_version); + compressed_offset = 0; + +#ifdef LE_DEBUG + printf( "Before A in %s, line %i:\n", __FUNCTION__, __LINE__); + DumpPacket(app); +#endif + if(!app->packet_prepared){ + if(app->PreparePacket(MaxLen) == 255) //invalid version + return; + } + +#ifdef LE_DEBUG + printf( "After Prepare in %s, line %i:\n", __FUNCTION__, __LINE__); + DumpPacket(app); +#endif +#ifdef WRITE_PACKETS + if (!app->eq2_compressed && !app->packet_encrypted) + WritePackets(app, true); +#endif + + if(!app->eq2_compressed && app->size>128){ + compressed_offset = EQ2_Compress(app); + if (compressed_offset) + app->eq2_compressed = true; + } + if(!app->packet_encrypted){ + EncryptPacket(app, compressed_offset, offset); + if(app->size > 2 && app->pBuffer[2] == 0){ + uchar* new_buffer = new uchar[app->size+1]; + new_buffer[2] = 0; + memcpy(new_buffer+3, app->pBuffer+2, app->size-2); + delete[] app->pBuffer; + app->pBuffer = new_buffer; + app->size++; + } + } + +#ifdef LE_DEBUG + printf( "After A in %s, line %i:\n", __FUNCTION__, __LINE__); + DumpPacket(app); +#endif + +} + +void EQStream::SendPacket(EQProtocolPacket *p) +{ + uint32 chunksize,used; + uint32 length; + + // Convert the EQApplicationPacket to 1 or more EQProtocolPackets + if (p->size>( MaxLen-8)) { // proto-op(2), seq(2), app-op(2) ... data ... crc(2) + uchar* tmpbuff=p->pBuffer; + length=p->size - 2; + + EQProtocolPacket *out=new EQProtocolPacket(OP_Fragment,NULL,MaxLen-4); + *(uint32 *)(out->pBuffer+2)=htonl(length); + used=MaxLen-10; + memcpy(out->pBuffer+6,tmpbuff+2,used); + +#ifdef LE_DEBUG + printf("(%s, %i) New Fragment:\n ", __FUNCTION__, __LINE__); + DumpPacket(out); +#endif + + SequencedPush(out); + + while (usedpBuffer+2,tmpbuff,1); + memcpy(out->pBuffer+2,tmpbuff+used+2,chunksize); +#ifdef LE_DEBUG + printf("Chunk: \n"); + DumpPacket(out); +#endif + SequencedPush(out); + used+=chunksize; + + } + +#ifdef LE_DEBUG + printf( "ChunkDelete: \n"); + DumpPacket(out); + //cerr << "1: Deleting 0x" << hex << (uint32)(p) << dec << endl; +#endif + + delete p; + } else { + SequencedPush(p); + } +} +void EQStream::SendPacket(EQApplicationPacket *p) +{ +uint32 chunksize,used; +uint32 length; + + // Convert the EQApplicationPacket to 1 or more EQProtocolPackets + if (p->size>(MaxLen-8)) { // proto-op(2), seq(2), app-op(2) ... data ... crc(2) + //cout << "Making oversized packet for: " << endl; + //cout << p->size << endl; + //p->DumpRawHeader(); + //dump_message(p->pBuffer,p->size,timestamp()); + //cout << p->size << endl; + unsigned char *tmpbuff=new unsigned char[p->size+2]; + //cout << hex << (int)tmpbuff << dec << endl; + length=p->serialize(tmpbuff); + + EQProtocolPacket *out=new EQProtocolPacket(OP_Fragment,NULL,MaxLen-4); + *(uint32 *)(out->pBuffer+2)=htonl(p->Size()); + memcpy(out->pBuffer+6,tmpbuff,MaxLen-10); + used=MaxLen-10; + SequencedPush(out); + //cout << "Chunk #" << ++i << " size=" << used << ", length-used=" << (length-used) << endl; + while (usedpBuffer+2,tmpbuff+used,chunksize); + out->size=chunksize+2; + SequencedPush(out); + used+=chunksize; + //cout << "Chunk #"<< ++i << " size=" << chunksize << ", length-used=" << (length-used) << endl; + } + //cerr << "1: Deleting 0x" << hex << (uint32)(p) << dec << endl; + delete p; + delete[] tmpbuff; + } else { + EQProtocolPacket *out=new EQProtocolPacket(OP_Packet,NULL,p->Size()+2); + p->serialize(out->pBuffer+2); + SequencedPush(out); + //cerr << "2: Deleting 0x" << hex << (uint32)(p) << dec << endl; + delete p; + } +} + +void EQStream::SequencedPush(EQProtocolPacket *p) +{ + p->setVersion(client_version); + MOutboundQueue.lock(); + *(uint16 *)(p->pBuffer)=htons(NextOutSeq); + SequencedQueue.push_back(p); + p->sequence = NextOutSeq; + NextOutSeq++; + MOutboundQueue.unlock(); +} + +void EQStream::NonSequencedPush(EQProtocolPacket *p) +{ + p->setVersion(client_version); + MOutboundQueue.lock(); + NonSequencedQueue.push(p); + MOutboundQueue.unlock(); +} + +void EQStream::SendAck(uint16 seq) +{ + uint16 Seq=htons(seq); + SetLastAckSent(seq); + NonSequencedPush(new EQProtocolPacket(OP_Ack,(unsigned char *)&Seq,sizeof(uint16))); +} + +void EQStream::SendOutOfOrderAck(uint16 seq) +{ + uint16 Seq=htons(seq); + NonSequencedPush(new EQProtocolPacket(OP_OutOfOrderAck,(unsigned char *)&Seq,sizeof(uint16))); +} + +bool EQStream::CheckCombineQueue(){ + bool ret = true; //processed all packets + MCombineQueueLock.lock(); + if(combine_queue.size() > 0){ + EQ2Packet* first = combine_queue.front(); + combine_queue.pop_front(); + if(combine_queue.size() == 0){ //nothing to combine this with + EQ2QueuePacket(first, true); + } + else{ + PreparePacket(first); + EQ2Packet* second = 0; + bool combine_worked = false; + int16 count = 0; + while(combine_queue.size()){ + count++; + second = combine_queue.front(); + combine_queue.pop_front(); + PreparePacket(second); + /*if(first->GetRawOpcode() != OP_AppCombined && first->pBuffer[2] == 0){ + EQ2Packet* tmp = second; + second = first; + first = tmp; + }*/ + if(!first->AppCombine(second)){ + first->SetProtocolOpcode(OP_Packet); + if(combine_worked){ + SequencedPush(first); + } + else{ + EQ2QueuePacket(first, true); + } + first = second; + combine_worked = false; + } + else{ + combine_worked = true; + //DumpPacket(first); + } + if(count >= 60 || first->size > 4000){ //other clients need packets too + ret = false; + break; + } + } + if(first){ + first->SetProtocolOpcode(OP_Packet); + if(combine_worked){ + SequencedPush(first); + } + else{ + EQ2QueuePacket(first, true); + } + } + } + } + MCombineQueueLock.unlock(); + return ret; +} + +void EQStream::CheckResend(int eq_fd){ + int32 curr = Timer::GetCurrentTime2(); + EQProtocolPacket* packet = 0; + deque::iterator itr; + MResendQue.lock(); + for(itr=resend_que.begin();itr!=resend_que.end();itr++){ + packet = *itr; + if(packet->attempt_count >= 5){//tried to resend this packet 5 times, client must already have it but didnt ack it + safe_delete(packet); + itr = resend_que.erase(itr); + if(itr == resend_que.end()) + break; + } + else{ + if((curr - packet->sent_time) < 1000) + continue; + packet->sent_time -=1000; + packet->attempt_count++; + WritePacket(eq_fd, packet); + } + } + MResendQue.unlock(); +} + + + +//returns SeqFuture if `seq` is later than `expected_seq` +EQStream::SeqOrder EQStream::CompareSequence(uint16 expected_seq, uint16 seq) +{ + if (expected_seq == seq) { + // Curent + return SeqInOrder; + } + else if ((seq > expected_seq && (uint32)seq < ((uint32)expected_seq + EQStream::MaxWindowSize)) || seq < (expected_seq - EQStream::MaxWindowSize)) { + // Future + return SeqFuture; + } + else { + // Past + return SeqPast; + } +} + +void EQStream::AckPackets(uint16 seq) +{ + std::deque::iterator itr, tmp; + + MOutboundQueue.lock(); + + SeqOrder ord = CompareSequence(SequencedBase, seq); + if (ord == SeqInOrder) { + //they are not acking anything new... + LogWrite(PACKET__DEBUG, 9, "Packet", "Received an ack with no window advancement (seq %u)", seq); + } + else if (ord == SeqPast) { + //they are nacking blocks going back before our buffer, wtf? + LogWrite(PACKET__DEBUG, 9, "Packet", "Received an ack with backward window advancement (they gave %u, our window starts at %u). This is bad" , seq, SequencedBase); + } + else { + LogWrite(PACKET__DEBUG, 9, "Packet", "Received an ack up through sequence %u. Our base is %u", seq, SequencedBase); + + + //this is a good ack, we get to ack some blocks. + seq++; //we stop at the block right after their ack, counting on the wrap of both numbers. + while (SequencedBase != seq) { + if (SequencedQueue.empty()) { + LogWrite(PACKET__DEBUG, 9, "Packet", "OUT OF PACKETS acked packet with sequence %u. Next send is %u before this", (unsigned long)SequencedBase, SequencedQueue.size()); + SequencedBase = NextOutSeq; + break; + } + LogWrite(PACKET__DEBUG, 9, "Packet", "Removing acked packet with sequence %u", (unsigned long)SequencedBase); + //clean out the acked packet + delete SequencedQueue.front(); + SequencedQueue.pop_front(); + //advance the base sequence number to the seq of the block after the one we just got rid of. + SequencedBase++; + } + if (uint16(SequencedBase + SequencedQueue.size()) != NextOutSeq) { + LogWrite(PACKET__DEBUG, 9, "Packet", "Post-Ack on %u Invalid Sequenced queue: BS %u + SQ %u != NOS %u", seq, SequencedBase, SequencedQueue.size(), NextOutSeq); + } + } + + MOutboundQueue.unlock(); +} + +void EQStream::Write(int eq_fd) +{ + queue ReadyToSend; + long maxack; + + // Check our rate to make sure we can send more + MRate.lock(); + sint32 threshold=RateThreshold; + MRate.unlock(); + if (BytesWritten > threshold) { + //cout << "Over threshold: " << BytesWritten << " > " << threshold << endl; + return; + } + + MCombinedAppPacket.lock(); + EQApplicationPacket *CombPack=CombinedAppPacket; + CombinedAppPacket=NULL; + MCombinedAppPacket.unlock(); + + if (CombPack) { + SendPacket(CombPack); + } + + // If we got more packets to we need to ack, send an ack on the highest one + MAcks.lock(); + maxack=MaxAckReceived; + // Added from peaks findings + if (NextAckToSend>LastAckSent || LastAckSent == 0x0000ffff) + SendAck(NextAckToSend); + MAcks.unlock(); + + // Lock the outbound queues while we process + MOutboundQueue.lock(); + + // Adjust where we start sending in case we get a late ack + //if (maxack>LastSeqSent) + // LastSeqSent=maxack; + + // Place to hold the base packet t combine into + EQProtocolPacket *p=NULL; + std::deque::iterator sitr; + + // Find the next sequenced packet to send from the "queue" + sitr = SequencedQueue.begin(); + + uint16 count = 0; + // get to start of packets + while (sitr != SequencedQueue.end() && (*sitr)->sent_time > 0) { + ++sitr; + ++count; + } + + bool SeqEmpty = false, NonSeqEmpty = false; + // Loop until both are empty or MaxSends is reached + while (!SeqEmpty || !NonSeqEmpty) { + + // See if there are more non-sequenced packets left + if (!NonSequencedQueue.empty()) { + if (!p) { + // If we don't have a packet to try to combine into, use this one as the base + // And remove it form the queue + p = NonSequencedQueue.front(); + LogWrite(PACKET__DEBUG, 9, "Packet", "Starting combined packet with non-seq packet of len %u",p->size); + NonSequencedQueue.pop(); + } + else if (!p->combine(NonSequencedQueue.front())) { + // Trying to combine this packet with the base didn't work (too big maybe) + // So just send the base packet (we'll try this packet again later) + LogWrite(PACKET__DEBUG, 9, "Packet", "Combined packet full at len %u, next non-seq packet is len %u", p->size, (NonSequencedQueue.front())->size); + ReadyToSend.push(p); + BytesWritten += p->size; + p = nullptr; + + if (BytesWritten > threshold) { + // Sent enough this round, lets stop to be fair + LogWrite(PACKET__DEBUG, 9, "Packet", "Exceeded write threshold in nonseq (%u > %u)", BytesWritten, threshold); + break; + } + } + else { + // Combine worked, so just remove this packet and it's spot in the queue + LogWrite(PACKET__DEBUG, 9, "Packet", "Combined non-seq packet of len %u, yeilding %u combined", (NonSequencedQueue.front())->size, p->size); + delete NonSequencedQueue.front(); + NonSequencedQueue.pop(); + } + } + else { + // No more non-sequenced packets + NonSeqEmpty = true; + } + + if (sitr != SequencedQueue.end()) { + uint16 seq_send = SequencedBase + count; //just for logging... + + if (SequencedQueue.empty()) { + LogWrite(PACKET__DEBUG, 9, "Packet", "Tried to write a packet with an empty queue (%u is past next out %u)", seq_send, NextOutSeq); + SeqEmpty = true; + continue; + } + + if ((*sitr)->acked || (*sitr)->sent_time != 0) { + ++sitr; + ++count; + if (p) { + LogWrite(PACKET__DEBUG, 9, "Packet", "Final combined packet not full, len %u", p->size); + ReadyToSend.push(p); + BytesWritten += p->size; + p = nullptr; + } + LogWrite(PACKET__DEBUG, 9, "Packet", "Not retransmitting seq packet %u because already marked as acked", seq_send); + } + else if (!p) { + // If we don't have a packet to try to combine into, use this one as the base + // Copy it first as it will still live until it is acked + p = (*sitr)->Copy(); + LogWrite(PACKET__DEBUG, 9, "Packet", "Starting combined packet with seq packet %u of len %u", seq_send, p->size); + (*sitr)->sent_time = Timer::GetCurrentTime2(); + ++sitr; + ++count; + } + else if (!p->combine(*sitr)) { + // Trying to combine this packet with the base didn't work (too big maybe) + // So just send the base packet (we'll try this packet again later) + LogWrite(PACKET__DEBUG, 9, "Packet", "Combined packet full at len %u, next seq packet %u is len %u", p->size, seq_send + 1, (*sitr)->size); + ReadyToSend.push(p); + BytesWritten += p->size; + p = nullptr; + if ((*sitr)->opcode != OP_Fragment && BytesWritten > threshold) { + // Sent enough this round, lets stop to be fair + LogWrite(PACKET__DEBUG, 9, "Packet", "Exceeded write threshold in seq (%u > %u)", BytesWritten, threshold); + break; + } + } + else { + // Combine worked + LogWrite(PACKET__DEBUG, 9, "Packet", "Combined seq packet %u of len %u, yeilding %u combined", seq_send, (*sitr)->size, p->size); + (*sitr)->sent_time = Timer::GetCurrentTime2(); + ++sitr; + ++count; + } + + if (uint16(SequencedBase + SequencedQueue.size()) != NextOutSeq) { + LogWrite(PACKET__DEBUG, 9, "Packet", "Post send Invalid Sequenced queue: BS %u + SQ %u != NOS %u", SequencedBase, SequencedQueue.size(), NextOutSeq); + } + } + else { + // No more sequenced packets + SeqEmpty = true; + } + } + MOutboundQueue.unlock(); // Unlock the queue + + // We have a packet still, must have run out of both seq and non-seq, so send it + if (p) { + LogWrite(PACKET__DEBUG, 9, "Packet", "Final combined packet not full, len %u", p->size); + ReadyToSend.push(p); + BytesWritten += p->size; + } + + // Send all the packets we "made" + while (!ReadyToSend.empty()) { + p = ReadyToSend.front(); + WritePacket(eq_fd, p); + delete p; + ReadyToSend.pop(); + } + + //see if we need to send our disconnect and finish our close + if (SeqEmpty && NonSeqEmpty) { + //no more data to send + if (GetState() == CLOSING) { + MOutboundQueue.lock(); + if (SequencedQueue.size() > 0 ) { + // retransmission attempts + } + else + { + LogWrite(PACKET__DEBUG, 9, "Packet", "All outgoing data flushed, disconnecting client."); + //we are waiting for the queues to empty, now we can do our disconnect. + //this packet will not actually go out until the next call to Write(). + SendDisconnect(); + //SetState(CLOSED); + } + MOutboundQueue.unlock(); + } + } +} + +void EQStream::WritePacket(int eq_fd, EQProtocolPacket *p) +{ +uint32 length = 0; +sockaddr_in address; + address.sin_family = AF_INET; + address.sin_addr.s_addr=remote_ip; + address.sin_port=remote_port; +#ifdef NOWAY + uint32 ip=address.sin_addr.s_addr; + cout << "Sending to: " + << (int)*(unsigned char *)&ip + << "." << (int)*((unsigned char *)&ip+1) + << "." << (int)*((unsigned char *)&ip+2) + << "." << (int)*((unsigned char *)&ip+3) + << "," << (int)ntohs(address.sin_port) << "(" << p->size << ")" << endl; + + p->DumpRaw(); + cout << "-------------" << endl; +#endif + length=p->serialize(buffer); + if (p->opcode!=OP_SessionRequest && p->opcode!=OP_SessionResponse) { + if (compressed) { + BytesWritten -= p->size; + uint32 newlen=EQProtocolPacket::Compress(buffer,length,write_buffer,2048); + memcpy(buffer,write_buffer,newlen); + length=newlen; + BytesWritten += newlen; + } + if (encoded) { + EQProtocolPacket::ChatEncode(buffer,length,Key); + } + *(uint16 *)(buffer+length)=htons(CRC16(buffer,length,Key)); + length+=2; + } + sent_packets++; + //dump_message_column(buffer,length,"Writer: "); + //cout << "Raw Data:\n"; + //DumpPacket(buffer, length); + sendto(eq_fd,(char *)buffer,length,0,(sockaddr *)&address,sizeof(address)); +} + +EQProtocolPacket *EQStream::Read(int eq_fd, sockaddr_in *from) +{ +int socklen; +int length=0; +unsigned char buffer[2048]; +EQProtocolPacket *p=NULL; +char temp[15]; + + socklen=sizeof(sockaddr); +#ifdef WIN32 + length=recvfrom(eq_fd, (char *)buffer, 2048, 0, (struct sockaddr*)from, (int *)&socklen); +#else + length=recvfrom(eq_fd, buffer, 2048, 0, (struct sockaddr*)from, (socklen_t *)&socklen); +#endif + if (length>=2) { + DumpPacket(buffer, length); + p=new EQProtocolPacket(buffer[1],&buffer[2],length-2); + //printf("Read packet: opcode %i length %u, expected-length: %u\n",buffer[1], length, p->size); + uint32 ip=from->sin_addr.s_addr; + sprintf(temp,"%d.%d.%d.%d:%d", + *(unsigned char *)&ip, + *((unsigned char *)&ip+1), + *((unsigned char *)&ip+2), + *((unsigned char *)&ip+3), + ntohs(from->sin_port)); + //cout << timestamp() << "Data from: " << temp << " OpCode 0x" << hex << setw(2) << setfill('0') << (int)p->opcode << dec << endl; + //dump_message(p->pBuffer,p->size,timestamp()); + + } + return p; +} + +void EQStream::SendSessionResponse() +{ +EQProtocolPacket *out=new EQProtocolPacket(OP_SessionResponse,NULL,sizeof(SessionResponse)); + SessionResponse *Response=(SessionResponse *)out->pBuffer; + Response->Session=htonl(Session); + Response->MaxLength=htonl(MaxLen); + Response->UnknownA=2; + Response->Format=0; + if (compressed) + Response->Format|=FLAG_COMPRESSED; + if (encoded) + Response->Format|=FLAG_ENCODED; + Response->Key=htonl(Key); + + out->size=sizeof(SessionResponse); + + NonSequencedPush(out); +} + +void EQStream::SendSessionRequest() +{ + EQProtocolPacket *out=new EQProtocolPacket(OP_SessionRequest,NULL,sizeof(SessionRequest)); + SessionRequest *Request=(SessionRequest *)out->pBuffer; + memset(Request,0,sizeof(SessionRequest)); + Request->Session=htonl(time(NULL)); + Request->MaxLength=htonl(512); + + NonSequencedPush(out); +} + +void EQStream::SendDisconnect(bool setstate) +{ + try{ + if(GetState() != ESTABLISHED && GetState() != WAIT_CLOSE) + return; + + EQProtocolPacket *out=new EQProtocolPacket(OP_SessionDisconnect,NULL,sizeof(uint32)+sizeof(int16)); + *(uint32 *)out->pBuffer=htonl(Session); + out->pBuffer[4] = 0; + out->pBuffer[5] = 6; + NonSequencedPush(out); + if(setstate) + SetState(CLOSING); + } + catch(...){} +} + +void EQStream::InboundQueuePush(EQApplicationPacket *p) +{ + MInboundQueue.lock(); + InboundQueue.push_back(p); + MInboundQueue.unlock(); +} + +EQApplicationPacket *EQStream::PopPacket() +{ +EQApplicationPacket *p=NULL; + + MInboundQueue.lock(); + if (InboundQueue.size()) { + p=InboundQueue.front(); + InboundQueue.pop_front(); + } + MInboundQueue.unlock(); + if(p) + p->setVersion(client_version); + return p; +} + +void EQStream::InboundQueueClear() +{ + MInboundQueue.lock(); + while(InboundQueue.size()){ + delete InboundQueue.front(); + InboundQueue.pop_front(); + } + MInboundQueue.unlock(); +} +void EQStream::EncryptPacket(uchar* data, int16 size){ + if(size>6){ + + } +} +bool EQStream::HasOutgoingData() +{ +bool flag; + + //once closed, we have nothing more to say + if(CheckClosed()) + return(false); + + MOutboundQueue.lock(); + flag=(!NonSequencedQueue.empty()); + if (!flag) { + flag = (!SequencedQueue.empty()); + } + MOutboundQueue.unlock(); + + if (!flag) { + MAcks.lock(); + flag= (NextAckToSend>LastAckSent); + MAcks.unlock(); + } + + if (!flag) { + MCombinedAppPacket.lock(); + flag=(CombinedAppPacket!=NULL); + MCombinedAppPacket.unlock(); + } + + return flag; +} + +void EQStream::OutboundQueueClear() +{ + MOutboundQueue.lock(); + while(NonSequencedQueue.size()) { + delete NonSequencedQueue.front(); + NonSequencedQueue.pop(); + } + while(SequencedQueue.size()) { + delete SequencedQueue.front(); + SequencedQueue.pop_front(); + } + MOutboundQueue.unlock(); +} + +void EQStream::Process(const unsigned char *buffer, const uint32 length) +{ + received_packets++; +static unsigned char newbuffer[2048]; +uint32 newlength=0; + +#ifdef LE_DEBUG +printf("ProcessBuffer:\n"); +DumpPacket(buffer, length); +#endif + + if (EQProtocolPacket::ValidateCRC(buffer,length,Key)) { + if (compressed) { + newlength=EQProtocolPacket::Decompress(buffer,length,newbuffer,2048); +#ifdef LE_DEBUG + printf("ProcessBufferDecompress:\n"); + DumpPacket(buffer, newlength); +#endif + } else { + memcpy(newbuffer,buffer,length); + newlength=length; + if (encoded) + EQProtocolPacket::ChatDecode(newbuffer,newlength-2,Key); + } + +#ifdef LE_DEBUG + printf("ResultProcessBuffer:\n"); + DumpPacket(buffer, newlength); +#endif + uint16 opcode=ntohs(*(const uint16 *)newbuffer); + //printf("Read packet: opcode %i newlength %u, newbuffer2len: %u, newbuffer3len: %u\n",opcode, newlength, newbuffer[2], newbuffer[3]); + if(opcode > 0 && opcode <= OP_OutOfSession) + { + if (buffer[1]!=0x01 && buffer[1]!=0x02 && buffer[1]!=0x1d) + newlength-=2; + + EQProtocolPacket p(newbuffer,newlength); + ProcessPacket(&p); + } + else + { + cout << "2Orig Packet: " << opcode << endl; + DumpPacket(newbuffer, newlength); + ProcessEmbeddedPacket(newbuffer, newlength, OP_Fragment); + } + ProcessQueue(); + } else { + cout << "Incoming packet failed checksum:" <(buffer),length,"CRC failed: "); + } +} + +long EQStream::GetMaxAckReceived() +{ + MAcks.lock(); + long l=MaxAckReceived; + MAcks.unlock(); + + return l; +} + +long EQStream::GetNextAckToSend() +{ + MAcks.lock(); + long l=NextAckToSend; + MAcks.unlock(); + + return l; +} + +long EQStream::GetLastAckSent() +{ + MAcks.lock(); + long l=LastAckSent; + MAcks.unlock(); + + return l; +} + +void EQStream::SetMaxAckReceived(uint32 seq) +{ + deque::iterator itr; + + MAcks.lock(); + MaxAckReceived=seq; + MAcks.unlock(); + MOutboundQueue.lock(); + if (long(seq) > LastSeqSent) + LastSeqSent=seq; + MResendQue.lock(); + EQProtocolPacket* packet = 0; + for(itr=resend_que.begin();itr!=resend_que.end();itr++){ + packet = *itr; + if(packet && packet->sequence <= seq){ + safe_delete(packet); + itr = resend_que.erase(itr); + if(itr == resend_que.end()) + break; + } + } + MResendQue.unlock(); + MOutboundQueue.unlock(); +} + +void EQStream::SetNextAckToSend(uint32 seq) +{ + MAcks.lock(); + NextAckToSend=seq; + MAcks.unlock(); +} + +void EQStream::SetLastAckSent(uint32 seq) +{ + MAcks.lock(); + LastAckSent=seq; + MAcks.unlock(); +} + +void EQStream::SetLastSeqSent(uint32 seq) +{ + MOutboundQueue.lock(); + LastSeqSent=seq; + MOutboundQueue.unlock(); +} + +void EQStream::SetStreamType(EQStreamType type) +{ + StreamType=type; + switch (StreamType) { + case LoginStream: + app_opcode_size=1; + compressed=false; + encoded=false; + break; + case EQ2Stream: + app_opcode_size=2; + compressed=false; + encoded=false; + break; + case ChatOrMailStream: + case ChatStream: + case MailStream: + app_opcode_size=1; + compressed=false; + encoded=true; + break; + case ZoneStream: + case WorldStream: + default: + app_opcode_size=2; + compressed=true; + encoded=false; + break; + } +} + +void EQStream::ProcessQueue() +{ + if (OutOfOrderpackets.empty()) { + return; + } + + EQProtocolPacket* qp = NULL; + while ((qp = RemoveQueue(NextInSeq)) != NULL) { + //_log(NET__DEBUG, _L "Processing Queued Packet: Seq=%d" __L, NextInSeq); + ProcessPacket(qp); + delete qp; + //_log(NET__APP_TRACE, _L "OP_Packet Queue size=%d" __L, PacketQueue.size()); + } +} + +EQProtocolPacket* EQStream::RemoveQueue(uint16 seq) +{ + map::iterator itr; + EQProtocolPacket* qp = NULL; + if ((itr = OutOfOrderpackets.find(seq)) != OutOfOrderpackets.end()) { + qp = itr->second; + OutOfOrderpackets.erase(itr); + //_log(NET__APP_TRACE, _L "OP_Packet Queue size=%d" __L, PacketQueue.size()); + } + return qp; +} + +void EQStream::Decay() +{ + MRate.lock(); + uint32 rate=DecayRate; + MRate.unlock(); + if (BytesWritten>0) { + BytesWritten-=rate; + if (BytesWritten<0) + BytesWritten=0; + } + + int count = 0; + MOutboundQueue.lock(); + for (auto sitr = SequencedQueue.begin(); sitr != SequencedQueue.end(); ++sitr, count++) { + if (!(*sitr)->acked && (*sitr)->sent_time > 0 && ((*sitr)->sent_time + retransmittimeout) < Timer::GetCurrentTime2()) { + (*sitr)->sent_time = 0; + LogWrite(PACKET__DEBUG, 9, "Packet", "Timeout exceeded for seq %u. Flagging packet for retransmission", SequencedBase + count); + } + } + MOutboundQueue.unlock(); +} + +void EQStream::AdjustRates(uint32 average_delta) +{ + if (average_delta && (average_delta <= AVERAGE_DELTA_MAX)) { + MRate.lock(); + AverageDelta = average_delta; + RateThreshold = RATEBASE / average_delta; + DecayRate = DECAYBASE / average_delta; + if (BytesWritten > RateThreshold) + BytesWritten = RateThreshold + DecayRate; + MRate.unlock(); + } + else { + AverageDelta = AVERAGE_DELTA_MAX; + } +} diff --git a/old/EQStream.h b/old/EQStream.h new file mode 100644 index 0000000..3ce45a8 --- /dev/null +++ b/old/EQStream.h @@ -0,0 +1,375 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#ifndef _EQPROTOCOL_H +#define _EQPROTOCOL_H + +#include +#include +#include +#include + +#include +#include +#ifndef WIN32 +#include +#endif +#include "EQPacket.h" +#include "Mutex.h" +#include "opcodemgr.h" +#include "misc.h" +#include "Condition.h" +#include "Crypto.h" +#include "zlib.h" +#include "timer.h" +#ifdef WRITE_PACKETS +#include +#endif + +using namespace std; + +typedef enum { + ESTABLISHED, + WAIT_CLOSE, + CLOSING, + DISCONNECTING, + CLOSED +} EQStreamState; + +#define FLAG_COMPRESSED 0x01 +#define FLAG_ENCODED 0x04 + +#define RATEBASE 1048576 // 1 MB +#define DECAYBASE 78642 // RATEBASE/10 + +#ifndef RETRANSMIT_TIMEOUT_MULT +#define RETRANSMIT_TIMEOUT_MULT 3.0 +#endif + +#ifndef RETRANSMIT_TIMEOUT_MAX +#define RETRANSMIT_TIMEOUT_MAX 5000 +#endif + +#ifndef AVERAGE_DELTA_MAX +#define AVERAGE_DELTA_MAX 2500 +#endif + +#pragma pack(1) +struct SessionRequest { + uint32 UnknownA; + uint32 Session; + uint32 MaxLength; +}; + +struct SessionResponse { + uint32 Session; + uint32 Key; + uint8 UnknownA; + uint8 Format; + uint8 UnknownB; + uint32 MaxLength; + uint32 UnknownD; +}; + +//Deltas are in ms, representing round trip times +struct ClientSessionStats { +/*000*/ uint16 RequestID; +/*002*/ uint32 last_local_delta; +/*006*/ uint32 average_delta; +/*010*/ uint32 low_delta; +/*014*/ uint32 high_delta; +/*018*/ uint32 last_remote_delta; +/*022*/ uint64 packets_sent; +/*030*/ uint64 packets_recieved; +/*038*/ +}; + +struct ServerSessionStats { + uint16 RequestID; + uint32 current_time; + uint32 unknown1; + uint32 received_packets; + uint32 unknown2; + uint32 sent_packets; + uint32 unknown3; + uint32 sent_packets2; + uint32 unknown4; + uint32 received_packets2; +}; + +#pragma pack() + +class OpcodeManager; +extern OpcodeManager *EQNetworkOpcodeManager; + +class EQStreamFactory; + +typedef enum { + UnknownStream=0, + LoginStream, + WorldStream, + ZoneStream, + ChatOrMailStream, + ChatStream, + MailStream, + EQ2Stream, +} EQStreamType; + +class EQStream { + protected: + typedef enum { + SeqPast, + SeqInOrder, + SeqFuture + } SeqOrder; + + uint32 received_packets; + uint32 sent_packets; + uint32 remote_ip; + uint16 remote_port; + uint8 buffer[8192]; + unsigned char *oversize_buffer; + uint32 oversize_offset,oversize_length; + unsigned char *rogue_buffer; + uint32 roguebuf_offset,roguebuf_size; + uint8 app_opcode_size; + EQStreamType StreamType; + bool compressed,encoded; + + unsigned char write_buffer[2048]; + + uint32 retransmittimer; + uint32 retransmittimeout; + //uint32 buffer_len; + + uint16 sessionAttempts; + uint16 reconnectAttempt; + bool streamactive; + + uint32 Session, Key; + uint16 NextInSeq; + uint16 NextOutSeq; + uint16 SequencedBase; //the sequence number of SequencedQueue[0] + uint32 MaxLen; + uint16 MaxSends; + int8 timeout_delays; + + uint8 active_users; //how many things are actively using this + Mutex MInUse; + +#ifdef WRITE_PACKETS + FILE* write_packets = NULL; + char GetChar(uchar in); + void WriteToFile(char* pFormat, ...); + void WritePackets(const char* opcodeName, uchar* data, int32 size, bool outgoing); + void WritePackets(EQ2Packet* app, bool outgoing); + Mutex MWritePackets; +#endif + + EQStreamState State; + Mutex MState; + + uint32 LastPacket; + Mutex MVarlock; + + EQApplicationPacket* CombinedAppPacket; + Mutex MCombinedAppPacket; + + long LastSeqSent; + Mutex MLastSeqSent; + void SetLastSeqSent(uint32); + + // Ack sequence tracking. + long MaxAckReceived,NextAckToSend,LastAckSent; + long GetMaxAckReceived(); + long GetNextAckToSend(); + long GetLastAckSent(); + void SetMaxAckReceived(uint32 seq); + void SetNextAckToSend(uint32); + void SetLastAckSent(uint32); + + Mutex MAcks; + + // Packets waiting to be sent + queue NonSequencedQueue; + deque SequencedQueue; + map OutOfOrderpackets; + Mutex MOutboundQueue; + + // Packes waiting to be processed + deque InboundQueue; + Mutex MInboundQueue; + + static uint16 MaxWindowSize; + + sint32 BytesWritten; + + Mutex MRate; + sint32 RateThreshold; + sint32 DecayRate; + uint32 AverageDelta; + + EQStreamFactory *Factory; + + public: + Mutex MCombineQueueLock; + bool CheckCombineQueue(); + deque combine_queue; + Timer* combine_timer; + + Crypto* crypto; + int8 EQ2_Compress(EQ2Packet* app, int8 offset = 3); + z_stream stream; + uchar* stream_buffer; + int32 stream_buffer_size; + bool eq2_compressed; + int8 compressed_offset; + int16 client_version; + int16 GetClientVersion(){ return client_version; } + void SetClientVersion(int16 version){ client_version = version; } + void ResetSessionAttempts() { reconnectAttempt = 0; } + bool HasSessionAttempts() { return reconnectAttempt>0; } + EQStream() { init(); remote_ip = 0; remote_port = 0; State = CLOSED; StreamType = UnknownStream; compressed = true; + encoded = false; app_opcode_size = 2;} + EQStream(sockaddr_in addr); + virtual ~EQStream() { + MOutboundQueue.lock(); + SetState(CLOSED); + MOutboundQueue.unlock(); + RemoveData(); + safe_delete(crypto); + safe_delete(combine_timer); + safe_delete(resend_que_timer); + safe_delete_array(oversize_buffer); + safe_delete_array(rogue_buffer); + deque::iterator cmb; + MCombineQueueLock.lock(); + for (cmb = combine_queue.begin(); cmb != combine_queue.end(); cmb++){ + safe_delete(*cmb); + } + MCombineQueueLock.unlock(); + deflateEnd(&stream); + map::iterator oop; + for (oop = OutOfOrderpackets.begin(); oop != OutOfOrderpackets.end(); oop++){ + safe_delete(oop->second); + } +#ifdef WRITE_PACKETS + if (write_packets) + fclose(write_packets); +#endif + } + inline void SetFactory(EQStreamFactory *f) { Factory=f; } + void init(bool resetSession = true); + void SetMaxLen(uint32 length) { MaxLen=length; } + int8 getTimeoutDelays(){ return timeout_delays; } + void addTimeoutDelay(){ timeout_delays++; } + void EQ2QueuePacket(EQ2Packet* app, bool attempted_combine = false); + void PreparePacket(EQ2Packet* app, int8 offset = 0); + void UnPreparePacket(EQ2Packet* app); + void EncryptPacket(EQ2Packet* app, int8 compress_offset, int8 offset); + void FlushCombinedPacket(); + void SendPacket(EQApplicationPacket *p); + void QueuePacket(EQProtocolPacket *p); + void SendPacket(EQProtocolPacket *p); + vector convert(EQApplicationPacket *p); + void NonSequencedPush(EQProtocolPacket *p); + void SequencedPush(EQProtocolPacket *p); + + Mutex MResendQue; + Mutex MCompressData; + dequeresend_que; + void CheckResend(int eq_fd); + + void AckPackets(uint16 seq); + void Write(int eq_fd); + + void SetActive(bool val) { streamactive = val; } + + void WritePacket(int fd,EQProtocolPacket *p); + + void EncryptPacket(uchar* data, int16 size); + uint32 GetKey() { return Key; } + void SetKey(uint32 k) { Key=k; } + void SetSession(uint32 s) { Session=s; } + void SetLastPacketTime(uint32 t) {LastPacket=t;} + + void Process(const unsigned char *data, const uint32 length); + void ProcessPacket(EQProtocolPacket *p, EQProtocolPacket* lastp=NULL); + + bool ProcessEmbeddedPacket(uchar* pBuffer, uint16 length, int8 opcode = OP_Packet); + bool HandleEmbeddedPacket(EQProtocolPacket *p, int16 offset = 2, int16 length = 0); + + EQProtocolPacket * ProcessEncryptedPacket(EQProtocolPacket *p); + EQProtocolPacket * ProcessEncryptedData(uchar* data, int32 size, int16 opcode); + + virtual void DispatchPacket(EQApplicationPacket *p) { p->DumpRaw(); } + + void SendSessionResponse(); + void SendSessionRequest(); + void SendDisconnect(bool setstate = true); + void SendAck(uint16 seq); + void SendOutOfOrderAck(uint16 seq); + + bool CheckTimeout(uint32 now, uint32 timeout=30) { return (LastPacket && (now-LastPacket) > timeout); } + bool Stale(uint32 now, uint32 timeout=30) { return (LastPacket && (now-LastPacket) > timeout); } + + void InboundQueuePush(EQApplicationPacket *p); + EQApplicationPacket *PopPacket(); // InboundQueuePop + void InboundQueueClear(); + + void OutboundQueueClear(); + bool HasOutgoingData(); + void SendKeyRequest(); + int16 processRSAKey(EQProtocolPacket *p, uint16 subpacket_length = 0); + void RemoveData() { InboundQueueClear(); OutboundQueueClear(); if (CombinedAppPacket) delete CombinedAppPacket; } + + // + inline bool IsInUse() { bool flag; MInUse.lock(); flag=(active_users>0); MInUse.unlock(); return flag; } + inline void PutInUse() { MInUse.lock(); active_users++; MInUse.unlock(); } + inline void ReleaseFromUse() { MInUse.lock(); if(active_users > 0) active_users--; MInUse.unlock(); } + + static SeqOrder CompareSequence(uint16 expected_seq, uint16 seq); + + inline EQStreamState GetState() { return State; } + inline void SetState(EQStreamState state) { MState.lock(); State = state; MState.unlock(); } + + inline uint32 GetRemoteIP() { return remote_ip; } + inline uint32 GetrIP() { return remote_ip; } + inline uint16 GetRemotePort() { return remote_port; } + inline uint16 GetrPort() { return remote_port; } + + + static EQProtocolPacket *Read(int eq_fd, sockaddr_in *from); + + void Close() { SendDisconnect(); } + bool CheckActive() { return (GetState()==ESTABLISHED); } + bool CheckClosed() { return GetState()==CLOSED; } + void SetOpcodeSize(uint8 s) { app_opcode_size = s; } + void SetStreamType(EQStreamType t); + inline const EQStreamType GetStreamType() const { return StreamType; } + + void ProcessQueue(); + EQProtocolPacket* RemoveQueue(uint16 seq); + + void Decay(); + void AdjustRates(uint32 average_delta); + Timer* resend_que_timer; +}; + +#endif diff --git a/old/EQStreamFactory.cpp b/old/EQStreamFactory.cpp new file mode 100644 index 0000000..965865e --- /dev/null +++ b/old/EQStreamFactory.cpp @@ -0,0 +1,444 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ + +#include "EQStreamFactory.h" +#include "Log.h" + +#ifdef WIN32 + #include + #include + #include + #include + #include +#else + #include + #include + #include + #include + #include + #include +#endif +#include +#include +#include +#include "op_codes.h" +#include "EQStream.h" +#include "packet_dump.h" +#ifdef WORLD + #include "../WorldServer/client.h" +#endif +using namespace std; + +#ifdef WORLD + extern ClientList client_list; +#endif +ThreadReturnType EQStreamFactoryReaderLoop(void *eqfs) +{ + if(eqfs){ + EQStreamFactory *fs=(EQStreamFactory *)eqfs; + fs->ReaderLoop(); + } + THREAD_RETURN(NULL); +} + +ThreadReturnType EQStreamFactoryWriterLoop(void *eqfs) +{ + if(eqfs){ + EQStreamFactory *fs=(EQStreamFactory *)eqfs; + fs->WriterLoop(); + } + THREAD_RETURN(NULL); +} + +ThreadReturnType EQStreamFactoryCombinePacketLoop(void *eqfs) +{ + if(eqfs){ + EQStreamFactory *fs=(EQStreamFactory *)eqfs; + fs->CombinePacketLoop(); + } + THREAD_RETURN(NULL); +} + +EQStreamFactory::EQStreamFactory(EQStreamType type, int port) +{ + StreamType=type; + Port=port; + listen_ip_address = 0; +} + +void EQStreamFactory::Close() +{ + CheckTimeout(true); + Stop(); + if (sock != -1) { +#ifdef WIN32 + closesocket(sock); +#else + close(sock); +#endif + sock = -1; + } +} +bool EQStreamFactory::Open() +{ +struct sockaddr_in address; +#ifndef WIN32 + pthread_t t1, t2, t3; +#endif + /* Setup internet address information. + This is used with the bind() call */ + memset((char *) &address, 0, sizeof(address)); + address.sin_family = AF_INET; + address.sin_port = htons(Port); +#if defined(LOGIN) || defined(MINILOGIN) + if(listen_ip_address) + address.sin_addr.s_addr = inet_addr(listen_ip_address); + else + address.sin_addr.s_addr = htonl(INADDR_ANY); +#else + address.sin_addr.s_addr = htonl(INADDR_ANY); +#endif + /* Setting up UDP port for new clients */ + sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock < 0) { + return false; + } + + if (::bind(sock, (struct sockaddr *) &address, sizeof(address)) < 0) { + //close(sock); + sock=-1; + return false; + } + #ifdef WIN32 + unsigned long nonblock = 1; + ioctlsocket(sock, FIONBIO, &nonblock); + #else + fcntl(sock, F_SETFL, O_NONBLOCK); + #endif + //moved these because on windows the output was delayed and causing the console window to look bad +#ifdef LOGIN + LogWrite(LOGIN__DEBUG, 0, "Login", "Starting factory Reader"); + LogWrite(LOGIN__DEBUG, 0, "Login", "Starting factory Writer"); +#elif WORLD + LogWrite(WORLD__DEBUG, 0, "World", "Starting factory Reader"); + LogWrite(WORLD__DEBUG, 0, "World", "Starting factory Writer"); +#endif + #ifdef WIN32 + _beginthread(EQStreamFactoryReaderLoop,0, this); + _beginthread(EQStreamFactoryWriterLoop,0, this); + _beginthread(EQStreamFactoryCombinePacketLoop,0, this); + #else + pthread_create(&t1,NULL,EQStreamFactoryReaderLoop,this); + pthread_create(&t2,NULL,EQStreamFactoryWriterLoop,this); + pthread_create(&t3,NULL,EQStreamFactoryCombinePacketLoop,this); + pthread_detach(t1); + pthread_detach(t2); + pthread_detach(t3); + #endif + return true; +} + +EQStream *EQStreamFactory::Pop() +{ + if (!NewStreams.size()) + return NULL; + +EQStream *s=NULL; + //cout << "Pop():Locking MNewStreams" << endl; + MNewStreams.lock(); + if (NewStreams.size()) { + s=NewStreams.front(); + NewStreams.pop(); + s->PutInUse(); + } + MNewStreams.unlock(); + //cout << "Pop(): Unlocking MNewStreams" << endl; + + return s; +} + +void EQStreamFactory::Push(EQStream *s) +{ + //cout << "Push():Locking MNewStreams" << endl; + MNewStreams.lock(); + NewStreams.push(s); + MNewStreams.unlock(); + //cout << "Push(): Unlocking MNewStreams" << endl; +} + +void EQStreamFactory::ReaderLoop() +{ +fd_set readset; +map::iterator stream_itr; +int num; +int length; +unsigned char buffer[2048]; +sockaddr_in from; +int socklen=sizeof(sockaddr_in); +timeval sleep_time; + ReaderRunning=true; + while(sock!=-1) { + MReaderRunning.lock(); + if (!ReaderRunning) + break; + MReaderRunning.unlock(); + + FD_ZERO(&readset); + FD_SET(sock,&readset); + + sleep_time.tv_sec=30; + sleep_time.tv_usec=0; + if ((num=select(sock+1,&readset,NULL,NULL,&sleep_time))<0) { + // What do we wanna do? + } else if (num==0) + continue; + + if (FD_ISSET(sock,&readset)) { +#ifdef WIN32 + if ((length=recvfrom(sock,(char*)buffer,sizeof(buffer),0,(struct sockaddr*)&from,(int *)&socklen))<2) +#else + if ((length=recvfrom(sock,buffer,2048,0,(struct sockaddr *)&from,(socklen_t *)&socklen))<2) +#endif + { + // What do we wanna do? + } else { + char temp[25]; + sprintf(temp,"%u.%d",ntohl(from.sin_addr.s_addr),ntohs(from.sin_port)); + MStreams.lock(); + if ((stream_itr=Streams.find(temp))==Streams.end() || buffer[1]==OP_SessionRequest) { + MStreams.unlock(); + if (buffer[1]==OP_SessionRequest) { + if(stream_itr != Streams.end() && stream_itr->second) + stream_itr->second->SetState(CLOSED); + EQStream *s=new EQStream(from); + s->SetFactory(this); + s->SetStreamType(StreamType); + Streams[temp]=s; + WriterWork.Signal(); + Push(s); + s->Process(buffer,length); + s->SetLastPacketTime(Timer::GetCurrentTime2()); + } + } else { + EQStream *curstream = stream_itr->second; + //dont bother processing incoming packets for closed connections + if(curstream->CheckClosed()) + curstream = NULL; + else + curstream->PutInUse(); + MStreams.unlock(); + + if(curstream) { + curstream->Process(buffer,length); + curstream->SetLastPacketTime(Timer::GetCurrentTime2()); + curstream->ReleaseFromUse(); + } + } + } + } + } +} + +void EQStreamFactory::CheckTimeout(bool remove_all) +{ + //lock streams the entire time were checking timeouts, it should be fast. + MStreams.lock(); + + unsigned long now=Timer::GetCurrentTime2(); + map::iterator stream_itr; + + for(stream_itr=Streams.begin();stream_itr!=Streams.end();) { + EQStream *s = stream_itr->second; + EQStreamState state = s->GetState(); + + if (state==CLOSING && !s->HasOutgoingData()) { + stream_itr->second->SetState(CLOSED); + state = CLOSED; + } else if (s->CheckTimeout(now, STREAM_TIMEOUT)) { + const char* stateString; + switch (state){ + case ESTABLISHED: + stateString = "Established"; + break; + case CLOSING: + stateString = "Closing"; + break; + case CLOSED: + stateString = "Closed"; + break; + case WAIT_CLOSE: + stateString = "Wait-Close"; + break; + default: + stateString = "Unknown"; + break; + } + LogWrite(WORLD__DEBUG, 0, "World", "Timeout up!, state=%s (%u)", stateString, state); + if (state==ESTABLISHED) { + s->Close(); + } + else if (state == WAIT_CLOSE) { + s->SetState(CLOSING); + state = CLOSING; + } + else if (state == CLOSING) { + //if we time out in the closing state, just give up + s->SetState(CLOSED); + state = CLOSED; + } + } + //not part of the else so we check it right away on state change + if (remove_all || state==CLOSED) { + if (!remove_all && s->getTimeoutDelays()<2) { + s->addTimeoutDelay(); + //give it a little time for everybody to finish with it + } else { + //everybody is done, we can delete it now + +#ifdef LOGIN + LogWrite(LOGIN__DEBUG, 0, "Login", "Removing connection..."); +#else + LogWrite(WORLD__DEBUG, 0, "World", "Removing connection..."); +#endif + map::iterator temp=stream_itr; + stream_itr++; + //let whoever has the stream outside delete it + #ifdef WORLD + client_list.RemoveConnection(temp->second); + #endif + EQStream* stream = temp->second; + Streams.erase(temp); + delete stream; + continue; + } + } + + stream_itr++; + } + MStreams.unlock(); +} + +void EQStreamFactory::CombinePacketLoop(){ + deque combine_que; + CombinePacketRunning = true; + bool packets_waiting = false; + while(sock!=-1) { + if (!CombinePacketRunning) + break; + MStreams.lock(); + map::iterator stream_itr; + for(stream_itr=Streams.begin();stream_itr!=Streams.end();stream_itr++) { + if(!stream_itr->second){ + continue; + } + if(stream_itr->second->combine_timer && stream_itr->second->combine_timer->Check()) + combine_que.push_back(stream_itr->second); + } + EQStream* stream = 0; + packets_waiting = false; + while(combine_que.size()){ + stream = combine_que.front(); + if(stream->CheckActive()){ + if(!stream->CheckCombineQueue()) + packets_waiting = true; + } + combine_que.pop_front(); + } + MStreams.unlock(); + if(!packets_waiting) + Sleep(25); + + Sleep(1); + } +} + +void EQStreamFactory::WriterLoop() +{ +map::iterator stream_itr; +vector wants_write; +vector::iterator cur,end; +deque resend_que; +bool decay=false; +uint32 stream_count; + +Timer DecayTimer(20); + + WriterRunning=true; + DecayTimer.Enable(); + while(sock!=-1) { + Timer::SetCurrentTime(); + //if (!havework) { + //WriterWork.Wait(); + //} + MWriterRunning.lock(); + if (!WriterRunning) + break; + MWriterRunning.unlock(); + + wants_write.clear(); + + decay=DecayTimer.Check(); + + //copy streams into a seperate list so we dont have to keep + //MStreams locked while we are writting + MStreams.lock(); + for(stream_itr=Streams.begin();stream_itr!=Streams.end();stream_itr++) { + // If it's time to decay the bytes sent, then let's do it before we try to write + if(!stream_itr->second){ + Streams.erase(stream_itr); + break; + } + if (decay) + stream_itr->second->Decay(); + + if (stream_itr->second->HasOutgoingData()) { + stream_itr->second->PutInUse(); + wants_write.push_back(stream_itr->second); + } + if(stream_itr->second->resend_que_timer->Check()) + resend_que.push_back(stream_itr->second); + } + MStreams.unlock(); + + //do the actual writes + cur = wants_write.begin(); + end = wants_write.end(); + for(; cur != end; cur++) { + (*cur)->Write(sock); + (*cur)->ReleaseFromUse(); + } + while(resend_que.size()){ + resend_que.front()->CheckResend(sock); + resend_que.pop_front(); + } + Sleep(10); + + MStreams.lock(); + stream_count=Streams.size(); + MStreams.unlock(); + if (!stream_count) { + //cout << "No streams, waiting on condition" << endl; + WriterWork.Wait(); + //cout << "Awake from condition, must have a stream now" << endl; + } + } +} + + diff --git a/old/EQStreamFactory.h b/old/EQStreamFactory.h new file mode 100644 index 0000000..9dce3c7 --- /dev/null +++ b/old/EQStreamFactory.h @@ -0,0 +1,86 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#ifndef _EQSTREAMFACTORY_H + +#define _EQSTREAMFACTORY_H + +#include +#include +#include "../common/EQStream.h" +#include "../common/Condition.h" +#include "../common/opcodemgr.h" +#include "../common/timer.h" + +#define STREAM_TIMEOUT 45000 //in ms + +class EQStreamFactory { + private: + int sock; + int Port; + + bool ReaderRunning; + Mutex MReaderRunning; + bool WriterRunning; + Mutex MWriterRunning; + bool CombinePacketRunning; + Mutex MCombinePacketRunning; + + Condition WriterWork; + + EQStreamType StreamType; + + queue NewStreams; + Mutex MNewStreams; + + map Streams; + Mutex MStreams; + + + + Timer *DecayTimer; + + public: + char* listen_ip_address; + void CheckTimeout(bool remove_all = false); + EQStreamFactory(EQStreamType type) { ReaderRunning=false; WriterRunning=false; StreamType=type; } + EQStreamFactory(EQStreamType type, int port); + ~EQStreamFactory(){ + safe_delete_array(listen_ip_address); + } + + EQStream *Pop(); + void Push(EQStream *s); + + bool loadPublicKey(); + bool Open(); + bool Open(unsigned long port) { Port=port; return Open(); } + void Close(); + void ReaderLoop(); + void WriterLoop(); + void CombinePacketLoop(); + void Stop() { StopReader(); StopWriter(); StopCombinePacket(); } + void StopReader() { MReaderRunning.lock(); ReaderRunning=false; MReaderRunning.unlock(); } + void StopWriter() { MWriterRunning.lock(); WriterRunning=false; MWriterRunning.unlock(); WriterWork.Signal(); } + void StopCombinePacket() { MCombinePacketRunning.lock(); CombinePacketRunning=false; MCombinePacketRunning.unlock(); } + void SignalWriter() { WriterWork.Signal(); } + +}; + +#endif diff --git a/old/GlobalHeaders.h b/old/GlobalHeaders.h new file mode 100644 index 0000000..98b7458 --- /dev/null +++ b/old/GlobalHeaders.h @@ -0,0 +1,58 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ + +//Character Creation Replies, put in globals so name filter can return proper responses +#define UNKNOWNERROR_REPLY 0 +#define CREATESUCCESS_REPLY 1 +#define NOSERVERSAVAIL_REPLY 2 +#define CREATEPENDING_REPLY 3 +#define MAXCHARSALLOWED_REPLY 4 +#define INVALIDRACE_REPLY 5 +#define INVALIDCITY_REPLY 6 +#define INVALIDCLASS_REPLY 7 +#define INVALIDGENDER_REPLY 8 +#define INVALIDFIRST_LVL_REPLY 9 +#define BADNAMELENGTH_REPLY 10 +#define NAMEINVALID_REPLY 11 +#define NAMEFILTER_REPLY 12 // name_filter reply (bad word or blocked words) +#define NAMETAKEN_REPLY 13 +#define OVERLOADEDSERVER_REPLY 14 +#define UNKNOWNERROR_REPLY2 15 +#define INVALIDFEATURES1_REPLY 16 +#define INVALIDFEATURES2_REPLY 17 +#define INVALIDRACE_APPEARANCE_REPLY 18 + +#define PLAY_ERROR_PROBLEM 0 +#define PLAY_ERROR_ZONE_DOWN 4 +#define PLAY_ERROR_CHAR_NOT_LOADED 5 +#define PLAY_ERROR_CHAR_NOT_FOUND 6 +#define PLAY_ERROR_ACCOUNT_IN_USE 7 +#define PLAY_ERROR_SERVER_TIMEOUT 8 +#define PLAY_ERROR_SERVER_SHUTDOWN 9 +#define PLAY_ERROR_LOADING_ERROR 10 +#define PLAY_ERROR_EXCHANGE_SERVER 11 +#define PLAY_ERROR_REGION_SERVER 12 +#define PLAY_ERROR_CLASS_INVALID 13 +#define PLAY_ERROR_TOO_MANY_CHARACTERS 14 +#define PLAY_ERROR_EOF_EXP_NOT_FOUND 15 +#define PLAY_ERROR_UNKNOWN_RESPONSE 16 +#define PLAY_ERROR_UNKNOWN 17 +#define PLAY_ERROR_ACCOUNT_BANNED 18 +#define PLAY_ERROR_PROHIBITED 19 diff --git a/old/JsonParser.cpp b/old/JsonParser.cpp new file mode 100644 index 0000000..cca065a --- /dev/null +++ b/old/JsonParser.cpp @@ -0,0 +1,97 @@ +#include "JsonParser.h" + +JsonParser::JsonParser(const std::string &filename) { + is_loaded = false; + try { + boost::property_tree::read_json(filename, pt); + parseTree(pt, ""); + is_loaded = true; + } catch (const boost::property_tree::json_parser_error &e) { + std::cerr << "Error reading JSON file: " << e.what() << std::endl; + } +} + +bool JsonParser::convertStringToUnsignedChar(const std::string& str, unsigned char& result) { + unsigned long ul; + try { + ul = std::stoul(str); + } catch (const std::invalid_argument&) { + return false; // Not a valid number + } catch (const std::out_of_range&) { + return false; // Number is too large for unsigned long + } + + if (ul > std::numeric_limits::max()) { + return false; // Number is too large for unsigned short + } + + result = static_cast(ul); + return true; +} + +bool JsonParser::convertStringToUnsignedShort(const std::string& str, unsigned short& result) { + unsigned long ul; + try { + ul = std::stoul(str); + } catch (const std::invalid_argument&) { + return false; // Not a valid number + } catch (const std::out_of_range&) { + return false; // Number is too large for unsigned long + } + + if (ul > std::numeric_limits::max()) { + return false; // Number is too large for unsigned short + } + + result = static_cast(ul); + return true; +} + +bool JsonParser::convertStringToUnsignedInt(const std::string& str, unsigned int& result) { + unsigned long ul; + try { + ul = std::stoul(str); + } catch (const std::invalid_argument&) { + return false; // Not a valid number + } catch (const std::out_of_range&) { + return false; // Number is too large for unsigned long + } + + if (ul > std::numeric_limits::max()) { + return false; // Number is too large for unsigned short + } + + result = static_cast(ul); + return true; +} + +bool JsonParser::convertStringToUnsignedLong(const std::string& str, unsigned long& result) { + unsigned long ul; + try { + ul = std::stoul(str); + } catch (const std::invalid_argument&) { + return false; // Not a valid number + } catch (const std::out_of_range&) { + return false; // Number is too large for unsigned long + } + + if (ul > std::numeric_limits::max()) { + return false; // Number is too large for unsigned short + } + + result = ul; + return true; +} + +void JsonParser::parseTree(const boost::property_tree::ptree &tree, const std::string &path) { + for (const auto &node : tree) { + std::string currentPath = path.empty() ? node.first : path + "." + node.first; + if (node.second.empty()) { + std::string name = currentPath; + boost::algorithm::to_lower(name); + values[name] = node.second.get_value(); + } else { + parseTree(node.second, currentPath); + } + } +} \ No newline at end of file diff --git a/old/JsonParser.h b/old/JsonParser.h new file mode 100644 index 0000000..f92b07d --- /dev/null +++ b/old/JsonParser.h @@ -0,0 +1,33 @@ + +#include +#include +#include +#include +#include +#include +#include + +class JsonParser { +public: + JsonParser(const std::string &filename); + + std::string getValue(const std::string &path) const { + auto it = values.find(path); + if (it != values.end()) { + return it->second; + } + return ""; + } + + static bool convertStringToUnsignedChar(const std::string& str, unsigned char& result); + static bool convertStringToUnsignedShort(const std::string& str, unsigned short& result); + static bool convertStringToUnsignedInt(const std::string& str, unsigned int& result); + static bool convertStringToUnsignedLong(const std::string& str, unsigned long& result); + bool IsLoaded() { return is_loaded; } +private: + boost::property_tree::ptree pt; + std::map values; + + void parseTree(const boost::property_tree::ptree &tree, const std::string &path); + bool is_loaded; +}; diff --git a/old/Log.cpp b/old/Log.cpp new file mode 100644 index 0000000..b74be71 --- /dev/null +++ b/old/Log.cpp @@ -0,0 +1,615 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#include "Log.h" +#include "xmlParser.h" +#include "types.h" +#include +#include +#include +#include +#include +#include +#include +#include "../WorldServer/World.h" +#include "../WorldServer/client.h" +#include "../WorldServer/zoneserver.h" + +extern ZoneList zone_list; + +#ifdef _WIN32 + #include + #ifndef snprintf + #define snprintf sprintf_s + #endif +#include + #include +#else +#endif + +#define LOG_CATEGORY(category) #category, +const char *log_category_names[NUMBER_OF_LOG_CATEGORIES] = { + #include "LogTypes.h" +}; + +#define LOG_TYPE(category, type, level, color, enabled, logfile, console, client, str) { level, color, enabled, logfile, console, client, LOG_ ##category, #category "__" #type, ( strlen(str)>0 ) ? str : #category "__" #type }, +static LogTypeStatus real_log_type_info[NUMBER_OF_LOG_TYPES+1] = +{ + #include "LogTypes.h" + { 0, 0, false, false, false, false, NUMBER_OF_LOG_CATEGORIES, "BAD TYPE", "Bad Name" } /* dummy trailing record */ +}; + +LogTypeStatus *log_type_info = real_log_type_info; + +//make these rules? +#define LOG_CYCLE 100 //milliseconds between each batch of log writes +#define LOGS_PER_CYCLE 50 //amount of logs to write per cycle + +#define LOG_DIR "logs" + +#if defined LOGIN +#define EXE_NAME "login" +#elif defined WORLD +#define EXE_NAME "world" +#elif defined PARSER +#define EXE_NAME "parser" +#elif defined PATCHER +#define EXE_NAME "patcher" +#else +#define EXE_NAME "whatprogyourunning" +#endif + +#define DATE_MAX 8 +#define LOG_NAME_MAX 32 + +struct logq_t { + LogType log_type; + char date[DATE_MAX + 1]; + char name[LOG_NAME_MAX + 1]; + char *text; + struct logq_t *next; + struct logq_t *prev; +}; + +//doubly linked list of logs +static logq_t head; +static logq_t tail; +static int num_logqs = 0; +static Mutex mlogqs; + +//loop until.... +static bool looping = false; + +//because our code has LogWrite's before main(), make sure any of those do the +//call to LogStart if it hasn't been called already... +static bool start_called = false; + +static void SetConsoleColor(int color) { +#ifdef _WIN32 + HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); + + if (handle == NULL || handle == INVALID_HANDLE_VALUE) + return; +#endif + + switch (color) { + case FOREGROUND_WHITE: + case FOREGROUND_WHITE_BOLD: + case FOREGROUND_RED: + case FOREGROUND_RED_BOLD: + case FOREGROUND_GREEN: + case FOREGROUND_GREEN_BOLD: + case FOREGROUND_BLUE: + case FOREGROUND_BLUE_BOLD: + case FOREGROUND_YELLOW: + case FOREGROUND_YELLOW_BOLD: + case FOREGROUND_CYAN: + case FOREGROUND_CYAN_BOLD: + case FOREGROUND_MAGENTA: + case FOREGROUND_MAGENTA_BOLD: +#ifdef _WIN32 + SetConsoleTextAttribute(handle, color); +#else + printf("\033[%i;%i;40m", color > 100 ? 1 : 0, color > 100 ? color - 100 : color); +#endif + break; + default: +#ifdef _WIN32 + SetConsoleTextAttribute(handle, FOREGROUND_WHITE_BOLD); +#else + printf("\033[0;37;40m"); +#endif + break; + } +} + +static FILE * OpenLogFile() { + char file[FILENAME_MAX + 1]; + struct stat st; + struct tm *tm; + time_t now; + FILE *f; + + now = time(NULL); + tm = localtime(&now); + + //make sure the logs directory exists + if (stat(LOG_DIR, &st) != 0) { +#ifdef _WIN32 + if (!CreateDirectory(LOG_DIR, NULL)) { + fprintf(stderr, "Unable to create directory '%s'\n", LOG_DIR); + return stderr; + } +#else + if (mkdir(LOG_DIR, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) { + fprintf(stderr, "Unable to create direcotry '%s': %s\n", LOG_DIR, strerror(errno)); + return stderr; + } +#endif + } + +#ifdef NO_PIDLOG + snprintf(file, FILENAME_MAX, LOG_DIR"/%04i-%02i-%02i_eq2" EXE_NAME ".log", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday); +#else + snprintf(file, FILENAME_MAX, LOG_DIR"/%04i-%02i-%02i_eq2" EXE_NAME "_%04i.log", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, getpid()); +#endif + + if ((f = fopen(file, "a")) == NULL) { + fprintf(stderr, "Could not open '%s' for writing: %s\n", file, strerror(errno)); + return stderr; + } + + return f; +} + +static void WriteQueuedLogs(int count) { + logq_t pending_head, pending_tail, *logq, *tmp; + int i = 0; + FILE *f; + + pending_head.next = &pending_tail; + pending_tail.prev = &pending_head; + + //loop through our queued logs and store at most `count` logs into a temporary list + //since io functions are expensive, we'll write from a temporary list so we don't hold the + //write lock of the main list for a long period of time + mlogqs.writelock(); + logq = head.next; + + while (head.next != &tail) { + //first remove the log from the master list + logq = head.next; + logq->next->prev = &head; + head.next = logq->next; + + //now insert it into the temporary list + tmp = pending_tail.prev; + tmp->next = logq; + logq->prev = tmp; + logq->next = &pending_tail; + pending_tail.prev = logq; + --num_logqs; + + logq = logq->next; + + //if we have a limit, check it + if (count > 0 && ++i == count) + break; + } + + //if we have no logs to write, we're done + if ((logq = pending_head.next) == &pending_tail) + { + mlogqs.releasewritelock(); + return; + } + + while (logq != &pending_tail) { + if (log_type_info[logq->log_type].console) { + SetConsoleColor(FOREGROUND_WHITE_BOLD); + printf("%s ", logq->date); + SetConsoleColor(log_type_info[logq->log_type].color); + printf("%s ", log_type_info[logq->log_type].display_name); + SetConsoleColor(FOREGROUND_WHITE_BOLD); + printf("%-10s: ", logq->name); + SetConsoleColor(log_type_info[logq->log_type].color); + printf("%s\n", logq->text); + SetConsoleColor(-1); + fflush(stdout); + } + + if (log_type_info[logq->log_type].logfile) { + f = OpenLogFile(); + + if (f != stderr || (f == stderr && !log_type_info[logq->log_type].console)) { + fprintf(f, "%s %s %s: %s\n", logq->date, log_type_info[logq->log_type].display_name, logq->name, logq->text); + fflush(f); + if (f != stderr) + fclose(f); + } + } + +#if defined WORLD + if (log_type_info[logq->log_type].client) { + // eventually output logging to the client who "subscribed" to the logger + // in-game, they type: + // /logsys add WORLD__DEBUG 5 + // to watch world debug loggers of level 5 or less + } +#endif + + tmp = logq; + logq = logq->next; + + free(tmp->text); + free(tmp); + } + + mlogqs.releasewritelock(); +} + +ThreadReturnType LogLoop(void *args) { + while (looping) { + WriteQueuedLogs(LOGS_PER_CYCLE); + Sleep(LOG_CYCLE); + } + + THREAD_RETURN(NULL); +} + +void LogStart() { + if (start_called) + return; + + //initialize the doubly linked list + head.prev = NULL; + head.next = &tail; + tail.prev = &head; + tail.next = NULL; + + mlogqs.SetName("logqueue"); + looping = true; + +#ifdef _WIN32 + _beginthread(LogLoop, 0, NULL); +#else + pthread_t thread; + pthread_create(&thread, NULL, LogLoop, NULL); + pthread_detach(thread); +#endif + + start_called = true; +} + +void LogStop() { + looping = false; + WriteQueuedLogs(-1); + start_called = false; +} + +static void LogQueueAdd(LogType log_type, char *text, int len, const char *cat_text = NULL) { + logq_t *logq; + struct tm *tm; + time_t now; + + if ((logq = (logq_t *)calloc(1, sizeof(logq_t))) == NULL) { + free(text); + fprintf(stderr, "%s: %u: Unable to allocate %zu bytes\n", __FUNCTION__, __LINE__, sizeof(logq_t)); + return; + } + + if ((logq->text = (char *)calloc(len + 1, sizeof(char))) == NULL) { + free(text); + free(logq); + fprintf(stderr, "%s: %u: Unable to allocate %i bytes\n", __FUNCTION__, __LINE__, len + 1); + return; + } + + now = time(NULL); + tm = localtime(&now); + + logq->log_type = log_type; + snprintf(logq->date, DATE_MAX + 1, "%02i:%02i:%02i", tm->tm_hour, tm->tm_min, tm->tm_sec); + strncpy(logq->name, cat_text == NULL || cat_text[0] == '\0' ? log_type_info[log_type].name : cat_text, LOG_NAME_MAX); + strncpy(logq->text, text, len); + free(text); + + if (!start_called) + LogStart(); + + //insert at the end + mlogqs.writelock(); + tail.prev->next = logq; + logq->prev = tail.prev; + logq->next = &tail; + tail.prev = logq; + ++num_logqs; + mlogqs.releasewritelock(); +} + +int8 GetLoggerLevel(LogType type) +{ + return log_type_info[type].level; +} + +// JA: horrific hack for Parser, since queued logging keeps crashing between parses. +#ifndef PARSER +void LogWrite(LogType type, int8 log_level, const char *cat_text, const char *fmt, ...) { + int count, size = 64; + char *buf; + va_list ap; + + // if there is no formatting, or the logger is DISABLED + // or the log_level param exceeds the minimum allowed value, abort logwrite + if (!log_type_info[type].enabled || (log_level > 0 && log_type_info[type].level < log_level)) + return; + + while (true) { + if ((buf = (char *)malloc(size)) == NULL) { + fprintf(stderr, "%s: %i: Unable to allocate %i bytes\n", __FUNCTION__, __LINE__, size); + return; + } + + va_start(ap, fmt); + count = vsnprintf(buf, size, fmt, ap); + va_end(ap); + + if (count > -1 && count < size) + break; + + free(buf); + if (count > 1) + size = count + 1; + else + size *= 2; + } + + LogQueueAdd(type, buf, count, cat_text); +} +#else +void LogWrite(LogType type, int8 log_level, const char *cat_text, const char *format, ...) +{ + // if there is no formatting, or the logger is DISABLED + // or the log_level param exceeds the minimum allowed value, abort logwrite + if ( !format || !log_type_info[type].enabled || (log_level > 0 && log_type_info[type].level < log_level) ) + return; + + time_t clock; + struct tm *tm; + + char buffer[LOG_BUFFER_SIZE], date[32]; + va_list args; + FILE *f; + size_t cat_text_len = 0; + + memset(buffer, 0, sizeof(buffer)); + memset(date, 0, sizeof(date)); + + va_start(args, format); + vsnprintf(buffer, sizeof(buffer) - 1, format, args); + va_end(args); + + time(&clock); + tm = localtime(&clock); + snprintf(date, sizeof(date)-1, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec); + // DateString(date, sizeof(date)); + + cat_text_len = strlen(cat_text); + //if( strlen(cat_text) == 0 ) // cat_text was blank + // cat_text = (char*)log_type_info[type].name; + + /* write to the log file? */ + if (log_type_info[type].logfile) + { + char exename[200] = ""; + + #ifdef LOGIN + snprintf(exename, sizeof(exename), "login"); + #endif + #ifdef WORLD + snprintf(exename, sizeof(exename), "world"); + #endif + #ifdef PARSER + snprintf(exename, sizeof(exename), "parser"); + #endif + #ifdef PATCHER + snprintf(exename, sizeof(exename), "patcher"); + #endif + + char filename[200], log_header[200] = ""; + + #ifndef NO_PIDLOG + snprintf(filename, sizeof(filename)-1, "logs/%04d-%02d-%02d_eq2%s_%04i.log", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, exename, getpid()); + #else + snprintf(filename, sizeof(filename)-1, "logs/%04d-%02d-%02d_eq2%s.log", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, exename); + #endif + + f=fopen(filename, "r"); + if( !f ) + snprintf(log_header, sizeof(log_header), "===[ New log '%s' started ]===\n\n", filename); + else + fclose (f); + + f = fopen(filename, "a"); + if (f) { + if( strlen(log_header) > 0 ) + fprintf(f, "%s\n", log_header); + fprintf(f, "%s %s %s: %s\n", date, log_type_info[type].display_name, cat_text, buffer); + fclose(f); + } + } + + /* write to the console? */ + if (log_type_info[type].console) + { + #ifdef _WIN32 + ColorizeLog(log_type_info[type].color, date, log_type_info[type].display_name, cat_text_len == 0 ? log_type_info[type].name : cat_text, (string)buffer); + #else + printf("%s %s %s: %s\n", date, log_type_info[type].display_name, cat_text_len == 0 ? log_type_info[type].name : cat_text, buffer); + #endif + } +} + +void +ColorizeLog(int color, char *date, const char *display_name, const char *category, string buffer) +{ + #ifdef _WIN32 + HANDLE console = GetStdHandle(STD_OUTPUT_HANDLE); + if (console == INVALID_HANDLE_VALUE) { + printf("%s %s %s: %s\n", date, display_name, category, buffer); + return; + } + printf("%s ", date); + SetConsoleTextAttribute(console, color); + printf("%s ", display_name); + SetConsoleTextAttribute(console, FOREGROUND_WHITE_BOLD); + printf("%s: ", category); + SetConsoleTextAttribute(console, color); + printf("%s\n", buffer.c_str()); + SetConsoleTextAttribute(console, FOREGROUND_WHITE); + #endif +} + +#endif + +LogTypeStatus * +GetLogTypeStatus(const char *category, const char *type) { + char combined[256]; + int i; + + memset(combined, 0, sizeof(combined)); + snprintf(combined, sizeof(combined) - 1, "%s__%s", category, type); + + for (i = 0; i < NUMBER_OF_LOG_TYPES; i++) { + if (strcasecmp(log_type_info[i].name, combined) == 0) + return &log_type_info[i]; + } + + return &log_type_info[NUMBER_OF_LOG_TYPES]; +} + +void +ProcessLogConfig(XMLNode node) { + int i; + const char *category, *type, *level, *color, *enabled, *logs; + LogTypeStatus *lfs; + XMLNode child; + + category = node.getAttribute("Category"); + if (!category) { + LogWrite(MISC__WARNING, 0, "Misc", "Error parsing log config. Config missing a Category"); + return; + } + + for (i = 0; i < node.nChildNode("ConfigType"); i++) { + child = node.getChildNode("ConfigType", i); + type = child.getAttribute("Type"); + + if (!type) { + LogWrite(MISC__WARNING, 0, "Misc", "Error parsing log config. Config missing a Type"); + continue; + } + + lfs = GetLogTypeStatus(category, type); + level = child.getAttribute("Level"); + enabled = child.getAttribute("Enabled"); + color = child.getAttribute("Color"); + logs = child.getAttribute("Logs"); + + if (!logs) { + LogWrite(MISC__WARNING, 0, "Misc", "Error parsing log config. Config missing 'Logs' attribute to specify which log(s) to write to"); + continue; + } + if (!IsNumber(logs)) { + LogWrite(MISC__WARNING, 0, "Misc", "Error parsing log config. Attribute 'Logs' must be a number. See LogTypes.h for the valid types."); + continue; + } + + if (enabled) { + if (!strcasecmp("true", enabled) || !strcasecmp("on", enabled)) + lfs->enabled = true; + else if (!strcasecmp("false", enabled) || !strcasecmp("off", enabled)) + lfs->enabled = false; + else + LogWrite(MISC__WARNING, 0, "Misc", "Error parsing log config. Log setting 'Enabled' has invalid value '%s'. 'true'/'on' or 'false'/'off' are valid values", enabled); + } + + if (IsNumber(level)) + lfs->level = atoi(level); + else + lfs->level = 0; + + if (color) { + if (IsNumber(color)) + lfs->color = atoi(color); + else if (!strcasecmp("White", color)) + lfs->color = FOREGROUND_WHITE; + else if (!strcasecmp("Green", color)) + lfs->color = FOREGROUND_GREEN; + else if (!strcasecmp("Yellow", color)) + lfs->color = FOREGROUND_YELLOW; + else if (!strcasecmp("Red", color)) + lfs->color = FOREGROUND_RED; + else if (!strcasecmp("Blue", color)) + lfs->color = FOREGROUND_BLUE; + else if (!strcasecmp("Cyan", color)) + lfs->color = FOREGROUND_CYAN; + else if (!strcasecmp("Magenta", color)) + lfs->color = FOREGROUND_MAGENTA; + else if (!strcasecmp("WhiteBold", color)) + lfs->color = FOREGROUND_WHITE_BOLD; + else if (!strcasecmp("GreenBold", color)) + lfs->color = FOREGROUND_GREEN_BOLD; + else if (!strcasecmp("YellowBold", color)) + lfs->color = FOREGROUND_YELLOW_BOLD; + else if (!strcasecmp("RedBold", color)) + lfs->color = FOREGROUND_RED_BOLD; + else if (!strcasecmp("BlueBold", color)) + lfs->color = FOREGROUND_BLUE_BOLD; + else if (!strcasecmp("CyanBold", color)) + lfs->color = FOREGROUND_CYAN_BOLD; + else if (!strcasecmp("MagentaBold", color)) + lfs->color = FOREGROUND_MAGENTA_BOLD; + else + LogWrite(MISC__WARNING, 0, "Misc", "Error parsing log config. Log setting 'Color' has invalid value '%s'", color); + } + + // JA: something was wrong here, lfs->logfile or console always was true, even if bit was off. Will ask Scatman about it someday. + lfs->logfile = (atoi(logs) & LOG_LOGFILE); + lfs->console = (atoi(logs) & LOG_CONSOLE); + lfs->client = (atoi(logs) & LOG_CLIENT); + } +} + +bool +LogParseConfigs() { + XMLNode main_node; + int i; + + main_node = XMLNode::openFileHelper("log_config.xml", "EQ2EmuLogConfigs"); + if (main_node.isEmpty()) { + LogWrite(MISC__WARNING, 0, "Misc", "Unable to parse the file 'log_config.xml' or it does not exist. Default values will be used"); + return false; + } + + for (i = 0; i < main_node.nChildNode("LogConfig"); i++) + ProcessLogConfig(main_node.getChildNode("LogConfig", i)); + + return true; +} diff --git a/old/Log.h b/old/Log.h new file mode 100644 index 0000000..8a7e6a6 --- /dev/null +++ b/old/Log.h @@ -0,0 +1,69 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#ifndef LOG_H_ +#define LOG_H_ + +#include +#include "../WorldServer/client.h" + +#define LOG_BUFFER_SIZE 4096 + +#define LOG_CATEGORY(category) LOG_ ##category , +enum LogCategory +{ + #include "LogTypes.h" + NUMBER_OF_LOG_CATEGORIES +}; + +#define LOG_TYPE(category, type, level, color, enabled, logfile, console, client, str) category##__##type , +enum LogType +{ + #include "LogTypes.h" + NUMBER_OF_LOG_TYPES +}; + +extern const char* log_category_names[NUMBER_OF_LOG_CATEGORIES]; + +struct LogTypeStatus +{ + int8 level; + int color; + bool enabled; + bool logfile; + bool console; + bool client; + LogCategory category; + const char *name; + const char *display_name; +}; + +extern LogTypeStatus* log_type_info; + +void LogStart(); +void LogStop(); +int8 GetLoggerLevel(LogType type); +void LogWrite(LogType type, int8 log_level, const char *cat_text, const char *fmt, ...); +#ifdef PARSER + void ColorizeLog(int color, char *date, const char *display_name, const char *category, string buffer); +#endif + +bool LogParseConfigs(); + +#endif diff --git a/old/LogTypes.h b/old/LogTypes.h new file mode 100644 index 0000000..94aeae8 --- /dev/null +++ b/old/LogTypes.h @@ -0,0 +1,528 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#ifndef LOG_CATEGORY + #define LOG_CATEGORY(name) +#endif + +#ifndef LOG_TYPE + #define LOG_TYPE(category, type, level, color, enabled, logfile, console, client, str) +#endif + +#ifndef ENABLED + #define ENABLED true +#endif + +#ifndef DISABLED + #define DISABLED false +#endif + +#ifdef _WIN32 + #define FOREGROUND_WHITE (FOREGROUND_RED |FOREGROUND_GREEN | FOREGROUND_BLUE) + #define FOREGROUND_WHITE_BOLD (FOREGROUND_RED |FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY) + #define FOREGROUND_RED_BOLD (FOREGROUND_RED | FOREGROUND_INTENSITY) + #define FOREGROUND_GREEN_BOLD (FOREGROUND_GREEN | FOREGROUND_INTENSITY) + #define FOREGROUND_BLUE_BOLD (FOREGROUND_BLUE | FOREGROUND_INTENSITY) + #define FOREGROUND_YELLOW (FOREGROUND_RED | FOREGROUND_GREEN) + #define FOREGROUND_YELLOW_BOLD (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY) + #define FOREGROUND_CYAN (FOREGROUND_GREEN | FOREGROUND_BLUE) + #define FOREGROUND_CYAN_BOLD (FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY) + #define FOREGROUND_MAGENTA (FOREGROUND_RED | FOREGROUND_BLUE) + #define FOREGROUND_MAGENTA_BOLD (FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY) +#else + #define FOREGROUND_WHITE 37 + #define FOREGROUND_WHITE_BOLD 137 + #define FOREGROUND_RED 31 + #define FOREGROUND_RED_BOLD 131 + #define FOREGROUND_GREEN 32 + #define FOREGROUND_GREEN_BOLD 132 + #define FOREGROUND_BLUE 34 + #define FOREGROUND_BLUE_BOLD 134 + #define FOREGROUND_YELLOW 33 + #define FOREGROUND_YELLOW_BOLD 133 + #define FOREGROUND_CYAN 36 + #define FOREGROUND_CYAN_BOLD 136 + #define FOREGROUND_MAGENTA 35 + #define FOREGROUND_MAGENTA_BOLD 135 +#endif + + + +#define LOG_LOGFILE 1 +#define LOG_CONSOLE 2 +#define LOG_CLIENT 4 /* not yet using */ + +/* + Legend for str output (optional): + I : Information messages + W : Warning messages + E : Error messages + D : Debug messages + P : DumpPacket/PrintPacket messages - should NEVER go to Client channel!!! + T : Low-level debug tracing messages - should NEVER go to Client channel!!! + + Note: If str = null, output #catagory__#type to logs +*/ + + +/*** SYSTEM Loggers ******************************************************************************/ +// Logging specific to general events within the World code +LOG_CATEGORY(WORLD) +LOG_TYPE(WORLD, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") // Information messages (minimum output) +LOG_TYPE(WORLD, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") // Warning messages +LOG_TYPE(WORLD, ERROR, 0, FOREGROUND_RED_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "E") // Error messages (should always be enabled) +LOG_TYPE(WORLD, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") // Debug messages (enabled during alpha dev) +LOG_TYPE(WORLD, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") // DumpPacket/PrintPacket messages +LOG_TYPE(WORLD, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") // Low-level debug tracing messages + +// LoginServer and MiniLogin events +LOG_CATEGORY(LOGIN) +LOG_TYPE(LOGIN, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") +LOG_TYPE(LOGIN, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") +LOG_TYPE(LOGIN, ERROR, 0, FOREGROUND_RED_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "E") +LOG_TYPE(LOGIN, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") +LOG_TYPE(LOGIN, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") +LOG_TYPE(LOGIN, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") + +// PatchServer (DB auto-patcher) events +LOG_CATEGORY(PATCHER) +LOG_TYPE(PATCHER, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") +LOG_TYPE(PATCHER, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") +LOG_TYPE(PATCHER, ERROR, 0, FOREGROUND_RED_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "E") +LOG_TYPE(PATCHER, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") +LOG_TYPE(PATCHER, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") +LOG_TYPE(PATCHER, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") + +// PacketParser events +LOG_CATEGORY(PARSER) +LOG_TYPE(PARSER, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") +LOG_TYPE(PARSER, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") +LOG_TYPE(PARSER, ERROR, 0, FOREGROUND_RED_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "E") +LOG_TYPE(PARSER, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") +LOG_TYPE(PARSER, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") +LOG_TYPE(PARSER, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") + +// World/Login/Patcher/Parser Initialization loggers +LOG_CATEGORY(INIT) +LOG_TYPE(INIT, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") +LOG_TYPE(INIT, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") +LOG_TYPE(INIT, ERROR, 0, FOREGROUND_RED_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "E") +LOG_TYPE(INIT, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") +LOG_TYPE(INIT, LOGIN_INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") +LOG_TYPE(INIT, LOGIN_WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") +LOG_TYPE(INIT, LOGIN_ERROR, 0, FOREGROUND_RED_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "E") +LOG_TYPE(INIT, LOGIN_DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") +LOG_TYPE(INIT, PATCHER_INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") +LOG_TYPE(INIT, PATCHER_WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") +LOG_TYPE(INIT, PATCHER_ERROR, 0, FOREGROUND_RED_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "E") +LOG_TYPE(INIT, PATCHER_DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") + +// General DB logging +LOG_CATEGORY(DATABASE) +LOG_TYPE(DATABASE, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") +LOG_TYPE(DATABASE, ERROR, 0, FOREGROUND_RED_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "E") +LOG_TYPE(DATABASE, QUERY, 0, FOREGROUND_CYAN, DISABLED, DISABLED, DISABLED, DISABLED, "Q") +LOG_TYPE(DATABASE, RESULT, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "R") +LOG_TYPE(DATABASE, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") +LOG_TYPE(DATABASE, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") + +// Logging Mutex code +LOG_CATEGORY(MUTEX) +LOG_TYPE(MUTEX, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") +LOG_TYPE(MUTEX, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") +LOG_TYPE(MUTEX, ERROR, 0, FOREGROUND_RED_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "E") +LOG_TYPE(MUTEX, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") +LOG_TYPE(MUTEX, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") +LOG_TYPE(MUTEX, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") + +// anything else... including a special DEV type "TODO" +LOG_CATEGORY(MISC) +LOG_TYPE(MISC, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") +LOG_TYPE(MISC, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") +LOG_TYPE(MISC, ERROR, 0, FOREGROUND_RED_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "E") +LOG_TYPE(MISC, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") +LOG_TYPE(MISC, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") +LOG_TYPE(MISC, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") +LOG_TYPE(MISC, TODO, 0, FOREGROUND_YELLOW_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "M") + + + +/*** NETWORK Loggers *****************************************************************************/ +// Client Communications Logging +LOG_CATEGORY(CCLIENT) +LOG_TYPE(CCLIENT, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") +LOG_TYPE(CCLIENT, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") +LOG_TYPE(CCLIENT, ERROR, 0, FOREGROUND_RED_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "E") +LOG_TYPE(CCLIENT, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") +LOG_TYPE(CCLIENT, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") +LOG_TYPE(CCLIENT, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") + +// Logging Net code +LOG_CATEGORY(NET) +LOG_TYPE(NET, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") +LOG_TYPE(NET, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") +LOG_TYPE(NET, ERROR, 0, FOREGROUND_RED_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "E") +LOG_TYPE(NET, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") +LOG_TYPE(NET, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") +LOG_TYPE(NET, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") + +// Logging opcodes as they are encountered +LOG_CATEGORY(OPCODE) +LOG_TYPE(OPCODE, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") +LOG_TYPE(OPCODE, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") +LOG_TYPE(OPCODE, ERROR, 0, FOREGROUND_RED_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "E") +LOG_TYPE(OPCODE, DEBUG, 0, FOREGROUND_MAGENTA_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") +LOG_TYPE(OPCODE, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") +LOG_TYPE(OPCODE, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") + +// Special category for dumping out excessive DumpPacket or Opcode debugging entries - All DISABLED by default! +LOG_CATEGORY(PACKET) +LOG_TYPE(PACKET, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") +LOG_TYPE(PACKET, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") +LOG_TYPE(PACKET, ERROR, 0, FOREGROUND_RED_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "E") +LOG_TYPE(PACKET, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") +LOG_TYPE(PACKET, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") +LOG_TYPE(PACKET, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") + + + +/*** PLAYER Loggers ******************************************************************************/ +// Events related to character progress +LOG_CATEGORY(PLAYER) +LOG_TYPE(PLAYER, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") +LOG_TYPE(PLAYER, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") +LOG_TYPE(PLAYER, ERROR, 0, FOREGROUND_RED_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "E") +LOG_TYPE(PLAYER, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") +LOG_TYPE(PLAYER, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") +LOG_TYPE(PLAYER, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") + + + +/*** SUBSYSTEM Loggers ***************************************************************************/ +// Achievements Logging +LOG_CATEGORY(ACHIEVEMENT) +LOG_TYPE(ACHIEVEMENT, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") +LOG_TYPE(ACHIEVEMENT, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") +LOG_TYPE(ACHIEVEMENT, ERROR, 0, FOREGROUND_RED_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "E") +LOG_TYPE(ACHIEVEMENT, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") +LOG_TYPE(ACHIEVEMENT, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") +LOG_TYPE(ACHIEVEMENT, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") + +// Chat Logging +LOG_CATEGORY(CHAT) +LOG_TYPE(CHAT, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") +LOG_TYPE(CHAT, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") +LOG_TYPE(CHAT, ERROR, 0, FOREGROUND_RED_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "E") +LOG_TYPE(CHAT, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") +LOG_TYPE(CHAT, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") +LOG_TYPE(CHAT, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") + +// Collection generated events +LOG_CATEGORY(COLLECTION) +LOG_TYPE(COLLECTION, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") +LOG_TYPE(COLLECTION, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") +LOG_TYPE(COLLECTION, ERROR, 0, FOREGROUND_RED_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "E") +LOG_TYPE(COLLECTION, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") +LOG_TYPE(COLLECTION, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") +LOG_TYPE(COLLECTION, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") + +// Events related to combat,aggro, hate, melee, damages, etc. +LOG_CATEGORY(COMBAT) +LOG_TYPE(COMBAT, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") +LOG_TYPE(COMBAT, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") +LOG_TYPE(COMBAT, ERROR, 0, FOREGROUND_RED_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "E") +LOG_TYPE(COMBAT, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") +LOG_TYPE(COMBAT, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") +LOG_TYPE(COMBAT, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") + +// Events related to commands (slash commands, UI commands, etc) +LOG_CATEGORY(COMMAND) +LOG_TYPE(COMMAND, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") +LOG_TYPE(COMMAND, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") +LOG_TYPE(COMMAND, ERROR, 0, FOREGROUND_RED_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "E") +LOG_TYPE(COMMAND, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") +LOG_TYPE(COMMAND, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") +LOG_TYPE(COMMAND, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") + +// Faction-related events, adjustments, querying, etc. +LOG_CATEGORY(FACTION) +LOG_TYPE(FACTION, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") +LOG_TYPE(FACTION, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") +LOG_TYPE(FACTION, ERROR, 0, FOREGROUND_RED_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "E") +LOG_TYPE(FACTION, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") +LOG_TYPE(FACTION, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") +LOG_TYPE(FACTION, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") + +// Guild events, members, logging, permissions, recruiting, etc. +LOG_CATEGORY(GUILD) +LOG_TYPE(GUILD, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") +LOG_TYPE(GUILD, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") +LOG_TYPE(GUILD, ERROR, 0, FOREGROUND_RED_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "E") +LOG_TYPE(GUILD, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") +LOG_TYPE(GUILD, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") +LOG_TYPE(GUILD, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") + +// Group events, members, permissions, etc. +LOG_CATEGORY(GROUP) +LOG_TYPE(GROUP, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") +LOG_TYPE(GROUP, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") +LOG_TYPE(GROUP, ERROR, 0, FOREGROUND_RED_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "E") +LOG_TYPE(GROUP, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") +LOG_TYPE(GROUP, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") +LOG_TYPE(GROUP, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") + +// Item events, stats, appearances, loading/reloading, etc. +LOG_CATEGORY(ITEM) +LOG_TYPE(ITEM, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") +LOG_TYPE(ITEM, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") +LOG_TYPE(ITEM, ERROR, 0, FOREGROUND_RED_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "E") +LOG_TYPE(ITEM, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") +LOG_TYPE(ITEM, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") +LOG_TYPE(ITEM, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") + +// Loot events, loot lists, rules, smart loot +LOG_CATEGORY(LOOT) +LOG_TYPE(LOOT, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") +LOG_TYPE(LOOT, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") +LOG_TYPE(LOOT, ERROR, 0, FOREGROUND_RED_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "E") +LOG_TYPE(LOOT, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") +LOG_TYPE(LOOT, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") +LOG_TYPE(LOOT, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") + +// Events that occur within the LUA subsystem +LOG_CATEGORY(LUA) +LOG_TYPE(LUA, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") +LOG_TYPE(LUA, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") +LOG_TYPE(LUA, ERROR, 0, FOREGROUND_RED_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "E") +LOG_TYPE(LUA, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") +LOG_TYPE(LUA, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") +LOG_TYPE(LUA, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") + +// Merchant events, buy/sell/broker, faction merchants, etc. +LOG_CATEGORY(MERCHANT) +LOG_TYPE(MERCHANT, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") +LOG_TYPE(MERCHANT, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") +LOG_TYPE(MERCHANT, ERROR, 0, FOREGROUND_RED_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "E") +LOG_TYPE(MERCHANT, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") +LOG_TYPE(MERCHANT, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") +LOG_TYPE(MERCHANT, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") + +// NPC events, stats, appearances, movement, gear, abilities, etc. +LOG_CATEGORY(NPC) +LOG_TYPE(NPC, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") +LOG_TYPE(NPC, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") +LOG_TYPE(NPC, ERROR, 0, FOREGROUND_RED_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "E") +LOG_TYPE(NPC, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") +LOG_TYPE(NPC, COMBAT, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") +LOG_TYPE(NPC, SPELLS, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") +LOG_TYPE(NPC, AI, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") +LOG_TYPE(NPC, DAMAGE, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") +LOG_TYPE(NPC, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") +LOG_TYPE(NPC, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") + +// What is that NPC thinking?! ...etc. +LOG_CATEGORY(NPC_AI) +LOG_TYPE(NPC_AI, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") +LOG_TYPE(NPC_AI, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") +LOG_TYPE(NPC_AI, ERROR, 0, FOREGROUND_RED_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "E") +LOG_TYPE(NPC_AI, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") +LOG_TYPE(NPC_AI, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") +LOG_TYPE(NPC_AI, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") + +// PET events, stats, appearances, movement, gear, abilities, etc. +LOG_CATEGORY(PET) +LOG_TYPE(PET, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") +LOG_TYPE(PET, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") +LOG_TYPE(PET, ERROR, 0, FOREGROUND_RED_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "E") +LOG_TYPE(PET, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") +LOG_TYPE(PET, COMBAT, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") +LOG_TYPE(PET, SPELLS, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") +LOG_TYPE(PET, AI, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") +LOG_TYPE(PET, DAMAGE, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") +LOG_TYPE(PET, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") +LOG_TYPE(PET, TRACE, 0, FOREGROUND_GREEN, DISABLED, DISABLED, DISABLED, DISABLED, "T") + +// Quest generated events +LOG_CATEGORY(QUEST) +LOG_TYPE(QUEST, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") +LOG_TYPE(QUEST, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") +LOG_TYPE(QUEST, ERROR, 0, FOREGROUND_RED_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "E") +LOG_TYPE(QUEST, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") +LOG_TYPE(QUEST, REWARD, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") +LOG_TYPE(QUEST, STEP, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") +LOG_TYPE(QUEST, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") +LOG_TYPE(QUEST, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") + +// Events relating to Recipes in the world +LOG_CATEGORY(RECIPE) +LOG_TYPE(RECIPE, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") +LOG_TYPE(RECIPE, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") +LOG_TYPE(RECIPE, ERROR, 0, FOREGROUND_RED_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "E") +LOG_TYPE(RECIPE, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") +LOG_TYPE(RECIPE, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") +LOG_TYPE(RECIPE, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") + +// Events relating to Rules in the world +LOG_CATEGORY(RULESYS) +LOG_TYPE(RULESYS, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") +LOG_TYPE(RULESYS, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") +LOG_TYPE(RULESYS, ERROR, 0, FOREGROUND_RED_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "E") +LOG_TYPE(RULESYS, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") +LOG_TYPE(RULESYS, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") +LOG_TYPE(RULESYS, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") + +// The Skill system, books, scribing, stats, usage +LOG_CATEGORY(SKILL) +LOG_TYPE(SKILL, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") +LOG_TYPE(SKILL, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") +LOG_TYPE(SKILL, ERROR, 0, FOREGROUND_RED_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "E") +LOG_TYPE(SKILL, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") +LOG_TYPE(SKILL, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") +LOG_TYPE(SKILL, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") + +// The Spell system, books, scribing, stats, usage +LOG_CATEGORY(SPELL) +LOG_TYPE(SPELL, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") +LOG_TYPE(SPELL, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") +LOG_TYPE(SPELL, ERROR, 0, FOREGROUND_RED_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "E") +LOG_TYPE(SPELL, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") +LOG_TYPE(SPELL, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") +LOG_TYPE(SPELL, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") + +// The Crafting system, recipies, reactions, progress, etc. +LOG_CATEGORY(TRADESKILL) +LOG_TYPE(TRADESKILL, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") +LOG_TYPE(TRADESKILL, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") +LOG_TYPE(TRADESKILL, ERROR, 0, FOREGROUND_RED_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "E") +LOG_TYPE(TRADESKILL, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") +LOG_TYPE(TRADESKILL, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") +LOG_TYPE(TRADESKILL, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") + +// The Transportation system, teleporters, mounts, etc. +LOG_CATEGORY(TRANSPORT) +LOG_TYPE(TRANSPORT, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") +LOG_TYPE(TRANSPORT, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") +LOG_TYPE(TRANSPORT, ERROR, 0, FOREGROUND_RED_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "E") +LOG_TYPE(TRANSPORT, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") +LOG_TYPE(TRANSPORT, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") +LOG_TYPE(TRANSPORT, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") + + +/*** SPAWN Loggers *******************************************************************************/ +// General Spawn events, location, placement, grouping +LOG_CATEGORY(SPAWN) +LOG_TYPE(SPAWN, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") +LOG_TYPE(SPAWN, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") +LOG_TYPE(SPAWN, ERROR, 0, FOREGROUND_RED_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "E") +LOG_TYPE(SPAWN, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") +LOG_TYPE(SPAWN, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") +LOG_TYPE(SPAWN, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") + +// Events relating to interactable objects in the world +LOG_CATEGORY(OBJECT) +LOG_TYPE(OBJECT, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") +LOG_TYPE(OBJECT, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") +LOG_TYPE(OBJECT, ERROR, 0, FOREGROUND_RED_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "E") +LOG_TYPE(OBJECT, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") +LOG_TYPE(OBJECT, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") +LOG_TYPE(OBJECT, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") + +// Events relating to Signs in the world +LOG_CATEGORY(SIGN) +LOG_TYPE(SIGN, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") +LOG_TYPE(SIGN, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") +LOG_TYPE(SIGN, ERROR, 0, FOREGROUND_RED_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "E") +LOG_TYPE(SIGN, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") +LOG_TYPE(SIGN, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") +LOG_TYPE(SIGN, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") + +// Events relating to Widgets in the world +LOG_CATEGORY(WIDGET) +LOG_TYPE(WIDGET, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") +LOG_TYPE(WIDGET, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") +LOG_TYPE(WIDGET, ERROR, 0, FOREGROUND_RED_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "E") +LOG_TYPE(WIDGET, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") +LOG_TYPE(WIDGET, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") +LOG_TYPE(WIDGET, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") + +// Events relating to Groundspawns in the world +LOG_CATEGORY(GROUNDSPAWN) +LOG_TYPE(GROUNDSPAWN, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") +LOG_TYPE(GROUNDSPAWN, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") +LOG_TYPE(GROUNDSPAWN, ERROR, 0, FOREGROUND_RED_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "E") +LOG_TYPE(GROUNDSPAWN, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") +LOG_TYPE(GROUNDSPAWN, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") +LOG_TYPE(GROUNDSPAWN, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") + + + +/*** ZONE Loggers ********************************************************************************/ +// Zone-related events, status, messaging, access +LOG_CATEGORY(ZONE) +LOG_TYPE(ZONE, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") +LOG_TYPE(ZONE, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") +LOG_TYPE(ZONE, ERROR, 0, FOREGROUND_RED_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "E") +LOG_TYPE(ZONE, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") +LOG_TYPE(ZONE, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") +LOG_TYPE(ZONE, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") + +// Instance loading/reloading, etc. +LOG_CATEGORY(INSTANCE) +LOG_TYPE(INSTANCE, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") +LOG_TYPE(INSTANCE, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") +LOG_TYPE(INSTANCE, ERROR, 0, FOREGROUND_RED_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "E") +LOG_TYPE(INSTANCE, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") +LOG_TYPE(INSTANCE, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") +LOG_TYPE(INSTANCE, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") + + +/*** MAP Loggers ********************************************************************************/ +// Map-related events, status, messaging, access +LOG_CATEGORY(MAP) +LOG_TYPE(MAP, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") +LOG_TYPE(MAP, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") +LOG_TYPE(MAP, ERROR, 0, FOREGROUND_RED_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "E") +LOG_TYPE(MAP, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") +LOG_TYPE(MAP, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") +LOG_TYPE(MAP, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") + + +/*** Region Map Loggers ********************************************************************************/ +// RegionMap-related events, status, messaging, access +LOG_CATEGORY(REGION) +LOG_TYPE(REGION, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") +LOG_TYPE(REGION, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") +LOG_TYPE(REGION, ERROR, 0, FOREGROUND_RED_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "E") +LOG_TYPE(REGION, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") +LOG_TYPE(REGION, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") +LOG_TYPE(REGION, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") + +// Logging Peering code +LOG_CATEGORY(PEERING) +LOG_TYPE(PEERING, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") +LOG_TYPE(PEERING, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") +LOG_TYPE(PEERING, ERROR, 0, FOREGROUND_RED_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "E") +LOG_TYPE(PEERING, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") +LOG_TYPE(PEERING, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") +LOG_TYPE(PEERING, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") + +#undef LOG_TYPE +#undef LOG_CATEGORY +#undef ENABLED +#undef DISABLED diff --git a/old/MiscFunctions.cpp b/old/MiscFunctions.cpp new file mode 100644 index 0000000..21ec052 --- /dev/null +++ b/old/MiscFunctions.cpp @@ -0,0 +1,973 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#include "../common/debug.h" +#include "../common/Log.h" +#include "MiscFunctions.h" +#include +#include +#include +#include +#include + +#ifndef WIN32 +#include +#include +#endif +#include +#include +#ifdef WIN32 + #include +#endif +#include "../common/timer.h" +#include "../common/seperator.h" +#include "../common/packet_dump.h" +#include + +using namespace std; + +#ifndef PATCHER +extern map EQOpcodeVersions; +#endif + +#ifdef WIN32 + #include + #include + + #define snprintf _snprintf + #define vsnprintf _vsnprintf + #define strncasecmp _strnicmp + #define strcasecmp _stricmp +#else + #include + #include + #include + #include + #include +#ifdef FREEBSD //Timothy Whitman - January 7, 2003 + #include + #include + #endif + #include + #include + #include + #include +#endif + +void CoutTimestamp(bool ms) { + time_t rawtime; + struct tm* gmt_t; + time(&rawtime); + gmt_t = gmtime(&rawtime); + + struct timeval read_time; + gettimeofday(&read_time,0); + + cout << (gmt_t->tm_year + 1900) << "/" << setw(2) << setfill('0') << (gmt_t->tm_mon + 1) << "/" << setw(2) << setfill('0') << gmt_t->tm_mday << " " << setw(2) << setfill('0') << gmt_t->tm_hour << ":" << setw(2) << setfill('0') << gmt_t->tm_min << ":" << setw(2) << setfill('0') << gmt_t->tm_sec; + if (ms) + cout << "." << setw(3) << setfill('0') << (read_time.tv_usec / 1000); + cout << " GMT"; +} + +string loadInt32String(uchar* buffer, int16 buffer_size, int16* pos, EQ2_32BitString* eq_string){ + buffer += *pos; + int32 size = *(int32*)buffer; + if((size + *pos + sizeof(int16)) > buffer_size){ + cout << "Error in loadInt32String: Corrupt packet.\n"; + return string(""); + } + buffer += sizeof(int32); + string ret((char*)buffer, 0, size); + if(eq_string){ + eq_string->size = size; + eq_string->data = ret; + } + *pos += (size + sizeof(int32)); + return ret; +} +string loadInt16String(uchar* buffer, int16 buffer_size, int16* pos, EQ2_16BitString* eq_string){ + buffer += *pos; + int16 size = *(int16*)buffer; + if((size + *pos + sizeof(int16))> buffer_size){ + cout << "Error in loadInt16String: Corrupt packet.\n"; + return string(""); + } + buffer += sizeof(int16); + string ret((char*)buffer, 0, size); + if(eq_string){ + eq_string->size = size; + eq_string->data = ret; + } + *pos += (size + sizeof(int16)); + return ret; +} +string loadInt8String(uchar* buffer, int16 buffer_size, int16* pos, EQ2_8BitString* eq_string){ + buffer += *pos; + int8 size = *(int8*)buffer; + if((size + *pos + sizeof(int16)) > buffer_size){ + cout << "Error in loadInt8String: Corrupt packet.\n"; + return string(""); + } + buffer += sizeof(int8); + string ret((char*)buffer, 0, size); + if(eq_string){ + eq_string->size = size; + eq_string->data = ret; + } + *pos += (size + sizeof(int8)); + return ret; +} + +sint16 storeInt32String(uchar* buffer, int16 buffer_size, string in_str){ + sint16 string_size = in_str.length(); + if((string_size + sizeof(int32)) > buffer_size) + return -1; + memcpy(buffer, &string_size, sizeof(int32)); + buffer += sizeof(int32); + memcpy(buffer, in_str.c_str(), string_size); + buffer += string_size; + return (buffer_size - (string_size + sizeof(int32))); +} +sint16 storeInt16String(uchar* buffer, int16 buffer_size, string in_str){ + sint16 string_size = in_str.length(); + if((string_size + sizeof(int16)) > buffer_size) + return -1; + memcpy(buffer, &string_size, sizeof(int16)); + buffer += sizeof(int16); + memcpy(buffer, in_str.c_str(), string_size); + buffer += string_size; + return (buffer_size - (string_size + sizeof(int16))); +} +sint16 storeInt8String(uchar* buffer, int16 buffer_size, string in_str){ + sint16 string_size = in_str.length(); + if((string_size + sizeof(int8)) > buffer_size) + return -1; + memcpy(buffer, &string_size, sizeof(int8)); + buffer += sizeof(int8); + memcpy(buffer, in_str.c_str(), string_size); + buffer += string_size; + return (buffer_size - (string_size + sizeof(int8))); +} + + +sint32 filesize(FILE* fp) { +#ifdef WIN32 + return _filelength(_fileno(fp)); +#else + struct stat file_stat; + fstat(fileno(fp), &file_stat); + return (sint32) file_stat.st_size; +#endif +} + +int32 ResolveIP(const char* hostname, char* errbuf) { +#ifdef WIN32 + static InitWinsock ws; +#endif + if (errbuf) + errbuf[0] = 0; + if (hostname == 0) { + if (errbuf) + snprintf(errbuf, ERRBUF_SIZE, "ResolveIP(): hostname == 0"); + return 0; + } + struct sockaddr_in server_sin; +#ifdef WIN32 + PHOSTENT phostent = NULL; +#else + struct hostent *phostent = NULL; +#endif + server_sin.sin_family = AF_INET; + if ((phostent = gethostbyname(hostname)) == NULL) { +#ifdef WIN32 + if (errbuf) + snprintf(errbuf, ERRBUF_SIZE, "Unable to get the host name. Error: %i", WSAGetLastError()); +#else + if (errbuf) + snprintf(errbuf, ERRBUF_SIZE, "Unable to get the host name. Error: %s", strerror(errno)); +#endif + return 0; + } +#ifdef WIN32 + memcpy ((char FAR *)&(server_sin.sin_addr), phostent->h_addr, phostent->h_length); +#else + memcpy ((char*)&(server_sin.sin_addr), phostent->h_addr, phostent->h_length); +#endif + return server_sin.sin_addr.s_addr; +} + +#ifdef WIN32 +InitWinsock::InitWinsock() { + WORD version = MAKEWORD (1,1); + WSADATA wsadata; + WSAStartup (version, &wsadata); +} + +InitWinsock::~InitWinsock() { + WSACleanup(); +} + +#endif + +#ifndef WIN32 +const char * itoa(int value) { + static char temp[_ITOA_BUFLEN]; + memset(temp, 0, _ITOA_BUFLEN); + snprintf(temp, _ITOA_BUFLEN,"%d", value); + return temp; +} + + +char * itoa(int value, char *result, int base) { + char *ptr1, *ptr2; + char c; + int tmp_value; + + //need a valid base + if (base < 2 || base > 36) { + *result = '\0'; + return result; + } + + ptr1 = ptr2 = result; + do { + tmp_value = value; + value /= base; + + *ptr1++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz" [35 + (tmp_value - value * base)]; + } + while (value > 0); + + //apply a negative sign if need be + if (tmp_value < 0) + *ptr1++ = '-'; + + *ptr1-- = '\0'; + while (ptr2 < ptr1) { + c = *ptr1; + *ptr1-- = *ptr2; + *ptr2++ = c; + } + + return result; +} +#endif + +/* + * solar: generate a random integer in the range low-high + * this should be used instead of the rand()%limit method + */ +int MakeRandomInt(int low, int high) +{ + return (int)MakeRandomFloat((double)low, (double)high + 0.999); +} +int32 hextoi(char* num) { + int len = strlen(num); + if (len < 3) + return 0; + + if (num[0] != '0' || (num[1] != 'x' && num[1] != 'X')) + return 0; + + int32 ret = 0; + int mul = 1; + for (int i=len-1; i>=2; i--) { + if (num[i] >= 'A' && num[i] <= 'F') + ret += ((num[i] - 'A') + 10) * mul; + else if (num[i] >= 'a' && num[i] <= 'f') + ret += ((num[i] - 'a') + 10) * mul; + else if (num[i] >= '0' && num[i] <= '9') + ret += (num[i] - '0') * mul; + else + return 0; + mul *= 16; + } + return ret; +} + +int64 hextoi64(char* num) { + int len = strlen(num); + if (len < 3) + return 0; + + if (num[0] != '0' || (num[1] != 'x' && num[1] != 'X')) + return 0; + + int64 ret = 0; + int mul = 1; + for (int i=len-1; i>=2; i--) { + if (num[i] >= 'A' && num[i] <= 'F') + ret += ((num[i] - 'A') + 10) * mul; + else if (num[i] >= 'a' && num[i] <= 'f') + ret += ((num[i] - 'a') + 10) * mul; + else if (num[i] >= '0' && num[i] <= '9') + ret += (num[i] - '0') * mul; + else + return 0; + mul *= 16; + } + return ret; +} + +float MakeRandomFloat(float low, float high) { + // Handle edge case where range is zero or inverted + float diff = high - low; + if(!diff) return low; + + if (low == high) return low; + if (low > high) std::swap(low, high); + + // Use a thread-local random generator for thread safety + thread_local std::mt19937 generator(std::random_device{}()); // Seed once per thread + std::uniform_real_distribution distribution(low, high); + + return distribution(generator); +} + +int32 GenerateEQ2Color(float* r, float* g, float* b){ + int8 rgb[4] = {0}; + rgb[0] = (int8)((*r)*255); + rgb[1] = (int8)((*b)*255); + rgb[2] = (int8)((*g)*255); + int32 color = 0; + memcpy(&color, rgb, sizeof(int32)); + return color; +} +int32 GenerateEQ2Color(float* rgb[3]){ + return GenerateEQ2Color(rgb[0], rgb[1], rgb[2]); +} +int8 MakeInt8(float* input){ + float input2 = *input; + if(input2 < 0) + input2 *= -1; + return (int8)(input2*255); +} + +vector* SplitString(string str, char delim){ + vector* results = new vector; + int32 pos; + while((pos = str.find_first_of(delim))!= str.npos){ + if(pos > 0){ + results->push_back(str.substr(0,pos)); + } + if(str.length() > pos) + str = str.substr(pos+1); + else + break; + } + if(str.length() > 0) + results->push_back(str); + return results; +} + +bool Unpack(uchar* data, uchar* dst, int16 dstLen, int16 version, bool reverse){ + int32 srcLen = 0; + memcpy(&srcLen, data, sizeof(int32)); + return Unpack(srcLen, data + 4, dst, dstLen, version, reverse); +} +bool Unpack(int32 srcLen, uchar* data, uchar* dst, int16 dstLen, int16 version, bool reverse) { +// int32 srcLen = 0; +// memcpy(&srcLen, data, sizeof(int32)); +// data+=4; + if(reverse) + Reverse(data, srcLen); + int16 pos = 0; + int16 real_pos = 0; + while(srcLen && pos < dstLen) { + if(srcLen >= 0 && !srcLen--) + return false; + int8 code = data[real_pos++]; + + if(code >= 128) { + for(int8 index=0; index<7; index++) { + if(code & 1) { + if(pos >= dstLen) + return false; + if(srcLen >= 0 && !srcLen--) + return false; + dst[pos++] = data[real_pos++]; + } else { + if(pos < dstLen) dst[pos++] = 0; + } + code >>= 1; + } + } else { + if(pos + code > dstLen) + return false; + memset(dst+pos, 0, code); + pos+=code; + } + } + return srcLen <= 0; +} + +int32 Pack(uchar* data, uchar* src, int16 srcLen, int16 dstLen, int16 version, bool reverse) { + int16 real_pos = 4; + int32 pos = 0; + int32 code = 0; + int codePos = 0; + int codeLen = 0; + int8 zeroLen = 0; + memset(data,0,dstLen); + if (version > 1 && version <= 374) + reverse = false; + while(pos < srcLen) { + if(src[pos] || codeLen) { + if(!codeLen) { + /*if(zeroLen > 5) { + data[real_pos++] = zeroLen; + zeroLen = 0; + } + else if(zeroLen >= 1 && zeroLen<=5){ + for(;zeroLen>0;zeroLen--) + codeLen++; + }*/ + if (zeroLen) { + data[real_pos++] = zeroLen; + zeroLen = 0; + } + codePos = real_pos; + code = 0; + data[real_pos++] = 0; + } + if(src[pos]) { + data[real_pos++] = src[pos]; + code |= 0x80; + } + code >>= 1; + codeLen++; + + if(codeLen == 7) { + data[codePos] = int8(0x80 | code); + codeLen = 0; + } + } else { + if(zeroLen == 0x7F) { + data[real_pos++] = zeroLen; + zeroLen = 0; + } + zeroLen++; + } + pos++; + } + if(codeLen) { + code >>= (7 - codeLen); + data[codePos] = int8(0x80 | code); + } else if(zeroLen) { + data[real_pos++] = zeroLen; + } + if(reverse) + Reverse(data + 4, real_pos - 4); + int32 dataLen = real_pos - 4; + memcpy(&data[0], &dataLen, sizeof(int32)); + return dataLen + 4; +} +void Reverse(uchar* input, int32 srcLen){ + int16 real_pos = 0; + int16 orig_pos = 0; + int8 reverse_count = 0; + while(srcLen > 0 && srcLen < 0xFFFFFFFF){ // XXX it was >=0 before. but i think it was a bug + int8 code = input[real_pos++]; + srcLen--; + if(code >= 128) { + for(int8 index=0; index<7; index++) { + if(code & 1) { + if(srcLen >= 0 && !srcLen--) + return; + real_pos++; + reverse_count++; + } + code >>= 1; + } + } + if(reverse_count > 0){ + int8 tmp_data[8] = {0}; + for(int8 i=0;i 0){ + ret = atoul(input.c_str()); + } + } + catch(...){} + return ret; +} + +int64 ParseLongLongValue(string input){ + int64 ret = 0xFFFFFFFFFFFFFFFF; + try{ + if(input.length() > 0){ +#ifdef WIN32 + ret = _strtoui64(input.c_str(), NULL, 10); +#else + ret = strtoull(input.c_str(), 0, 10); +#endif + } + } + catch(...){} + return ret; +} + +map TranslateBrokerRequest(string request){ + map ret; + string key; + string value; + int32 start_pos = 0; + int32 end_pos = 0; + int32 pos = request.find("="); + bool str_val = false; + while(pos < 0xFFFFFFFF){ + str_val = false; + key = request.substr(start_pos, pos-start_pos); + if(request.find("|", pos) == pos+1){ + pos++; + end_pos = request.find("|", pos+1); + str_val = true; + } + else + end_pos = request.find(" ", pos); + if(end_pos < 0xFFFFFFFF){ + value = request.substr(pos+1, end_pos-pos-1); + start_pos = end_pos+1; + if(str_val){ + start_pos++; + ret[key] = ToLower(value); + } + else + ret[key] = value; + pos = request.find("=", start_pos); + } + else{ + value = request.substr(pos+1); + if(str_val){ + start_pos++; + ret[key] = ToLower(value); + } + else + ret[key] = value; + break; + } + } + return ret; +} + +int8 CheckOverLoadSize(int32 val){ + int8 ret = 1; + if(val >= 0xFFFF) //int32 + ret = sizeof(int16) + sizeof(int32); + else if(val >= 0xFF) + ret = sizeof(int8) + sizeof(int16); + return ret; +} + +int8 DoOverLoad(int32 val, uchar* data){ + int8 ret = 1; + if(val >= 0xFFFF){ //int32 + memset(data, 0xFF, sizeof(int16)); + memcpy(data + sizeof(int16), &val, sizeof(int32)); + ret = sizeof(int16) + sizeof(int32); + } + else if(val >= 0xFF){ //int16 + memset(data, 0xFF, sizeof(int8)); + memcpy(data + sizeof(int8), &val, sizeof(int16)); + ret = sizeof(int8) + sizeof(int16); + } + else + memcpy(data, &val, sizeof(int8)); + return ret; +} + +/* Treats contiguous spaces as one space. */ +int32 CountWordsInString(const char* text) { + int32 words = 0; + if (text && strlen(text) > 0) { + bool on_word = false; + for (int32 i = 0; i < strlen(text); i++) { + char letter = text[i]; + if (on_word && !((letter >= 48 && letter <= 57) || (letter >= 65 && letter <= 90) || (letter >= 97 && letter <= 122))) + on_word = false; + else if (!on_word && ((letter >= 48 && letter <= 57) || (letter >= 65 && letter <= 90) || (letter >= 97 && letter <= 122))){ + on_word = true; + words++; + } + } + } + return words; +} + +bool IsNumber(const char *num) { + size_t len, i; + + if (!num) + return false; + + len = strlen(num); + if (len == 0) + return false; + + for (i = 0; i < len; i++) { + if (!isdigit(num[i])) + return false; + } + + return true; +} + +void PrintSep(Seperator *sep, const char *name) { + int32 i = 0; + + LogWrite(MISC__DEBUG, 0, "Misc", "Printing sep %s", name ? name : "No Name"); + if (!sep) + LogWrite(MISC__DEBUG, 0, "Misc", "\tSep is null"); + else { + while (sep->arg[i] && strlen(sep->arg[i]) > 0) { + LogWrite(MISC__DEBUG, 0, "Misc", "\t%i => %s", i, sep->arg[i]); + i++; + } + } +} + +#define INI_IGNORE(c) (c == '\n' || c == '\r' || c == '#') + +static bool INIGoToSection(FILE *f, const char *section) { + size_t size = strlen(section) + 3; + char line[256], *buf, *tmp; + bool found = false; + + if ((buf = (char *)malloc(size)) == NULL) { + fprintf(stderr, "%s: %u: Unable to allocate %zu bytes\n", __FUNCTION__, __LINE__, size); + return false; + } + + sprintf(buf, "[%s]", section); + + while (fgets(line, sizeof(line), f) != NULL) { + if (INI_IGNORE(line[0])) + continue; + + if (line[0] == '[') { + if ((tmp = strstr(line, "\n")) != NULL) + *tmp = '\0'; + if ((tmp = strstr(line, "\r")) != NULL) + *tmp = '\0'; + + if (strcasecmp(buf, line) == 0) { + found = true; + break; + } + } + } + + free(buf); + return found; +} + +static char * INIFindValue(FILE *f, const char *section, const char *property) { + char line[256], *key, *val; + + if (section != NULL && !INIGoToSection(f, section)) + return NULL; + + while (fgets(line, sizeof(line), f) != NULL) { + if (INI_IGNORE(line[0])) + continue; + + if (section != NULL && line[0] == '[') + return NULL; + + if ((key = strtok(line, "=")) == NULL) + continue; + + if (strcasecmp(key, property) == 0) { + val = strtok(NULL, "\n\r"); + + if (val == NULL) + return NULL; + + return strdup(val); + } + } + + return NULL; +} + +bool INIReadInt(FILE *f, const char *section, const char *property, int *out) { + char *value; + + rewind(f); + + if ((value = INIFindValue(f, section, property)) == NULL) + return false; + + if (!IsNumber(value)) { + free(value); + return false; + } + + *out = atoi(value); + free(value); + + return true; +} + +bool INIReadBool(FILE *f, const char *section, const char *property, bool *out) { + char *value; + + rewind(f); + + if ((value = INIFindValue(f, section, property)) == NULL) + return false; + + *out = (strcasecmp(value, "1") == 0 || strcasecmp(value, "true") == 0 || strcasecmp(value, "on") == 0 || strcasecmp(value, "yes") == 0); + free(value); + + return true; + +} +string GetDeviceName(string device) { + if (device == "chemistry_table") + device = "Chemistry Table"; + else if (device == "work_desk") + device = "Engraved Desk"; + else if (device == "forge") + device = "Forge"; + else if (device == "stove and keg") + device = "Stove & Keg"; + else if (device == "sewing_table") + device = "Sewing Table & Mannequin"; + else if (device == "woodworking_table") + device = "Woodworking Table"; + else if (device == "work_bench") + device = "Work Bench"; + else if (device == "crafting_intro_anvil") + device = "Mender's Anvil"; + return device; +} + +int32 GetDeviceID(string device) { + if (device == "Chemistry Table") + return 3; + else if (device == "Engraved Desk") + return 4; + else if (device == "Forge") + return 2; + else if (device == "Stove & Keg") + return 7; + else if (device == "Sewing Table & Mannequin") + return 1; + else if (device == "Woodworking Table") + return 6; + else if (device == "Work Bench") + return 5; + else if (device == "Mender's Anvil") + return 0xFFFFFFFF; + return 0; +} +int16 GetItemPacketType(int32 version) { + int16 item_version; + if (version >= 64707) + item_version = 0x5CFE; + else if (version >= 63119) + item_version = 0x56FE; + else if (version >= 60024) + item_version = 0x51FE; + else if (version >= 57107) + item_version = 0x4CFE; + else if (version >= 57048) + item_version = 0x48FE; + else if (version >= 1199) + item_version = 0x44FE; + else if (version >= 1195) + item_version = 0x40FE; + else if (version >= 1193) + item_version = 0x3FFE; + else if (version >= 1190) + item_version = 0x3EFE; + else if (version >= 1188) + item_version = 0x3DFE; + else if (version >= 1096) + item_version = 0x35FE; + else if (version >= 1027) + item_version = 0x31FE; + else if (version >= 1008) + item_version = 0x2CFE; + else if (version >= 927) + item_version = 0x23FE; + else if (version >= 893) + item_version = 0x22FE; + else if (version >= 860) + item_version = 0x20FE; + else if (version > 546) + item_version = 0x1CFE; + else + item_version = 0; + + return item_version; +} + +#ifndef PATCHER +int16 GetOpcodeVersion(int16 version) { + int16 ret = version; + int16 version1 = 0; + int16 version2 = 0; + map::iterator itr; + for (itr = EQOpcodeVersions.begin(); itr != EQOpcodeVersions.end(); itr++) { + version1 = itr->first; + version2 = itr->second; + if (version >= version1 && version <= version2) { + ret = version1; + break; + } + } + + return ret; +} +#endif + +void SleepMS(int32 milliseconds) { +#if defined(_WIN32) + Sleep(milliseconds); +#else + usleep(milliseconds * 1000); +#endif +} + +size_t +strlcpy(char *dst, const char *src, size_t size) { + char *d = dst; + const char *s = src; + size_t n = size; + + if (n != 0 && --n != 0) { + do { + if ((*d++ = *s++) == 0) + break; + } while (--n != 0); + } + + if (n == 0) { + if (size != 0) + *d = '\0'; + while (*s++) + ; + } + + return(s - src - 1); +} + +float short_to_float(const ushort x) { // IEEE-754 16-bit floating-point format (without infinity): 1-5-10, exp-15, +-131008.0, +-6.1035156E-5, +-5.9604645E-8, 3.311 digits + const uint32 e = (x & 0x7C00) >> 10; // exponent + const uint32 m = (x & 0x03FF) << 13; // mantissa + const uint32 v = as_uint((float)m) >> 23; // evil log2 bit hack to count leading zeros in denormalized format + return as_float((x & 0x8000) << 16 | (e != 0) * ((e + 112) << 23 | m) | ((e == 0) & (m != 0)) * ((v - 37) << 23 | ((m << (150 - v)) & 0x007FE000))); // sign : normalized : denormalized +} + +uint32 float_to_int(const float x) { // IEEE-754 16-bit floating-point format (without infinity): 1-5-10, exp-15, +-131008.0, +-6.1035156E-5, +-5.9604645E-8, 3.311 digits + const uint32 b = as_uint(x) + 0x00001000; // round-to-nearest-even: add last bit after truncated mantissa + const uint32 e = (b & 0x7F800000) >> 23; // exponent + const uint32 m = b & 0x007FFFFF; // mantissa; in line below: 0x007FF000 = 0x00800000-0x00001000 = decimal indicator flag - initial rounding + return (b & 0x80000000) >> 16 | (e > 112)* ((((e - 112) << 10) & 0x7C00) | m >> 13) | ((e < 113) & (e > 101))* ((((0x007FF000 + m) >> (125 - e)) + 1) >> 1) | (e > 143) * 0x7FFF; // sign : normalized : denormalized : saturate +} + +uint32 as_uint(const float x) { + return *(uint32*)&x; +} + +float as_float(const uint32 x) { + return *(float*)&x; +} + +// Function to get the current timestamp in milliseconds +int64 getCurrentTimestamp() { + auto now = std::chrono::steady_clock::now(); + auto duration = std::chrono::duration_cast(now.time_since_epoch()); + return duration.count(); +} + +std::tuple convertTimestampDuration(int64 total_milliseconds) { + std::chrono::milliseconds duration(total_milliseconds); + // Convert to days, hours, minutes, and seconds + auto hours = std::chrono::duration_cast(duration); + duration -= hours; + + auto days = hours / 24; + hours -= days * 24; + + auto minutes = std::chrono::duration_cast(duration); + duration -= minutes; + + auto seconds = std::chrono::duration_cast(duration); + + // Return the result as a tuple + return std::make_tuple(days.count(), hours.count(), minutes.count(), seconds.count()); +} \ No newline at end of file diff --git a/old/MiscFunctions.h b/old/MiscFunctions.h new file mode 100644 index 0000000..b9f39a6 --- /dev/null +++ b/old/MiscFunctions.h @@ -0,0 +1,228 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#ifndef MISCFUNCTIONS_H +#define MISCFUNCTIONS_H + +#include "types.h" +#include "seperator.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef ERRBUF_SIZE +#define ERRBUF_SIZE 1024 +#endif + +//int MakeAnyLenString(char** ret, const char* format, ...); +int32 hextoi(char* num); +int64 hextoi64(char* num); +sint32 filesize(FILE* fp); +int32 ResolveIP(const char* hostname, char* errbuf = 0); +void CoutTimestamp(bool ms = true); +//char* strn0cpy(char* dest, const char* source, int32 size); + // return value =true if entire string(source) fit, false if it was truncated +//bool strn0cpyt(char* dest, const char* source, int32 size); +string loadInt32String(uchar* buffer, int16 buffer_size, int16* pos, EQ2_32BitString* eq_string = NULL); +string loadInt16String(uchar* buffer, int16 buffer_size, int16* pos, EQ2_16BitString* eq_string = NULL); +string loadInt8String(uchar* buffer, int16 buffer_size, int16* pos, EQ2_8BitString* eq_string = NULL); +sint16 storeInt32String(uchar* buffer, int16 buffer_size, string in_str); +sint16 storeInt16String(uchar* buffer, int16 buffer_size, string in_str); +sint16 storeInt8String(uchar* buffer, int16 buffer_size, string in_str); +int MakeRandomInt(int low, int high); +float MakeRandomFloat(float low, float high); + +float TransformToFloat(sint16 data, int8 bits); +sint16 TransformFromFloat(float data, int8 bits); + +int32 GenerateEQ2Color(float r, float g, float b); +int32 GenerateEQ2Color(float* rgb[3]); +void SetColor(EQ2_Color* color, long data); +//void CreateEQ2Color(EQ2_Color* color, uchar* data, int16* size); +int8 MakeInt8(uchar* data, int16* size); +int8 MakeInt8(float* input); +bool Unpack(int32 srcLen, uchar* data, uchar* dst, int16 dstLen, int16 version = 0, bool reverse = true); +bool Unpack(uchar* data, uchar* dst, int16 dstLen, int16 version = 0, bool reverse = true); +int32 Pack(uchar* data, uchar* src, int16 srcLen, int16 dstLen, int16 version = 0, bool reverse = true); +void Reverse(uchar* input, int32 srcLen); +void Encode(uchar* dst, uchar* src, int16 len); +void Decode(uchar* dst, uchar* src, int16 len); +string ToUpper(string input); +string ToLower(string input); +int32 ParseIntValue(string input); +int64 ParseLongLongValue(string input); +map TranslateBrokerRequest(string request); +void MovementDecode(uchar* dst, uchar* newval, uchar* orig, int16 len); +vector* SplitString(string str, char delim); +int8 DoOverLoad(int32 val, uchar* data); +int8 CheckOverLoadSize(int32 val); +int32 CountWordsInString(const char* text); +bool IsNumber(const char *num); +void PrintSep(Seperator *sep, const char *name = 0); +string GetDeviceName(string device); +int32 GetDeviceID(string device); +///Gets the packet type for the given version +///The client version +int16 GetItemPacketType(int32 version); +///Gets the opcode version_range1 from the clients version +///The client version +int16 GetOpcodeVersion(int16 version); +void SleepMS(int32 milliseconds); +size_t strlcpy(char *dst, const char *src, size_t size); + +float short_to_float(const ushort x); +uint32 float_to_int(const float x); +uint32 as_uint(const float x); +float as_float(const uint32 x); + +int64 getCurrentTimestamp(); +std::tuple convertTimestampDuration(int64 total_milliseconds); + +bool INIReadBool(FILE *f, const char *section, const char *property, bool *out); +bool INIReadInt(FILE *f, const char *section, const char *property, int *out); + +static bool IsPrivateAddress(uint32_t ip) +{ + uint8_t b1, b2;//, b3, b4; + b1 = (uint8_t)(ip >> 24); + b2 = (uint8_t)((ip >> 16) & 0x0ff); + //b3 = (uint8_t)((ip >> 8) & 0x0ff); + //b4 = (uint8_t)(ip & 0x0ff); + + // 10.x.y.z + if (b1 == 10) + return true; + + // 172.16.0.0 - 172.31.255.255 + if ((b1 == 172) && (b2 >= 16) && (b2 <= 31)) + return true; + + // 192.168.0.0 - 192.168.255.255 + if ((b1 == 192) && (b2 == 168)) + return true; + + return false; +} + +static std::string FormatCoinReceiveMessage(int64 total_copper, const std::string& reason) { + // breakdown into denominations + int64 platinum = total_copper / 1'000'000; + int64 rem = total_copper % 1'000'000; + int64 gold = rem / 10'000; + rem %= 10'000; + int64 silver = rem / 100; + int64 copper = rem % 100; + + std::ostringstream oss; + oss << "You received "; + + bool first = true; + if (platinum > 0) { + oss << platinum << " platinum"; + first = false; + } + if (gold > 0) { + if (!first) oss << ", "; + oss << gold << " gold"; + first = false; + } + if (silver > 0) { + if (!first) oss << ", "; + oss << silver << " silver"; + first = false; + } + // if nothing else or there's copper, show copper + if (copper > 0 || first) { + if (!first) oss << ", "; + oss << copper << " copper"; + } + + oss << " earned through " << reason; + return oss.str(); +} + + +template void AddData(Type input, string* datastring){ + if(datastring) + datastring->append((char*)&input, sizeof(input)); +} + +template void AddData(Type input, int32 array_size, string* datastring){ + if(array_size>0){ + for(int32 i=0;i class AutoDelete { +public: + AutoDelete(T** iVar, T* iSetTo = 0) { + init(iVar, iSetTo); + } + AutoDelete() {} + void init(T** iVar, T* iSetTo = 0) + { + pVar = iVar; + if (iSetTo) + *pVar = iSetTo; + } + ~AutoDelete() { + safe_delete(*pVar); + } +private: + T** pVar; +}; + +class VersionRange { +public: + VersionRange(int32 in_min_version, int32 in_max_version) + { + min_version = in_min_version; + max_version = in_max_version; + } + int32 GetMinVersion() { return min_version; } + int32 GetMaxVersion() { return max_version; } +private: + int32 min_version; + int32 max_version; +}; +#endif + + diff --git a/old/Mutex.cpp b/old/Mutex.cpp new file mode 100644 index 0000000..732e451 --- /dev/null +++ b/old/Mutex.cpp @@ -0,0 +1,361 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#include "../common/Log.h" +#include "../common/debug.h" +#include "../common/Mutex.h" + +Mutex::Mutex() { + readers = 0; + mlocked = false; + writing = false; + name = ""; +#ifdef DEBUG + stack.clear(); +#endif + //CSLock is a pointer so we can use a different attribute type on create + CSLock = new CriticalSection(MUTEX_ATTRIBUTE_RECURSIVE); +} + +Mutex::~Mutex() { + safe_delete(CSLock); +#ifdef DEBUG + stack.clear(); +#endif +} + +void Mutex::SetName(string in_name) { +#ifdef DEBUG + name = in_name; +#endif +} + +void Mutex::lock() { +#ifdef DEBUG + int i = 0; +#endif + if (name.length() > 0) { + while (mlocked) { +#ifdef DEBUG + if (i > MUTEX_TIMEOUT_MILLISECONDS) { + LogWrite(MUTEX__ERROR, 0, "Mutex", "Possible deadlock attempt by '%s'!", name.c_str()); + return; + } + i++; +#endif + Sleep(1); + } + } + mlocked = true; + CSLock->lock(); +} + +bool Mutex::trylock() { + return CSLock->trylock(); +} + +void Mutex::unlock() { + CSLock->unlock(); + mlocked = false; +} + +void Mutex::readlock(const char* function, int32 line) { +#ifdef DEBUG + int32 i = 0; +#endif + while (true) { + //Loop until there isn't a writer, then we can read! + CSRead.lock(); + if (!writing) { + readers++; + CSRead.unlock(); +#ifdef DEBUG + CSStack.lock(); + if (function) + stack[(string)function]++; + CSStack.unlock(); +#endif + return; + } + CSRead.unlock(); +#ifdef DEBUG + if (i > MUTEX_TIMEOUT_MILLISECONDS) { + LogWrite(MUTEX__ERROR, 0, "Mutex", "The mutex %s called from %s at line %u timed out waiting for a readlock!", name.c_str(), function ? function : "name_not_provided", line); + LogWrite(MUTEX__ERROR, 0, "Mutex", "The following functions had locks:"); + map::iterator itr; + CSStack.lock(); + for (itr = stack.begin(); itr != stack.end(); itr++) { + if (itr->second > 0 && itr->first.length() > 0) + LogWrite(MUTEX__ERROR, 0, "Mutex", "%s, number of locks = %u", itr->first.c_str(), itr->second); + } + CSStack.unlock(); + i = 0; + continue; + } + i++; +#endif + Sleep(1); + } +} + +void Mutex::releasereadlock(const char* function, int32 line) { + //Wait for the readcount lock + CSRead.lock(); + //Lower the readcount by one, when readcount is 0 writers may start writing + readers--; + CSRead.unlock(); +#ifdef DEBUG + CSStack.lock(); + if (function) { + map::iterator itr = stack.find((string)function); + if (itr != stack.end()) { + if (--(itr->second) == 0) { + stack.erase(itr); + } + } + } + CSStack.unlock(); +#endif +} + +bool Mutex::tryreadlock(const char* function) { + //This returns true if able to instantly obtain a readlock, false if not + CSRead.lock(); + if (!writing) { + readers++; + CSRead.unlock(); + } + else { + CSRead.unlock(); + return false; + } + +#ifdef DEBUG + CSStack.lock(); + if (function) + stack[(string)function]++; + CSStack.unlock(); +#endif + + return true; +} + +void Mutex::writelock(const char* function, int32 line) { + //Wait until the writer lock becomes available, then we can be the only writer! +#ifdef DEBUG + int32 i = 0; +#endif + while (!CSWrite.trylock()) { +#ifdef DEBUG + if (i > MUTEX_TIMEOUT_MILLISECONDS) { + LogWrite(MUTEX__ERROR, 0, "Mutex", "The mutex %s called from %s at line %u timed out waiting on another writelock!", name.c_str(), function ? function : "name_not_provided", line); + LogWrite(MUTEX__ERROR, 0, "Mutex", "The following functions had locks:"); + map::iterator itr; + CSStack.lock(); + for (itr = stack.begin(); itr != stack.end(); itr++) { + if (itr->second > 0 && itr->first.length() > 0) + LogWrite(MUTEX__ERROR, 0, "Mutex", "%s, number of locks = %u", itr->first.c_str(), itr->second); + } + CSStack.unlock(); + i = 0; + continue; + } + i++; +#endif + Sleep(1); + } + waitReaders(function, line); +#ifdef DEBUG + CSStack.lock(); + if (function) + stack[(string)function]++; + CSStack.unlock(); +#endif +} + +void Mutex::releasewritelock(const char* function, int32 line) { + //Wait for the readcount lock + CSRead.lock(); + //Readers are aloud again + writing = false; + CSRead.unlock(); + //Allow other writers to write + CSWrite.unlock(); +#ifdef DEBUG + CSStack.lock(); + if (function) { + map::iterator itr = stack.find((string)function); + if (itr != stack.end()) { + if (--(itr->second) == 0) { + stack.erase(itr); + } + } + } + CSStack.unlock(); +#endif +} + +bool Mutex::trywritelock(const char* function) { + //This returns true if able to instantly obtain a writelock, false if not + if (CSWrite.trylock()) { + CSRead.lock(); + if (readers == 0) + writing = true; + CSRead.unlock(); + if (!writing) { + CSWrite.unlock(); + return false; + } + } + else + return false; + +#ifdef DEBUG + CSStack.lock(); + if (function) + stack[(string)function]++; + CSStack.unlock(); +#endif + + return true; +} + +void Mutex::waitReaders(const char* function, int32 line) +{ + //Wait for all current readers to stop, then we can write! +#ifdef DEBUG + int32 i = 0; +#endif + while (true) + { + CSRead.lock(); + if (readers == 0) + { + writing = true; + CSRead.unlock(); + break; + } + CSRead.unlock(); +#ifdef DEBUG + if (i > MUTEX_TIMEOUT_MILLISECONDS) { + LogWrite(MUTEX__ERROR, 0, "Mutex", "The mutex %s called from %s at line %u timed out while waiting on readers!", name.c_str(), function ? function : "name_not_provided", line); + LogWrite(MUTEX__ERROR, 0, "Mutex", "The following functions had locks:"); + map::iterator itr; + CSStack.lock(); + for (itr = stack.begin(); itr != stack.end(); itr++) { + if (itr->second > 0 && itr->first.length() > 0) + LogWrite(MUTEX__ERROR, 0, "Mutex", "%s, number of locks = %u", itr->first.c_str(), itr->second); + } + CSStack.unlock(); + i = 0; + continue; + } + i++; +#endif + Sleep(1); + } +} + +LockMutex::LockMutex(Mutex* in_mut, bool iLock) { + mut = in_mut; + locked = iLock; + if (locked) { + mut->lock(); + } +} + +LockMutex::~LockMutex() { + if (locked) { + mut->unlock(); + } +} + +void LockMutex::unlock() { + if (locked) + mut->unlock(); + locked = false; +} + +void LockMutex::lock() { + if (!locked) + mut->lock(); + locked = true; +} + +CriticalSection::CriticalSection(int attribute) { +#ifdef WIN32 + InitializeCriticalSection(&CSMutex); +#else + pthread_mutexattr_init(&type_attribute); + switch (attribute) + { + case MUTEX_ATTRIBUTE_FAST: + pthread_mutexattr_settype(&type_attribute, PTHREAD_MUTEX_FAST_NP); + break; + case MUTEX_ATTRIBUTE_RECURSIVE: + pthread_mutexattr_settype(&type_attribute, PTHREAD_MUTEX_RECURSIVE_NP); + break; + case MUTEX_ATTRIBUTE_ERRORCHK: + pthread_mutexattr_settype(&type_attribute, PTHREAD_MUTEX_ERRORCHECK_NP); + break; + default: + LogWrite(MUTEX__DEBUG, 0, "Critical Section", "Invalid mutex attribute type! Using PTHREAD_MUTEX_FAST_NP"); + pthread_mutexattr_settype(&type_attribute, PTHREAD_MUTEX_FAST_NP); + break; + } + pthread_mutex_init(&CSMutex, &type_attribute); +#endif +} + +CriticalSection::~CriticalSection() { +#ifdef WIN32 + DeleteCriticalSection(&CSMutex); +#else + pthread_mutex_destroy(&CSMutex); + pthread_mutexattr_destroy(&type_attribute); +#endif +} + +void CriticalSection::lock() { + //Waits for a lock on this critical section +#ifdef WIN32 + EnterCriticalSection(&CSMutex); +#else + pthread_mutex_lock(&CSMutex); +#endif +} + +void CriticalSection::unlock() { + //Gets rid of one of the current thread's locks on this critical section +#ifdef WIN32 + LeaveCriticalSection(&CSMutex); +#else + pthread_mutex_unlock(&CSMutex); +#endif +} + +bool CriticalSection::trylock() { + //Returns true if able to instantly get a lock on this critical section, false if not +#ifdef WIN32 + return TryEnterCriticalSection(&CSMutex); +#else + return (pthread_mutex_trylock(&CSMutex) == 0); +#endif +} + diff --git a/old/Mutex.h b/old/Mutex.h new file mode 100644 index 0000000..ae96333 --- /dev/null +++ b/old/Mutex.h @@ -0,0 +1,103 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#ifndef MYMUTEX_H +#define MYMUTEX_H +#ifdef WIN32 + #include + #include +#else + #include + #include "../common/unix.h" +#endif +#include "../common/types.h" +#include +#include + +#define MUTEX_ATTRIBUTE_FAST 1 +#define MUTEX_ATTRIBUTE_RECURSIVE 2 +#define MUTEX_ATTRIBUTE_ERRORCHK 3 +#define MUTEX_TIMEOUT_MILLISECONDS 10000 + +class CriticalSection { +public: + CriticalSection(int attribute = MUTEX_ATTRIBUTE_FAST); + ~CriticalSection(); + void lock(); + void unlock(); + bool trylock(); +private: +#ifdef WIN32 + CRITICAL_SECTION CSMutex; +#else + pthread_mutex_t CSMutex; + pthread_mutexattr_t type_attribute; +#endif +}; + +class Mutex { +public: + Mutex(); + ~Mutex(); + + void lock(); + void unlock(); + bool trylock(); + + void readlock(const char* function = 0, int32 line = 0); + void releasereadlock(const char* function = 0, int32 line = 0); + bool tryreadlock(const char* function = 0); + + void writelock(const char* function = 0, int32 line = 0); + void releasewritelock(const char* function = 0, int32 line = 0); + bool trywritelock(const char* function = 0); + + void waitReaders(const char* function = 0, int32 line = 0); + + void SetName(string in_name); +private: + CriticalSection CSRead; + CriticalSection CSWrite; + CriticalSection* CSLock; + +#ifdef DEBUG //Used for debugging only + CriticalSection CSStack; + map stack; +#endif + + int readers; + bool writing; + volatile bool mlocked; + string name; +}; + + +class LockMutex { +public: + LockMutex(Mutex* in_mut, bool iLock = true); + ~LockMutex(); + void unlock(); + void lock(); +private: + bool locked; + Mutex* mut; +}; + + +#endif diff --git a/old/PacketStruct.cpp b/old/PacketStruct.cpp new file mode 100644 index 0000000..6dcdff2 --- /dev/null +++ b/old/PacketStruct.cpp @@ -0,0 +1,2742 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#include +#include +#include "PacketStruct.h" +#include "ConfigReader.h" +#include "../common/debug.h" +#include "MiscFunctions.h" +#include "Log.h" + +#ifdef WORLD +#include "../WorldServer/Items/Items.h" +#include "../WorldServer/Player.h" +#include "../WorldServer/World.h" +#endif + +extern ConfigReader configReader; +using namespace std; + +DataStruct::DataStruct() { + item_size = 0; + type = 0; + type2 = 0; + length = 1; + if_flag_set = false; + if_flag_not_set = false; + if_set = false; + if_not_set = false; + if_not_equals = false; + if_equals = false; + is_set = false; + optional = false; + oversized = 0; + oversized_byte = 0; + add = false; + addType = 0; + maxArraySize = 0; + default_value = 0; +} +DataStruct::DataStruct(DataStruct* data_struct) { + type = data_struct->GetType(); + type2 = data_struct->GetType2(); + length = data_struct->GetLength(); + name = data_struct->GetName(); + array_size_variable = data_struct->array_size_variable; + default_value = data_struct->default_value; + add = true; + addType = type; + oversized = data_struct->GetOversized(); + oversized_byte = data_struct->GetOversizedByte(); + maxArraySize = data_struct->GetMaxArraySize(); + if_flag_set = false; + if_flag_not_set = false; + if_set = false; + if_not_set = false; + if_not_equals = false; + if_equals = false; + optional = false; + if (data_struct->GetIfSet()) + SetIfSetVariable(data_struct->GetIfSetVariable()); + if (data_struct->GetIfNotSet()) + SetIfNotSetVariable(data_struct->GetIfNotSetVariable()); + if (data_struct->GetIfNotEquals()) + SetIfNotEqualsVariable(data_struct->GetIfNotEqualsVariable()); + if (data_struct->GetIfEquals()) + SetIfEqualsVariable(data_struct->GetIfEqualsVariable()); + if (data_struct->GetIfFlagSet()) + SetIfFlagSetVariable(data_struct->GetIfFlagSetVariable()); + if (data_struct->GetIfFlagNotSet()) + SetIfFlagNotSetVariable(data_struct->GetIfFlagNotSetVariable()); + item_size = 0; + is_set = false; +} +DataStruct::DataStruct(const char* new_name, const char* new_type, int32 new_length, const char* new_type2) { + name = string(new_name); + type = 0; + type2 = 0; + SetType(new_type, &type); + if (new_type2) + SetType(new_type2, &type2); + length = new_length; + add = true; + addType = type; + if_set = false; + if_not_set = false; + is_set = false; + if_not_equals = false; + item_size = 0; +} +const char* DataStruct::GetArraySizeVariable() { + return array_size_variable.c_str(); +} +void DataStruct::SetArraySizeVariable(const char* new_name) { + array_size_variable = string(new_name); +} +void DataStruct::SetType(const char* new_type, int8* output_type) { + if (strlen(new_type) > 3 && strncasecmp("int", new_type, 3) == 0) { + if (strncasecmp("int8", new_type, 4) == 0) + *output_type = DATA_STRUCT_INT8; + else if (strncasecmp("int16", new_type, 5) == 0) + *output_type = DATA_STRUCT_INT16; + else if (strncasecmp("int32", new_type, 5) == 0) + *output_type = DATA_STRUCT_INT32; + else if (strncasecmp("int64", new_type, 5) == 0) + *output_type = DATA_STRUCT_INT64; + } + else if (strlen(new_type) > 4 && strncasecmp("sint", new_type, 4) == 0) { + if (strncasecmp("sint8", new_type, 5) == 0) + *output_type = DATA_STRUCT_SINT8; + else if (strncasecmp("sint16", new_type, 6) == 0) + *output_type = DATA_STRUCT_SINT16; + else if (strncasecmp("sint32", new_type, 6) == 0) + *output_type = DATA_STRUCT_SINT32; + else if (strncasecmp("sint64", new_type, 6) == 0) + *output_type = DATA_STRUCT_SINT64; + } + else if (strlen(new_type) == 4 && strncasecmp("char", new_type, 4) == 0) + *output_type = DATA_STRUCT_CHAR; + else if (strlen(new_type) == 5 && strncasecmp("float", new_type, 5) == 0) + *output_type = DATA_STRUCT_FLOAT; + else if (strlen(new_type) == 6 && strncasecmp("double", new_type, 6) == 0) + *output_type = DATA_STRUCT_DOUBLE; + else if (strlen(new_type) >= 5 && strncasecmp("EQ2_", new_type, 4) == 0) { + if (strncasecmp("EQ2_8", new_type, 5) == 0) + *output_type = DATA_STRUCT_EQ2_8BIT_STRING; + else if (strncasecmp("EQ2_16", new_type, 6) == 0) + *output_type = DATA_STRUCT_EQ2_16BIT_STRING; + else if (strncasecmp("EQ2_32", new_type, 6) == 0) + *output_type = DATA_STRUCT_EQ2_32BIT_STRING; + else if (strncasecmp("EQ2_E", new_type, 5) == 0) + *output_type = DATA_STRUCT_EQUIPMENT; + else if (strncasecmp("EQ2_C", new_type, 5) == 0) + *output_type = DATA_STRUCT_COLOR; + else if (strncasecmp("EQ2_I", new_type, 5) == 0) + *output_type = DATA_STRUCT_ITEM; + } + else if (strlen(new_type) >= 5) { + if (strncasecmp("Array", new_type, 5) == 0) + *output_type = DATA_STRUCT_ARRAY; + } + else + LogWrite(PACKET__ERROR, 0, "Packet", "Invalid Type: %s", new_type); +} +DataStruct::DataStruct(const char* new_name, int32 new_length) { + name = string(new_name); + length = new_length; + if_set = false; + if_not_set = false; + is_set = false; + item_size = 0; +} +DataStruct::DataStruct(const char* new_name, int8 new_type, int32 new_length, int8 new_type2) { + name = string(new_name); + type = new_type; + length = new_length; + type2 = new_type2; + addType = type; + if_set = false; + if_not_set = false; + is_set = false; + item_size = 0; +} +void DataStruct::SetType(int8 new_type) { + type = new_type; + addType = type; +} +void DataStruct::SetMaxArraySize(int8 size) { + maxArraySize = size; +} +void DataStruct::SetOversized(int8 val) { + oversized = val; +} +void DataStruct::SetDefaultValue(int8 new_val) { + default_value = new_val; +} +void DataStruct::SetName(const char* new_name) { + name = string(new_name); +} +void DataStruct::SetLength(int32 new_length) { + length = new_length; +} +void DataStruct::SetOversizedByte(int8 val) { + oversized_byte = val; +} +void DataStruct::SetItemSize(int32 val) { + item_size = val; + if(item_size) + is_set = true; + else + is_set = false; +} +void DataStruct::SetIfEqualsVariable(const char* variable) { + if (variable) { + if_equals = true; + if_equals_variable = string(variable); + } + else + if_equals = false; +} +void DataStruct::SetIfNotEqualsVariable(const char* variable) { + if (variable) { + if_not_equals = true; + if_not_equals_variable = string(variable); + } + else + if_not_equals = false; +} +void DataStruct::SetIfFlagNotSetVariable(const char* variable) { + if (variable) { + if_flag_not_set = true; + if_flag_not_set_variable = string(variable); + } + else + if_flag_not_set = false; +} +void DataStruct::SetIfFlagSetVariable(const char* variable) { + if (variable) { + if_flag_set = true; + if_flag_set_variable = string(variable); + } + else + if_flag_set = false; +} +void DataStruct::SetIfSetVariable(const char* variable) { + if (variable) { + if_set = true; + if_set_variable = string(variable); + } + else + if_set = false; +} +void DataStruct::SetIfNotSetVariable(const char* variable) { + if (variable) { + if_not_set = true; + if_not_set_variable = string(variable); + } + else + if_not_set = false; +} +void DataStruct::SetIsSet(bool val) { + is_set = val; +} +bool DataStruct::IsSet() { + return is_set; +} +void DataStruct::SetIsOptional(bool val) { + optional = val; +} +bool DataStruct::IsOptional() { + return optional; +} +int32 DataStruct::GetItemSize() { + return item_size; +} +bool DataStruct::GetIfSet() { + return if_set; +} +const char* DataStruct::GetIfSetVariable() { + if (if_set_variable.length() > 0) + return if_set_variable.c_str(); + return 0; +} +bool DataStruct::GetIfNotSet() { + return if_not_set; +} +const char* DataStruct::GetIfNotSetVariable() { + if (if_not_set_variable.length() > 0) + return if_not_set_variable.c_str(); + return 0; +} +bool DataStruct::GetIfEquals() { + return if_equals; +} +const char* DataStruct::GetIfEqualsVariable() { + if (if_equals_variable.length() > 0) + return if_equals_variable.c_str(); + return 0; +} +bool DataStruct::GetIfNotEquals() { + return if_not_equals; +} +const char* DataStruct::GetIfNotEqualsVariable() { + if (if_not_equals_variable.length() > 0) + return if_not_equals_variable.c_str(); + return 0; +} +bool DataStruct::GetIfFlagSet() { + return if_flag_set; +} +const char* DataStruct::GetIfFlagSetVariable() { + if (if_flag_set_variable.length() > 0) + return if_flag_set_variable.c_str(); + return 0; +} +bool DataStruct::GetIfFlagNotSet() { + return if_flag_not_set; +} +const char* DataStruct::GetIfFlagNotSetVariable() { + if (if_flag_not_set_variable.length() > 0) + return if_flag_not_set_variable.c_str(); + return 0; +} +int8 DataStruct::GetDefaultValue() { + return default_value; +} +int8 DataStruct::GetType() { + return type; +} +int8 DataStruct::GetType2() { + return type2; +} +const char* DataStruct::GetName() { + return name.c_str(); +} +int8 DataStruct::GetOversized() { + return oversized; +} +int8 DataStruct::GetOversizedByte() { + return oversized_byte; +} +int8 DataStruct::GetMaxArraySize() { + return maxArraySize; +} +int32 DataStruct::GetLength() { + return length; +} +string DataStruct::GetStringName() { + return name; +} +bool DataStruct::AddToStruct() { + return add; +} +void DataStruct::SetAddToStruct(bool val) { + add = val; +} +int8 DataStruct::GetAddType() { + return addType; +} +void DataStruct::SetAddType(int8 new_type) { + addType = new_type; +} +string DataStruct::AppendVariable(string orig, const char* val) { + if (!val) + return orig; + if(orig.length() == 0) + return string(val); + if (orig.find(",") < 0xFFFFFFFF) { //has more than one already + string valstr = string(val); + vector* varnames = SplitString(orig, ','); + if (varnames) { + for (int32 i = 0; i < varnames->size(); i++) { + if (valstr.compare(varnames->at(i)) == 0) { + return orig; //already in the variable, no need to append + } + } + safe_delete(varnames); + } + } + return orig.append(",").append(val); +} +int32 DataStruct::GetDataSizeInBytes() { + int32 ret = 0; + switch (type) { + case DATA_STRUCT_INT8: + ret = length * sizeof(int8); + break; + case DATA_STRUCT_INT16: + ret = length * sizeof(int16); + break; + case DATA_STRUCT_INT32: + ret = length * sizeof(int32); + break; + case DATA_STRUCT_INT64: + ret = length * sizeof(int64); + break; + case DATA_STRUCT_SINT8: + ret = length * sizeof(sint8); + break; + case DATA_STRUCT_SINT16: + ret = length * sizeof(sint16); + break; + case DATA_STRUCT_SINT32: + ret = length * sizeof(sint32); + break; + case DATA_STRUCT_SINT64: + ret = length * sizeof(sint64); + break; + case DATA_STRUCT_FLOAT: + ret = length * sizeof(float); + break; + case DATA_STRUCT_DOUBLE: + ret = length * sizeof(double); + break; + case DATA_STRUCT_ARRAY: + // Array elements won't have a size so get out now to avoid the warning. + break; + default: + LogWrite(PACKET__WARNING, 0, "DataStruct", "Tried retrieving a data size from an unsupported data struct type in GetDataSizeInBytes()"); + break; + } + return ret; +} + +PacketStruct::PacketStruct(PacketStruct* packet, int16 in_client_version) { + parent = packet->parent; + client_version = in_client_version; + vector::iterator itr2; + name = packet->name; + + for (itr2 = packet->structs.begin(); itr2 != packet->structs.end(); itr2++) { + add(new DataStruct(*itr2)); + } + vector::iterator itr; + for (itr = packet->flags.begin(); itr != packet->flags.end(); itr++) { + AddFlag((*itr).c_str()); + } + sub_packet = false; + opcode = packet->opcode; + version = packet->version; + opcode_type = packet->opcode_type; + sub_packet_size = 1; + + + addPacketArrays(packet); +} + +PacketStruct::PacketStruct() { + parent = 0; + opcode = OP_Unknown; + opcode_type = string(""); +} + +PacketStruct::PacketStruct(PacketStruct* packet, bool sub) { + vector::iterator itr2; + + for (itr2 = packet->structs.begin(); itr2 != packet->structs.end(); itr2++) { + add(new DataStruct(*itr2)); + } + vector::iterator itr; + for (itr = packet->flags.begin(); itr != packet->flags.end(); itr++) { + AddFlag((*itr).c_str()); + } + sub_packet = sub; + opcode = packet->opcode; + version = packet->version; + opcode_type = packet->opcode_type; + name = packet->name; + sub_packet_size = 0; + parent = 0; +} +PacketStruct::~PacketStruct() { + deleteDataStructs(&structs); + deleteDataStructs(&orig_structs); + deletePacketArrays(this); + struct_map.clear(); + struct_data.clear(); + flags.clear(); +} + +void PacketStruct::deleteDataStructs(vector* data_structs) { + if ( !data_structs || data_structs->size() == 0 ) + return; + + DataStruct* data = 0; + vector::iterator itr; + for (itr = data_structs->begin(); itr != data_structs->end(); itr++) { + data = *itr; + void* ptr = GetStructPointer(data); + + // stop the struct_data from growing with old data/ptr info, memory leaking and eventual buffer overflow (crash) + map::iterator datastr = struct_data.find(data); + if (datastr != struct_data.end()) + struct_data.erase(datastr); + + switch (data->GetType()) { + case DATA_STRUCT_EQ2_8BIT_STRING: { + EQ2_8BitString* real_ptr = (EQ2_8BitString*)ptr; + safe_delete(real_ptr); + break; + } + case DATA_STRUCT_EQ2_16BIT_STRING: { + EQ2_16BitString* real_ptr = (EQ2_16BitString*)ptr; + safe_delete(real_ptr); + break; + } + case DATA_STRUCT_EQ2_32BIT_STRING: { + EQ2_32BitString* real_ptr = (EQ2_32BitString*)ptr; + safe_delete(real_ptr); + break; + } + case DATA_STRUCT_EQUIPMENT: { + EQ2_EquipmentItem* real_ptr = (EQ2_EquipmentItem*)ptr; + safe_delete_array(real_ptr); + break; + } + case DATA_STRUCT_DOUBLE: { + double* real_ptr = (double*)ptr; + safe_delete_array(real_ptr); + break; + } + case DATA_STRUCT_FLOAT: { + float* real_ptr = (float*)ptr; + safe_delete_array(real_ptr); + break; + } + case DATA_STRUCT_INT8: { + int8* real_ptr = (int8*)ptr; + safe_delete_array(real_ptr); + break; + } + case DATA_STRUCT_INT16: { + int16* real_ptr = (int16*)ptr; + safe_delete_array(real_ptr); + break; + } + case DATA_STRUCT_INT32: { + int32* real_ptr = (int32*)ptr; + safe_delete_array(real_ptr); + break; + } + case DATA_STRUCT_INT64: { + int64* real_ptr = (int64*)ptr; + safe_delete_array(real_ptr); + break; + } + case DATA_STRUCT_SINT8: { + sint8* real_ptr = (sint8*)ptr; + safe_delete_array(real_ptr); + break; + } + case DATA_STRUCT_SINT16: { + sint16* real_ptr = (sint16*)ptr; + safe_delete_array(real_ptr); + break; + } + case DATA_STRUCT_SINT32: { + sint32* real_ptr = (sint32*)ptr; + safe_delete_array(real_ptr); + break; + } + case DATA_STRUCT_SINT64: { + sint64* real_ptr = (sint64*)ptr; + safe_delete_array(real_ptr); + break; + } + case DATA_STRUCT_ITEM: { + uchar* real_ptr = (uchar*)ptr; + safe_delete_array(real_ptr); + break; + } + case DATA_STRUCT_CHAR: { + char* real_ptr = (char*)ptr; + safe_delete_array(real_ptr); + break; + } + case DATA_STRUCT_COLOR: { + EQ2_Color* real_ptr = (EQ2_Color*)ptr; + safe_delete_array(real_ptr); + break; + } + } + ptr = 0; + safe_delete(data); + } +} +void PacketStruct::deletePacketArrays(PacketStruct* packet) { + if (!packet) + return; + vector::iterator itr; + + for (itr = packet->arrays.begin(); itr != packet->arrays.end(); itr++) + safe_delete(*itr); + packet->arrays.clear(); + + for (itr = packet->orig_packets.begin(); itr != packet->orig_packets.end(); itr++) + safe_delete(*itr); + packet->orig_packets.clear(); +} + +void PacketStruct::renameSubstructArray(const char* substruct, int32 index) { + vector::iterator itr; + char tmp[10] = { 0 }; + sprintf(tmp, "%i", index); + for (itr = arrays.begin(); itr != arrays.end(); itr++) { + (*itr)->SetName(string(substruct).append("_").append((*itr)->GetName()).append("_").append(tmp).c_str()); + } +} + +void PacketStruct::addPacketArrays(PacketStruct* packet) { + if (!packet) + return; + vector::iterator itr; + + for (itr = packet->arrays.begin(); itr != packet->arrays.end(); itr++) { + PacketStruct* tmp = new PacketStruct(*itr, true); + tmp->addPacketArrays(*itr); + add(tmp); + } +} + +bool PacketStruct::IsStringValueType(string in_name, int32 index) { + DataStruct* data = findStruct(in_name.c_str(), index); + switch (data->GetType()) { + case DATA_STRUCT_CHAR: + case DATA_STRUCT_EQ2_8BIT_STRING: + case DATA_STRUCT_EQ2_16BIT_STRING: + case DATA_STRUCT_EQ2_32BIT_STRING: + return true; + } + return false; +} + +bool PacketStruct::IsColorValueType(string in_name, int32 index) { + DataStruct* data = findStruct(in_name.c_str(), index); + if (data->GetType() == DATA_STRUCT_COLOR) + return true; + else + return false; +} +void PacketStruct::setColor(DataStruct* data, int8 red, int8 green, int8 blue, int32 index = 0) { + if (data && data->GetType() == DATA_STRUCT_COLOR) { + EQ2_Color* color = (EQ2_Color*)GetStructPointer(data); + color[index].blue = blue; + color[index].red = red; + color[index].green = green; + if (blue > 0 || green > 0 || red > 0) + data->SetIsSet(true); + } +} +void PacketStruct::setEquipment(DataStruct* data, int16 type, int8 c_red, int8 c_blue, int8 c_green, int8 h_red, int8 h_blue, int8 h_green, int32 index) { + if (data) { + EQ2_EquipmentItem* equipment = (EQ2_EquipmentItem*)GetStructPointer(data); + EQ2_Color* color = (EQ2_Color*)&equipment[index].color; + EQ2_Color* highlight = (EQ2_Color*)&equipment[index].highlight; + equipment[index].type = type; + color->blue = c_blue; + color->red = c_red; + color->green = c_green; + highlight->blue = h_blue; + highlight->red = h_red; + highlight->green = h_green; + if (c_red > 0 || c_blue > 0 || c_green > 0 || h_red > 0 || h_blue > 0 || h_green > 0) + data->SetIsSet(true); + } +} +void PacketStruct::add(PacketStruct* packet_struct) { + packet_struct->parent = this; + arrays.push_back(packet_struct); +} +const char* PacketStruct::GetOpcodeType() { + return opcode_type.c_str(); +} + +void PacketStruct::SetOpcodeType(const char* in_type) { + if (in_type) + opcode_type = string(in_type); + else + opcode_type = string(""); +} + +void PacketStruct::setDataType(DataStruct* data_struct, sint8 data, int32 index) { + if (data_struct) { + sint8* ptr = (sint8*)GetStructPointer(data_struct); + ptr[index] = data; + if (data != 0 && data != -1) + data_struct->SetIsSet(true); + } +} +void PacketStruct::setDataType(DataStruct* data_struct, sint16 data, int32 index) { + if (data_struct) { + sint16* ptr = (sint16*)GetStructPointer(data_struct); + ptr[index] = data; + if (data != 0 && data != -1) + data_struct->SetIsSet(true); + } +} +void PacketStruct::setDataType(DataStruct* data_struct, sint32 data, int32 index) { + if (data_struct) { + sint32* ptr = (sint32*)GetStructPointer(data_struct); + ptr[index] = data; + if (data != 0 && data != -1) + data_struct->SetIsSet(true); + } +} +void PacketStruct::setDataType(DataStruct* data_struct, sint64 data, int32 index) { + if (data_struct) { + sint64* ptr = (sint64*)GetStructPointer(data_struct); + ptr[index] = data; + if (data > 0) + data_struct->SetIsSet(true); + } +} +void PacketStruct::setDataType(DataStruct* data_struct, char data, int32 index) { + if (data_struct) { + char* ptr = (char*)GetStructPointer(data_struct); + ptr[index] = data; + if (data > 0) + data_struct->SetIsSet(true); + } +} +void PacketStruct::setDataType(DataStruct* data_struct, int8 data, int32 index) { + if (data_struct) { + int8* ptr = (int8*)GetStructPointer(data_struct); + ptr[index] = data; + if (data > 0) + data_struct->SetIsSet(true); + } +} +void PacketStruct::setDataType(DataStruct* data_struct, int16 data, int32 index) { + if (data_struct) { + int16* ptr = (int16*)GetStructPointer(data_struct); + ptr[index] = data; + if (data > 0) + data_struct->SetIsSet(true); + } +} +void PacketStruct::setDataType(DataStruct* data_struct, int32 data, int32 index) { + if (data_struct) { + int32* ptr = (int32*)GetStructPointer(data_struct); + ptr[index] = data; + if (data > 0) + data_struct->SetIsSet(true); + } +} +void PacketStruct::setDataType(DataStruct* data_struct, int64 data, int32 index) { + if (data_struct) { + int64* ptr = (int64*)GetStructPointer(data_struct); + ptr[index] = data; + if (data > 0) + data_struct->SetIsSet(true); + } +} +void PacketStruct::setDataType(DataStruct* data_struct, float data, int32 index) { + if (data_struct) { + float* ptr = (float*)GetStructPointer(data_struct); + ptr[index] = data; + if (data > 0) + data_struct->SetIsSet(true); + } +} +void PacketStruct::setDataType(DataStruct* data_struct, double data, int32 index) { + if (data_struct) { + double* ptr = (double*)GetStructPointer(data_struct); + ptr[index] = data; + if (data > 0) + data_struct->SetIsSet(true); + } +} +void PacketStruct::setData(DataStruct* data_struct, EQ2_8BitString* input_string, int32 index, bool use_second_type) { + if (data_struct) { + EQ2_8BitString* tmp = (EQ2_8BitString*)GetStructPointer(data_struct); + tmp->data = input_string->data; + tmp->size = input_string->data.length(); + if (input_string->data.length() > 0) + data_struct->SetIsSet(true); + } +} +void PacketStruct::setData(DataStruct* data_struct, EQ2_16BitString* input_string, int32 index, bool use_second_type) { + if (data_struct) { + EQ2_16BitString* tmp = (EQ2_16BitString*)GetStructPointer(data_struct); + tmp->data = input_string->data; + tmp->size = input_string->data.length(); + if (input_string->data.length() > 0) + data_struct->SetIsSet(true); + } +} +void PacketStruct::setData(DataStruct* data_struct, EQ2_32BitString* input_string, int32 index, bool use_second_type) { + if (data_struct) { + EQ2_32BitString* tmp = (EQ2_32BitString*)GetStructPointer(data_struct); + tmp->data = input_string->data; + tmp->size = input_string->data.length(); + if (input_string->data.length() > 0) + data_struct->SetIsSet(true); + } +} +void PacketStruct::add(DataStruct* data) { + structs.push_back(data); + struct_map[data->GetStringName()] = data; + switch (data->GetType()) { + case DATA_STRUCT_INT8: { + struct_data[data] = new int8[data->GetLength()]; + int8* ptr = (int8*)GetStructPointer(data); + if (data->GetLength() > 1) { + int8 default_val = data->GetDefaultValue(); + memset(ptr, default_val, data->GetLength() * sizeof(int8)); + } + else + ptr[0] = 0; + break; + } + case DATA_STRUCT_INT16: { + struct_data[data] = new int16[data->GetLength()]; + int16* ptr = (int16*)GetStructPointer(data); + if (data->GetLength() > 1) { + int8 default_val = data->GetDefaultValue(); + memset(ptr, default_val, data->GetLength() * sizeof(int16)); + } + else + ptr[0] = 0; + break; + } + case DATA_STRUCT_INT32: { + struct_data[data] = new int32[data->GetLength()]; + int32* ptr = (int32*)GetStructPointer(data); + if (data->GetLength() > 1) { + int8 default_val = data->GetDefaultValue(); + memset(ptr, default_val, data->GetLength() * sizeof(int32)); + } + else + ptr[0] = 0; + break; + } + case DATA_STRUCT_INT64: { + struct_data[data] = new int64[data->GetLength()]; + int64* ptr = (int64*)GetStructPointer(data); + if (data->GetLength() > 1) { + int8 default_val = data->GetDefaultValue(); + memset(ptr, default_val, data->GetLength() * sizeof(int64)); + } + else + ptr[0] = 0; + break; + } + case DATA_STRUCT_SINT8: { + struct_data[data] = new sint8[data->GetLength()]; + sint8* ptr = (sint8*)GetStructPointer(data); + if (data->GetLength() > 1) + memset(ptr, 0, data->GetLength() * sizeof(sint8)); + else + ptr[0] = 0; + break; + } + case DATA_STRUCT_SINT16: { + struct_data[data] = new sint16[data->GetLength()]; + sint16* ptr = (sint16*)GetStructPointer(data); + if (data->GetLength() > 1) + memset(ptr, 0, data->GetLength() * sizeof(sint16)); + else + ptr[0] = 0; + break; + } + case DATA_STRUCT_SINT32: { + struct_data[data] = new sint32[data->GetLength()]; + sint32* ptr = (sint32*)GetStructPointer(data); + if (data->GetLength() > 1) + memset(ptr, 0, data->GetLength() * sizeof(sint32)); + else + ptr[0] = 0; + break; + } + case DATA_STRUCT_SINT64: { + struct_data[data] = new sint64[data->GetLength()]; + sint64* ptr = (sint64*)GetStructPointer(data); + if (data->GetLength() > 1) + memset(ptr, 0, data->GetLength() * sizeof(sint64)); + else + ptr[0] = 0; + break; + } + case DATA_STRUCT_CHAR: { + struct_data[data] = new char[data->GetLength()]; + char* ptr = (char*)GetStructPointer(data); + if (data->GetLength() > 1) + memset(ptr, 0, data->GetLength() * sizeof(char)); + else + ptr[0] = 0; + break; + } + case DATA_STRUCT_FLOAT: { + struct_data[data] = new float[data->GetLength()]; + float* ptr = (float*)GetStructPointer(data); + if (data->GetLength() > 1) + memset(ptr, 0, data->GetLength() * sizeof(float)); + else + ptr[0] = 0; + break; + } + case DATA_STRUCT_DOUBLE: { + struct_data[data] = new double[data->GetLength()]; + double* ptr = (double*)GetStructPointer(data); + if (data->GetLength() > 1) + memset(ptr, 0, data->GetLength() * sizeof(double)); + else + ptr[0] = 0; + break; + } + case DATA_STRUCT_ARRAY: { + data->SetLength(0); + break; + } + case DATA_STRUCT_EQ2_8BIT_STRING: { + string name2 = data->GetStringName(); + for (int32 i = 1; i < data->GetLength(); i++) { + DataStruct* new_data = new DataStruct(data); + char blah[10] = { 0 }; + sprintf(blah, "%i", i); + name2.append("_").append(blah); + new_data->SetName(name2.c_str()); + new_data->SetLength(1); + EQ2_8BitString* tmp = new EQ2_8BitString; + tmp->size = 0; + struct_data[new_data] = tmp; + structs.push_back(new_data); + } + data->SetLength(1); + EQ2_8BitString* tmp = new EQ2_8BitString; + tmp->size = 0; + struct_data[data] = tmp; + break; + } + case DATA_STRUCT_EQ2_16BIT_STRING: { + string name2 = data->GetStringName(); + for (int32 i = 1; i < data->GetLength(); i++) { + DataStruct* new_data = new DataStruct(data); + char blah[10] = { 0 }; + sprintf(blah, "%i", i); + name2.append("_").append(blah); + new_data->SetName(name2.c_str()); + new_data->SetLength(1); + EQ2_16BitString* tmp = new EQ2_16BitString; + tmp->size = 0; + struct_data[new_data] = tmp; + structs.push_back(new_data); + } + data->SetLength(1); + EQ2_16BitString* tmp = new EQ2_16BitString; + tmp->size = 0; + struct_data[data] = tmp; + break; + } + case DATA_STRUCT_EQ2_32BIT_STRING: { + string name2 = data->GetStringName(); + for (int32 i = 1; i < data->GetLength(); i++) { + DataStruct* new_data = new DataStruct(data); + char blah[10] = { 0 }; + sprintf(blah, "%i", i); + name2.append("_").append(blah); + new_data->SetName(name2.c_str()); + new_data->SetLength(1); + EQ2_32BitString* tmp = new EQ2_32BitString; + tmp->size = 0; + struct_data[new_data] = tmp; + structs.push_back(new_data); + } + data->SetLength(1); + EQ2_32BitString* tmp = new EQ2_32BitString; + tmp->size = 0; + struct_data[data] = tmp; + break; + } + case DATA_STRUCT_COLOR: { + struct_data[data] = new EQ2_Color[data->GetLength()]; + EQ2_Color* ptr = (EQ2_Color*)GetStructPointer(data); + for (int16 i = 0; i < data->GetLength(); i++) { + ptr[i].red = 0; + ptr[i].blue = 0; + ptr[i].green = 0; + } + break; + } + case DATA_STRUCT_EQUIPMENT: { + struct_data[data] = new EQ2_EquipmentItem[data->GetLength()]; + EQ2_EquipmentItem* ptr = (EQ2_EquipmentItem*)GetStructPointer(data); + for (int16 i = 0; i < data->GetLength(); i++) { + memset(&ptr[i].color, 0, sizeof(ptr[i].color)); + memset(&ptr[i].highlight, 0, sizeof(ptr[i].highlight)); + ptr[i].type = 0; + } + break; + } + case DATA_STRUCT_ITEM: { + struct_data[data] = new uchar[10000]; + char* ptr = (char*)GetStructPointer(data); + memset(ptr, 0, 10000); + break; + } + } +} +void PacketStruct::remove(DataStruct* data) { + vector::iterator itr; + for (itr = structs.begin(); itr != structs.end(); itr++) { + if (data == (*itr)) { + structs.erase(itr); + safe_delete(data); + return; + } + } +} +DataStruct* PacketStruct::findStruct(const char* name, int32 index) { + return findStruct(name, index, index); +} + +DataStruct* PacketStruct::findStruct(const char* name, int32 index1, int32 index2) { + DataStruct* data = 0; + + if (struct_map.count(string(name)) > 0) { + data = struct_map[string(name)]; + if (data && index2 < data->GetLength()) + return data; + } + vector::iterator itr; + + PacketStruct* packet = 0; + vector::iterator itr2; + string name2 = string(name); + if (index1 < 0xFFFF) { + char blah[10] = { 0 }; + sprintf(blah, "_%i", index1); + name2.append(blah); + } + if (struct_map.count(name2) > 0) { + data = struct_map[name2]; + if (data && index2 < data->GetLength()) + return data; + } + for (itr2 = arrays.begin(); itr2 != arrays.end(); itr2++) { + packet = *itr2; + data = packet->findStruct(name, index1, index2); + if (data != 0) + return data; + } + return 0; +} +void PacketStruct::remove(const char* name) { + DataStruct* data = 0; + vector::iterator itr; + for (itr = structs.begin(); itr != structs.end(); itr++) { + data = *itr; + if (strcmp(name, data->GetName()) == 0) { + structs.erase(itr); + safe_delete(data); + return; + } + } +} +string* PacketStruct::serializeString() { + serializePacket(); + return getDataString(); +} +void PacketStruct::setSmallString(DataStruct* data_struct, const char* text, int32 index) { + EQ2_8BitString* string_data = new EQ2_8BitString; + string_data->data = string(text); + string_data->size = string_data->data.length(); + setData(data_struct, string_data, index); + safe_delete(string_data); +} +void PacketStruct::setMediumString(DataStruct* data_struct, const char* text, int32 index) { + EQ2_16BitString* string_data = new EQ2_16BitString; + string_data->data = string(text); + string_data->size = string_data->data.length(); + setData(data_struct, string_data, index); + safe_delete(string_data); +} +void PacketStruct::setLargeString(DataStruct* data_struct, const char* text, int32 index) { + EQ2_32BitString* string_data = new EQ2_32BitString; + string_data->data = string(text); + string_data->size = string_data->data.length(); + setData(data_struct, string_data, index); + safe_delete(string_data); +} +void PacketStruct::setSmallStringByName(const char* name, const char* text, int32 index) { + setSmallString(findStruct(name, index), text, index); +} +void PacketStruct::setMediumStringByName(const char* name, const char* text, int32 index) { + setMediumString(findStruct(name, index), text, index); +} +void PacketStruct::setLargeStringByName(const char* name, const char* text, int32 index) { + setLargeString(findStruct(name, index), text, index); +} + +bool PacketStruct::GetVariableIsSet(const char* name) { + DataStruct* ds2 = findStruct(name, 0); + if (!ds2 || !ds2->IsSet()) + return false; + return true; +} + +bool PacketStruct::GetVariableIsNotSet(const char* name) { + DataStruct* ds2 = findStruct(name, 0); + if (ds2 && ds2->IsSet()) + return false; + return true; +} + +bool PacketStruct::CheckFlagExists(const char* name) { + vector::iterator itr; + for (itr = flags.begin(); itr != flags.end(); itr++) { + if (*itr == string(name)) + return true; + } + return false; +} + +void PacketStruct::AddFlag(const char* name) { + if (flags.size() > 0) { + vector::iterator itr; + for (itr = flags.begin(); itr != flags.end(); itr++) { + if (*itr == string(name)) + return; + } + } + flags.push_back(string(name)); +} + +bool PacketStruct::LoadPacketData(uchar* data, int32 data_len, bool create_color) { + loadedSuccessfully = true; + DataStruct* data_struct = 0; + try { + InitializeLoadData(data, data_len); + vector::iterator itr; + + for (itr = structs.begin(); itr != structs.end(); itr++) { + data_struct = *itr; + if (!data_struct->AddToStruct()) + continue; + + if (data_struct->GetIfSet() && data_struct->GetIfSetVariable()) { + string varname = string(data_struct->GetIfSetVariable()); + if (varname.find(",") < 0xFFFFFFFF) { + vector* varnames = SplitString(varname, ','); + if (varnames) { + bool should_continue = true; + for (int32 i = 0; i < varnames->size(); i++) { + if (GetVariableIsSet(varnames->at(i).c_str())) { + should_continue = false; + break; + } + } + safe_delete(varnames); + if (should_continue) + continue; + } + } + else { + // Check to see if the variable contains %i, if it does assume we are in an array + // and get the current index from the end of the data struct's name + char name[250] = { 0 }; + if (varname.find("%i") < 0xFFFFFFFF) { + vector* varnames = SplitString(data_struct->GetName(), '_'); + int index = atoi(varnames->at(varnames->size() - 1).c_str()); + sprintf(name, varname.c_str(), index); + } + else + strcpy(name, varname.c_str()); + + if (!GetVariableIsSet(name)) + continue; + } + } + if (data_struct->GetIfNotSet() && data_struct->GetIfNotSetVariable()) { + string varname = string(data_struct->GetIfNotSetVariable()); + if (varname.find(",") < 0xFFFFFFFF) { + vector* varnames = SplitString(varname, ','); + if (varnames) { + bool should_continue = false; + for (int32 i = 0; i < varnames->size(); i++) { + if (!GetVariableIsNotSet(varnames->at(i).c_str())) { + should_continue = true; + break; + } + } + safe_delete(varnames); + if (should_continue) + continue; + } + } + else { + // Check to see if the variable contains %i, if it does assume we are in an array + // and get the current index from the end of the data struct's name + char name[250] = { 0 }; + if (varname.find("%i") < 0xFFFFFFFF) { + vector* varnames = SplitString(data_struct->GetName(), '_'); + int index = atoi(varnames->at(varnames->size() - 1).c_str()); + sprintf(name, varname.c_str(), index); + } + else + strcpy(name, varname.c_str()); + + if (!GetVariableIsNotSet(name)) + continue; + } + } + // Quick implementaion of IfVariableNotEquals + // probably not what it was intended for as it currently just checks to see if the given variable equals 1 + // should probably change it so you can define what the variable should or shouldn't equal + // + // ie: IfVariableNotEquals="stat_type_%i=1" + // would be a check to make sure that stat_type_%i does not equal 1 and if it does exclude this element + if (data_struct->GetIfNotEquals() && data_struct->GetIfNotEqualsVariable()) { + // Get the variable name + string varname = string(data_struct->GetIfNotEqualsVariable()); + char name[250] = { 0 }; + // Check to see if the variable has %i in the name, if it does assume we are in an array and get the current + // index and replace it + if (varname.find("%i") < 0xFFFFFFFF) { + // Get the current index by getting the number at the end of the name + vector* varnames = SplitString(data_struct->GetName(), '_'); + int index = atoi(varnames->at(varnames->size() - 1).c_str()); + + string substr = "stat_type"; + if (strncmp(varname.c_str(), substr.c_str(), strlen(substr.c_str())) == 0) { + // adorn_stat_subtype 18 chars + string temp = varname.substr(12); + char temp2[20] = { 0 }; + int index2 = atoi(temp.c_str()); + itoa(index2, temp2, 10); + varname = varname.substr(0, 12).append(temp2).append("_%i"); + } + sprintf(name, varname.c_str(), index); + safe_delete(varnames); + } + else + strcpy(name, varname.c_str()); + + // Get the data for the variable + int16 value = 0; + DataStruct* data_struct2 = findStruct(name, 0); + value = getType_int16(data_struct2); + // Hack for items as it is the only struct that currently uses IfVariableNotEquals + if (value == 1) + continue; + } + // copy and paste of the code above for IfEquals + if (data_struct->GetIfEquals() && data_struct->GetIfEqualsVariable()) { + // Get the variable name + string varname = string(data_struct->GetIfEqualsVariable()); + char name[250] = { 0 }; + // Check to see if the variable has %i in the name, if it does assume we are in an array and get the current + // index and replace it + if (varname.find("%i") < 0xFFFFFFFF) { + // Get the current index by getting the number at the end of the name + vector* varnames = SplitString(data_struct->GetName(), '_'); + int index = atoi(varnames->at(varnames->size() - 1).c_str()); + + string substr = "stat_type"; + if (strncmp(varname.c_str(), substr.c_str(), strlen(substr.c_str())) == 0) { + // adorn_stat_subtype 18 chars + string temp = varname.substr(12); + char temp2[20] = { 0 }; + int index2 = atoi(temp.c_str()); + itoa(index2, temp2, 10); + varname = varname.substr(0, 12).append(temp2).append("_%i"); + } + sprintf(name, varname.c_str(), index); + safe_delete(varnames); + } + else + strcpy(name, varname.c_str()); + + // Get the data for the variable + int16 value = 0; + DataStruct* data_struct2 = findStruct(name, 0); + value = getType_int16(data_struct2); + // Hack for items as it is the only struct that currently uses IfVariableNotEquals + if (value != 1) + continue; + } + + // The following is tailored to items as they are the only structs that use type2 + // if more type2's are needed outside of the item stats array we need to think up an element + // to determine when to use type2 over type. + // Added checks for set stats and adorn stats - Zcoretri + bool useType2 = false; + if (data_struct->GetType2() > 0) { + int16 type = 0; + char name[250] = { 0 }; + vector* varnames = SplitString(data_struct->GetName(), '_'); + string struct_name = data_struct->GetName(); + if (struct_name.find("set") < 0xFFFFFFFF) { + string tmp = "set_stat_type"; + struct_name.replace(0, 9, tmp); + sprintf(name, "%s", struct_name.c_str()); + } + else if (struct_name.find("adorn") < 0xFFFFFFFF) { + string tmp = "adorn_stat_type"; + struct_name.replace(0, 9, tmp); + sprintf(name, "%s", struct_name.c_str()); + } + else { + // set name to stat_type_# (where # is the current index of the array we are in) + sprintf(name, "%s_%s", "stat_type", varnames->at(varnames->size() - 1).c_str()); + } + // Look up the value for stat_type + DataStruct* data_struct2 = findStruct(name, 0); + type = getType_int16(data_struct2); + // If stat_type == 6 we use a float, else we use sint16 + if (type != 6 && type != 7) + useType2 = true; + safe_delete(varnames); + } + if (!StructLoadData(data_struct, GetStructPointer(data_struct), data_struct->GetLength(), useType2, create_color)) + { + loadedSuccessfully = false; + break; + } + } + } + catch (...) { + loadedSuccessfully = false; + } + return loadedSuccessfully; +} +bool PacketStruct::StructLoadData(DataStruct* data_struct, void* data, int32 len, bool useType2, bool create_color) { + int8 type = 0; + if (useType2) { + type = data_struct->GetType2(); + // Need to change the data the struct expects to type2 + data_struct->SetType(type); + LogWrite(PACKET__DEBUG, 7, "Items", "Using type2 = %i", type); + } + else + type = data_struct->GetType(); + + switch (type) { + case DATA_STRUCT_INT8: + LoadData((int8*)data, len); + data_struct->SetIsSet(*((int8*)data) > 0); + break; + case DATA_STRUCT_INT16: + if (data_struct->GetOversized() > 0) { + LoadData((int8*)data, len); + if (getType_int16(data_struct) == data_struct->GetOversizedByte()) { + LoadData((int16*)data, len); + } + } + else { + LoadData((int16*)data, len); + } + data_struct->SetIsSet(*((int16*)data) > 0); + break; + case DATA_STRUCT_INT32: + if (data_struct->GetOversized() > 0) { + LoadData((int8*)data, len); + if (getType_int32(data_struct) == data_struct->GetOversizedByte()) { + LoadData((int32*)data, len); + } + else + LoadData((int8*)data, len); + } + else { + LoadData((int32*)data, len); + } + data_struct->SetIsSet(*((int32*)data) > 0); + break; + case DATA_STRUCT_INT64: + LoadData((int64*)data, len); + data_struct->SetIsSet(*((int64*)data) > 0); + break; + case DATA_STRUCT_SINT8: + LoadData((sint8*)data, len); + data_struct->SetIsSet(*((sint8*)data) > 0); + break; + case DATA_STRUCT_SINT16: + if (data_struct->GetOversized() > 0) { + LoadData((sint8*)data, len); + sint8 val = (sint8)getType_sint16(data_struct); + if (val < 0) //necessary because when using memcpy from a smaller data type to a larger one, the sign is lost + setData(data_struct, val, 0); + if (getType_sint16(data_struct) == data_struct->GetOversizedByte()) + LoadData((sint16*)data, len); + } + else + LoadData((sint16*)data, len); + data_struct->SetIsSet(*((sint16*)data) > 0); + break; + case DATA_STRUCT_SINT32: + LoadData((sint32*)data, len); + data_struct->SetIsSet(*((sint32*)data) > 0); + break; + case DATA_STRUCT_SINT64: + LoadData((sint64*)data, len); + data_struct->SetIsSet(*((sint64*)data) > 0); + break; + case DATA_STRUCT_CHAR: + LoadData((char*)data, len); + data_struct->SetIsSet(true); + break; + case DATA_STRUCT_FLOAT: + LoadData((float*)data, len); + data_struct->SetIsSet(*((float*)data) > 0); + break; + case DATA_STRUCT_DOUBLE: + LoadData((double*)data, len); + data_struct->SetIsSet(*((double*)data) > 0); + break; + case DATA_STRUCT_EQ2_8BIT_STRING: { + LoadDataString((EQ2_8BitString*)data); + data_struct->SetIsSet(((EQ2_8BitString*)data)->data.length() > 0); + break; + } + case DATA_STRUCT_EQ2_16BIT_STRING: { + LoadDataString((EQ2_16BitString*)data); + data_struct->SetIsSet(((EQ2_16BitString*)data)->data.length() > 0); + break; + } + case DATA_STRUCT_EQ2_32BIT_STRING: { + LoadDataString((EQ2_32BitString*)data); + data_struct->SetIsSet(((EQ2_32BitString*)data)->data.length() > 0); + break; + } + case DATA_STRUCT_COLOR: { + // lets not do this again, DoF behaves differently than AoM, DoF is not compatible with CreateEQ2Color + //if (strcmp(GetName(), "CreateCharacter") == 0 || strcmp(GetName(), "WS_SubmitCharCust") == 0) + if(create_color) + CreateEQ2Color((EQ2_Color*)data); + else + LoadData((EQ2_Color*)data, len); + break; + } + case DATA_STRUCT_EQUIPMENT: { + LoadData((EQ2_EquipmentItem*)data); + break; + } + case DATA_STRUCT_ARRAY: { + int32 size = GetArraySize(data_struct, 0); + if (size > 0xFFFF || size > GetLoadLen()-GetLoadPos()) { + LogWrite(PACKET__WARNING, 1, "Packet", "Possible corrupt packet while loading struct array, orig array size: %u in struct name %s, data name %s, load_len %u, load_pos %u", size, GetName(), (data_struct && data_struct->GetName()) ? data_struct->GetName() : "??", GetLoadLen(), GetLoadPos()); + return false; + } + PacketStruct* ps = GetPacketStructByName(data_struct->GetName()); + if (ps && ps->GetSubPacketSize() != size) { + if (data_struct->GetMaxArraySize() > 0 && size > data_struct->GetMaxArraySize()) + size = data_struct->GetMaxArraySize(); + ps->reAddAll(size); + } + if (ps && size > 0) { + //for(int i=0;i 0;i++){ + if(ps->LoadPacketData(GetLoadBuffer() + GetLoadPos(), GetLoadLen() - GetLoadPos(), create_color)) { + SetLoadPos(GetLoadPos() + ps->GetLoadPos()); + } + //} + } + break; + } + default: { + data_struct->SetIsSet(false); + } + } + + return true; +} +PacketStruct* PacketStruct::GetPacketStructByName(const char* name) { + PacketStruct* ps = 0; + vector::iterator itr; + for (itr = arrays.begin(); itr != arrays.end(); itr++) { + ps = *itr; + if (strcmp(ps->GetName(), name) == 0) + return ps; + ps = ps->GetPacketStructByName(name); + if (ps) + return ps; + } + return 0; +} + +void PacketStruct::reAddAll(int32 length) { + vector::iterator itr; + DataStruct* ds = 0; + PacketStruct* ps = 0; + vector::iterator packet_itr; + if (orig_structs.size() == 0) + orig_structs = structs; + else + deleteDataStructs(&structs); + structs.clear(); + + if (orig_packets.size() == 0 && arrays.size() > 0) + orig_packets = arrays; + else { + for (packet_itr = arrays.begin(); packet_itr != arrays.end(); packet_itr++) { + ps = *packet_itr; + safe_delete(ps); + } + } + arrays.clear(); + + for (int16 i = 0; i < length; i++) { + for (packet_itr = orig_packets.begin(); packet_itr != orig_packets.end(); packet_itr++) { + ps = *packet_itr; + PacketStruct* new_packet = new PacketStruct(ps, true); + char tmp[20] = { 0 }; + sprintf(tmp, "_%i", i); + string name = string(new_packet->GetName()); + name.append(tmp); + new_packet->SetName(name.c_str()); + add(new_packet); + } + for (itr = orig_structs.begin(); itr != orig_structs.end(); itr++) { + ds = *itr; + DataStruct* new_data = new DataStruct(ds); + char tmp[20] = { 0 }; + sprintf(tmp, "_%i", i); + string name = new_data->GetStringName(); + if (IsSubPacket() && parent->IsSubPacket()) { + string parent_name = string(GetName()); + try { + if (parent_name.rfind("_") < 0xFFFFFFFF) + sprintf(tmp, "%i_%i", atoi(parent_name.substr(parent_name.rfind("_") + 1).c_str()), i); + } + catch (...) { + sprintf(tmp, "_%i", i); + } + } + name.append(tmp); + new_data->SetName(name.c_str()); + if (new_data->GetType() == DATA_STRUCT_ARRAY) { + string old_size_arr = string(new_data->GetArraySizeVariable()); + new_data->SetArraySizeVariable(old_size_arr.append(tmp).c_str()); + } + add(new_data); + } + } + sub_packet_size = length; +} +int32 PacketStruct::GetArraySize(DataStruct* data_struct, int32 index) { + if (data_struct) { + const char* name = data_struct->GetArraySizeVariable(); + return GetArraySize(name, index); + } + return 0; +} +int32 PacketStruct::GetArraySize(const char* name, int32 index) { + int32 ret = 0; + DataStruct* ds = findStruct(name, index); + if (ds) { + if (ds->GetType() == DATA_STRUCT_INT8) { + int8* tmp = (int8*)GetStructPointer(ds); + ret = *tmp; + } + else if (ds->GetType() == DATA_STRUCT_INT16) { + int16* tmp = (int16*)GetStructPointer(ds); + ret = *tmp; + } + else if (ds->GetType() == DATA_STRUCT_INT32) { + int32* tmp = (int32*)GetStructPointer(ds); + ret = *tmp; + } + else if (ds->GetType() == DATA_STRUCT_INT64) { + int64* tmp = (int64*)GetStructPointer(ds); + ret = *tmp; + } + } + return ret; +} + +void PacketStruct::UpdateArrayByArrayLength(DataStruct* data_struct, int32 index, int32 size) { + if (data_struct) { + PacketStruct* packet = 0; + DataStruct* data = 0; + vector::iterator itr; + + for (itr = structs.begin(); itr != structs.end(); itr++) { + data = *itr; + if (strcmp(data->GetArraySizeVariable(), data_struct->GetName()) == 0) { + packet = GetPacketStructByName(data->GetName()); + if (packet) + packet->reAddAll(size); + return; + } + } + vector::iterator itr2; + for (itr2 = arrays.begin(); itr2 != arrays.end(); itr2++) { + packet = *itr2; + packet->UpdateArrayByArrayLength(data_struct, index, size); + } + } +} + +void PacketStruct::UpdateArrayByArrayLengthName(const char* name, int32 index, int32 size) { + UpdateArrayByArrayLength(findStruct(name, index), index, size); +} +int32 PacketStruct::GetArraySizeByName(const char* name, int32 index) { + DataStruct* ds1 = findStruct(name, index); + return GetArraySize(ds1, index); +} + +int16 PacketStruct::GetOpcodeValue(int16 client_version) { + int16 opcode = 0xFFFF; + bool client_cmd = false; + int16 OpcodeVersion = 0; +#ifndef LOGIN + if (GetOpcode() == OP_ClientCmdMsg && strlen(GetOpcodeType()) > 0 && !IsSubPacket()) + client_cmd = true; +#endif + if (client_cmd) { + EmuOpcode sub_opcode = EQOpcodeManager[0]->NameSearch(GetOpcodeType()); + if (sub_opcode != OP_Unknown) { //numbers should be used at OpcodeTypes, define them! + OpcodeVersion = GetOpcodeVersion(client_version); + if (EQOpcodeManager.count(OpcodeVersion) > 0) { + opcode = EQOpcodeManager[OpcodeVersion]->EmuToEQ(sub_opcode); + if (opcode == 0xCDCD) { + LogWrite(PACKET__ERROR, 0, "Packet", "Could not find valid opcode for opcode: %s and client_version: %i", EQOpcodeManager[OpcodeVersion]->EmuToName(sub_opcode), client_version); + } + } + } + } + else { + OpcodeVersion = GetOpcodeVersion(client_version); + if (EQOpcodeManager.count(OpcodeVersion) > 0) { + opcode = EQOpcodeManager[OpcodeVersion]->EmuToEQ(GetOpcode()); + if (opcode == 0xCDCD) { + LogWrite(PACKET__ERROR, 0, "Packet", "Could not find valid opcode for opcode: %s and client_version: %i", EQOpcodeManager[OpcodeVersion]->EmuToName(GetOpcode()), client_version); + } + } + } +#ifndef LOGIN + if(opcode == 0) + opcode = 0xFFFF; +#endif + return opcode; +} + +void PacketStruct::serializePacket(bool clear) { + if (clear) + Clear(); + bool client_cmd = false; + string client_data; +#ifndef LOGIN + if (GetOpcode() == OP_ClientCmdMsg && strlen(GetOpcodeType()) > 0 && !IsSubPacket()) + client_cmd = true; +#endif + DataStruct* data = 0; + vector::iterator itr; + for (itr = structs.begin(); itr != structs.end(); itr++) { + data = *itr; + if (data->IsOptional())//this would be false if the datastruct WAS optional, but was set + continue; + if (data->AddToStruct()) { + if (data->GetIfFlagNotSet() && CheckFlagExists(data->GetIfFlagNotSetVariable())) + continue; + if (data->GetIfFlagSet() && !CheckFlagExists(data->GetIfFlagSetVariable())) + continue; + if (data->GetIfSet() && data->GetIfSetVariable()) { + string varname = string(data->GetIfSetVariable()); + if (varname.find(",") < 0xFFFFFFFF) { + vector* varnames = SplitString(varname, ','); + if (varnames) { + bool should_continue = true; + for (int32 i = 0; i < varnames->size(); i++) { + if (GetVariableIsSet(varnames->at(i).c_str())) { + should_continue = false; + break; + } + } + safe_delete(varnames); + if (should_continue) + continue; + } + } + else { + if (!GetVariableIsSet(varname.c_str())) + continue; + } + } + if (data->GetIfNotSet() && data->GetIfNotSetVariable()) { + string varname = string(data->GetIfNotSetVariable()); + if (varname.find(",") < 0xFFFFFFFF) { + vector* varnames = SplitString(varname, ','); + if (varnames) { + bool should_continue = false; + for (int32 i = 0; i < varnames->size(); i++) { + if (!GetVariableIsNotSet(varnames->at(i).c_str())) { + should_continue = true; + break; + } + } + safe_delete(varnames); + if (should_continue) + continue; + } + } + else { + // Check to see if the variable contains %i, if it does assume we are in an array + // and get the current index from the end of the data struct's name + char name[250] = { 0 }; + if (varname.find("%i") < 0xFFFFFFFF) { + vector* varnames = SplitString(varname, '_'); + vector* indexes = SplitString(data->GetName(), '_'); + int index = 0; + if (indexes->size() > 0) + index = atoi(indexes->at(indexes->size() - 1).c_str()); + + sprintf(name, varname.c_str(), index); + } + else + strcpy(name, varname.c_str()); + if (!GetVariableIsNotSet(name)) + continue; + } + } + // Quick implementaion of IfVariableNotEquals + // probably not what it was intended for as it currently just checks to see if the given variable equals 1 + // should probably change it so you can define what the variable should or shouldn't equal + // + // ie: IfVariableNotEquals="stat_type_%i=1" + // would be a check to make sure that stat_type_%i does not equal 1 and if it does exclude this element + if (data->GetIfNotEquals() && data->GetIfNotEqualsVariable()) { + // Get the variable name + string varname = string(data->GetIfNotEqualsVariable()); + char name[250] = { 0 }; + // Check to see if the variable has %i in the name, if it does assume we are in an array and get the current + // index and replace it + if (varname.find("%i") < 0xFFFFFFFF) { + // Get the current index by getting the number at the end of the name + vector* varnames = SplitString(data->GetName(), '_'); + int index = atoi(varnames->at(varnames->size() - 1).c_str()); + + string substr = "stat_type"; + if (strncmp(varname.c_str(), substr.c_str(), strlen(substr.c_str())) == 0) { + // adorn_stat_subtype 18 chars + string temp = varname.substr(12); + char temp2[20] = { 0 }; + int index2 = atoi(temp.c_str()); + itoa(index2, temp2, 10); + varname = varname.substr(0, 12).append(temp2).append("_%i"); + } + sprintf(name, varname.c_str(), index); + safe_delete(varnames); + } + else + strcpy(name, varname.c_str()); + + // Get the data for the variable + int16 value = 0; + DataStruct* data_struct2 = findStruct(name, 0); + value = getType_int16(data_struct2); + // Hack for items as it is the only struct that currently uses IfVariableNotEquals + if (value == 1) + continue; + } + // copy and paste of the code above for IfEquals + if (data->GetIfEquals() && data->GetIfEqualsVariable()) { + // Get the variable name + string varname = string(data->GetIfEqualsVariable()); + char name[250] = { 0 }; + // Check to see if the variable has %i in the name, if it does assume we are in an array and get the current + // index and replace it + if (varname.find("%i") < 0xFFFFFFFF) { + // Get the current index by getting the number at the end of the name + vector* varnames = SplitString(data->GetName(), '_'); + int index = 0; + if (varnames) + index = atoi(varnames->at(varnames->size() - 1).c_str()); + + string substr = "stat_type"; + if (strncmp(varname.c_str(), substr.c_str(), strlen(substr.c_str())) == 0) { + // adorn_stat_subtype 18 chars + string temp = varname.substr(12); + char temp2[20] = { 0 }; + int index2 = atoi(temp.c_str()); + itoa(index2, temp2, 10); + varname = varname.substr(0, 12).append(temp2).append("_%i"); + } + sprintf(name, varname.c_str(), index); + safe_delete(varnames); + } + else + strcpy(name, varname.c_str()); + + // Get the data for the variable + int16 value = 0; + DataStruct* data_struct2 = findStruct(name, 0); + value = getType_int16(data_struct2); + // Hack for items as it is the only struct that currently uses IfVariableNotEquals + if (value != 1) + continue; + } + if (client_cmd) + AddSerializedData(data, 0, &client_data); + else + AddSerializedData(data); + } + } +#ifndef LOGIN + if (client_cmd) { + int16 opcode_val = GetOpcodeValue(client_version); + Clear(); + int32 size = client_data.length() + 3; //gotta add the opcode and oversized + int8 oversized = 0xFF; + int16 OpcodeVersion = GetOpcodeVersion(client_version); + if (opcode_val == EQOpcodeManager[OpcodeVersion]->EmuToEQ(OP_EqExamineInfoCmd) && client_version > 561) + size += (size - 9); + if (client_version <= 374) { + if (size >= 255) { + StructAddData(oversized, sizeof(int8), 0); + StructAddData(size, sizeof(int16), 0); + } + else { + StructAddData(size, sizeof(int8), 0); + } + StructAddData(oversized, sizeof(int8), 0); + StructAddData(opcode_val, sizeof(int16), 0); + } + else { + StructAddData(size, sizeof(int32), 0); + StructAddData(oversized, sizeof(int8), 0); + StructAddData(opcode_val, sizeof(int16), 0); + } + AddData(client_data); + } +#endif +} +int32 PacketStruct::GetTotalPacketSize() { + int32 retSize = 0; + DataStruct* data = 0; + vector::iterator itr; + EQ2_8BitString* tmp1 = 0; + EQ2_16BitString* tmp2 = 0; + EQ2_32BitString* tmp3 = 0; + for (itr = structs.begin(); itr != structs.end(); itr++) { + data = *itr; + switch (data->GetType()) { + case DATA_STRUCT_INT8: + case DATA_STRUCT_SINT8: + case DATA_STRUCT_CHAR: + retSize += (1 * data->GetLength()); + break; + case DATA_STRUCT_SINT16: + case DATA_STRUCT_INT16: + retSize += (2 * data->GetLength()); + break; + case DATA_STRUCT_INT32: + case DATA_STRUCT_SINT32: + case DATA_STRUCT_FLOAT: + case DATA_STRUCT_DOUBLE: + retSize += (4 * data->GetLength()); + break; + case DATA_STRUCT_SINT64: + case DATA_STRUCT_INT64: + retSize += (8 * data->GetLength()); + break; + case DATA_STRUCT_EQ2_8BIT_STRING: + tmp1 = (EQ2_8BitString*)GetStructPointer(data); + if (tmp1) { + for (int16 i = 0; i < data->GetLength(); i++) + retSize += tmp1[i].data.length(); + } + retSize += (1 * data->GetLength()); + break; + case DATA_STRUCT_EQ2_16BIT_STRING: { + tmp2 = (EQ2_16BitString*)GetStructPointer(data); + if (tmp2) { + for (int16 i = 0; i < data->GetLength(); i++) + retSize += tmp2[i].data.length(); + } + retSize += (2 * data->GetLength()); + break; + } + case DATA_STRUCT_EQ2_32BIT_STRING: { + tmp3 = (EQ2_32BitString*)GetStructPointer(data); + if (tmp3) { + for (int16 i = 0; i < data->GetLength(); i++) + retSize += tmp3[i].data.length(); + } + retSize += (4 * data->GetLength()); + break; + } + case DATA_STRUCT_ARRAY: { + int32 size = GetArraySize(data, 0); + PacketStruct* ps = GetPacketStructByName(data->GetName()); + if (ps && ps->GetSubPacketSize() != size) { + ps->reAddAll(size); + } + if (ps && size > 0) + retSize += ps->GetTotalPacketSize(); + break; + } + case DATA_STRUCT_COLOR: { + retSize += ((sizeof(int8) * 3) * data->GetLength()); + break; + } + case DATA_STRUCT_EQUIPMENT: { + retSize += ((((sizeof(int8) * 3) * 2) + sizeof(int16)) * data->GetLength()); + break; + } + } + } + return retSize; +} + +void PacketStruct::AddSerializedData(DataStruct* data, int32 index, string* datastring) { + switch (data->GetAddType()) { + case DATA_STRUCT_INT8: + StructAddData((int8*)GetStructPointer(data), data->GetLength(), sizeof(int8), datastring); + break; + case DATA_STRUCT_INT16: + if (data->GetOversized() > 0) { + if (*((int16*)GetStructPointer(data)) >= data->GetOversized()) { + StructAddData(data->GetOversizedByte(), sizeof(int8), datastring); + StructAddData((int16*)GetStructPointer(data), data->GetLength(), sizeof(int16), datastring); + } + else + StructAddData((int8*)GetStructPointer(data), data->GetLength(), sizeof(int8), datastring); + } + else + StructAddData((int16*)GetStructPointer(data), data->GetLength(), sizeof(int16), datastring); + break; + case DATA_STRUCT_INT32: + if (data->GetOversized() > 0) { + if (*((int32*)GetStructPointer(data)) >= data->GetOversized()) { + StructAddData(data->GetOversizedByte(), sizeof(int8), datastring); + StructAddData((int32*)GetStructPointer(data), data->GetLength(), sizeof(int32), datastring); + } + else + StructAddData((int16*)GetStructPointer(data), data->GetLength(), sizeof(int16), datastring); + } + else + StructAddData((int32*)GetStructPointer(data), data->GetLength(), sizeof(int32), datastring); + break; + case DATA_STRUCT_INT64: + StructAddData((int64*)GetStructPointer(data), data->GetLength(), sizeof(int64), datastring); + break; + case DATA_STRUCT_SINT8: + StructAddData((sint8*)GetStructPointer(data), data->GetLength(), sizeof(sint8), datastring); + break; + case DATA_STRUCT_SINT16: + if (data->GetOversized() > 0) { + sint16 val = *((sint16*)GetStructPointer(data)); + if (val >= data->GetOversized() || val <= (data->GetOversized() * -1)) { + StructAddData(data->GetOversizedByte(), sizeof(sint8), datastring); + StructAddData((sint16*)GetStructPointer(data), data->GetLength(), sizeof(sint16), datastring); + } + else + StructAddData((sint8*)GetStructPointer(data), data->GetLength(), sizeof(sint8), datastring); + } + else + StructAddData((sint16*)GetStructPointer(data), data->GetLength(), sizeof(sint16), datastring); + break; + case DATA_STRUCT_SINT32: + StructAddData((sint32*)GetStructPointer(data), data->GetLength(), sizeof(sint32), datastring); + break; + case DATA_STRUCT_SINT64: + StructAddData((sint64*)GetStructPointer(data), data->GetLength(), sizeof(sint64), datastring); + break; + case DATA_STRUCT_CHAR: + StructAddData((char*)GetStructPointer(data), data->GetLength(), sizeof(char), datastring); + break; + case DATA_STRUCT_FLOAT: + StructAddData((float*)GetStructPointer(data), data->GetLength(), sizeof(float), datastring); + break; + case DATA_STRUCT_DOUBLE: + StructAddData((double*)GetStructPointer(data), data->GetLength(), sizeof(double), datastring); + break; + case DATA_STRUCT_EQ2_8BIT_STRING: { + for (int16 i = 0; i < data->GetLength(); i++) { + EQ2_8BitString* ds = (EQ2_8BitString*)GetStructPointer(data); + AddDataString(ds[i], datastring); + } + break; + } + case DATA_STRUCT_EQ2_16BIT_STRING: { + for (int16 i = 0; i < data->GetLength(); i++) { + EQ2_16BitString* ds = (EQ2_16BitString*)GetStructPointer(data); + AddDataString(ds[i], datastring); + } + break; + } + case DATA_STRUCT_EQ2_32BIT_STRING: { + for (int16 i = 0; i < data->GetLength(); i++) { + EQ2_32BitString* ds = (EQ2_32BitString*)GetStructPointer(data); + AddDataString(ds[i], datastring); + } + break; + } + case DATA_STRUCT_ARRAY: { + int32 size = GetArraySize(data, 0); + PacketStruct* ps = GetPacketStructByName(data->GetName()); + if (ps && ps->GetSubPacketSize() != size) { + ps->reAddAll(size); + } + if (ps && size > 0) { + ps->serializePacket(); + string data = *(ps->getDataString()); + AddData(data, datastring); + } + break; + } + case DATA_STRUCT_COLOR: { + StructAddData((EQ2_Color*)GetStructPointer(data), data->GetLength(), sizeof(EQ2_Color), datastring); + break; + } + case DATA_STRUCT_EQUIPMENT: { + StructAddData((EQ2_EquipmentItem*)GetStructPointer(data), data->GetLength(), sizeof(EQ2_EquipmentItem), datastring); + break; + } + case DATA_STRUCT_ITEM: { + //DumpPacket((uchar*)GetStructPointer(data), data->GetItemSize()); + AddCharArray((char*)GetStructPointer(data), data->GetItemSize(), datastring); + break; + } + } +} + +int8 PacketStruct::getType_int8_ByName(const char* name, int32 index, bool force) { + DataStruct* data_struct = findStruct(name, index); + return getType_int8(data_struct, index, force); +} + +int16 PacketStruct::getType_int16_ByName(const char* name, int32 index, bool force) { + DataStruct* data_struct = findStruct(name, index); + return getType_int16(data_struct, index, force); +} + +int32 PacketStruct::getType_int32_ByName(const char* name, int32 index, bool force) { + DataStruct* data_struct = findStruct(name, index); + return getType_int32(data_struct, index, force); +} + +int64 PacketStruct::getType_int64_ByName(const char* name, int32 index, bool force) { + DataStruct* data_struct = findStruct(name, index); + return getType_int64(data_struct, index, force); +} + +sint8 PacketStruct::getType_sint8_ByName(const char* name, int32 index, bool force) { + DataStruct* data_struct = findStruct(name, index); + return getType_sint8(data_struct, index, force); +} + +sint16 PacketStruct::getType_sint16_ByName(const char* name, int32 index, bool force) { + DataStruct* data_struct = findStruct(name, index); + return getType_sint16(data_struct, index, force); +} + +sint32 PacketStruct::getType_sint32_ByName(const char* name, int32 index, bool force) { + DataStruct* data_struct = findStruct(name, index); + return getType_sint32(data_struct, index, force); +} + +sint64 PacketStruct::getType_sint64_ByName(const char* name, int32 index, bool force) { + DataStruct* data_struct = findStruct(name, index); + return getType_sint64(data_struct, index, force); +} + +float PacketStruct::getType_float_ByName(const char* name, int32 index, bool force) { + DataStruct* data_struct = findStruct(name, index); + return getType_float(data_struct, index, force); +} + +double PacketStruct::getType_double_ByName(const char* name, int32 index, bool force) { + DataStruct* data_struct = findStruct(name, index); + return getType_double(data_struct, index, force); +} + +char PacketStruct::getType_char_ByName(const char* name, int32 index, bool force) { + DataStruct* data_struct = findStruct(name, index); + return getType_char(data_struct, index, force); +} + +EQ2_8BitString PacketStruct::getType_EQ2_8BitString_ByName(const char* name, int32 index, bool force) { + DataStruct* data_struct = findStruct(name, index); + return getType_EQ2_8BitString(data_struct, index, force); +} + +EQ2_16BitString PacketStruct::getType_EQ2_16BitString_ByName(const char* name, int32 index, bool force) { + DataStruct* data_struct = findStruct(name, index); + return getType_EQ2_16BitString(data_struct, index, force); +} + +EQ2_32BitString PacketStruct::getType_EQ2_32BitString_ByName(const char* name, int32 index, bool force) { + DataStruct* data_struct = findStruct(name, index); + return getType_EQ2_32BitString(data_struct, index, force); +} + +EQ2_Color PacketStruct::getType_EQ2_Color_ByName(const char* name, int32 index, bool force) { + DataStruct* data_struct = findStruct(name, index); + return getType_EQ2_Color(data_struct, index, force); +} + +EQ2_EquipmentItem PacketStruct::getType_EQ2_EquipmentItem_ByName(const char* name, int32 index, bool force) { + DataStruct* data_struct = findStruct(name, index); + return getType_EQ2_EquipmentItem(data_struct, index, force); +} + +int8 PacketStruct::getType_int8(DataStruct* data_struct, int32 index, bool force) { + if (data_struct && ((data_struct->GetType() == DATA_STRUCT_INT8) || force)) { + int8* ptr = (int8*)GetStructPointer(data_struct); + return ptr[index]; + } + return 0; +} +int16 PacketStruct::getType_int16(DataStruct* data_struct, int32 index, bool force) { + if (data_struct && ((data_struct->GetType() == DATA_STRUCT_INT16) || force)) { + int16* ptr = (int16*)GetStructPointer(data_struct); + return ptr[index]; + } + return 0; +} +int32 PacketStruct::getType_int32(DataStruct* data_struct, int32 index, bool force) { + if (data_struct && ((data_struct->GetType() == DATA_STRUCT_INT32) || force)) { + int32* ptr = (int32*)GetStructPointer(data_struct); + return ptr[index]; + } + return 0; +} +int64 PacketStruct::getType_int64(DataStruct* data_struct, int32 index, bool force) { + if (data_struct && ((data_struct->GetType() == DATA_STRUCT_INT64) || force)) { + int64* ptr = (int64*)GetStructPointer(data_struct); + return ptr[index]; + } + return 0; +} +sint8 PacketStruct::getType_sint8(DataStruct* data_struct, int32 index, bool force) { + if (data_struct && ((data_struct->GetType() == DATA_STRUCT_SINT8) || force)) { + sint8* ptr = (sint8*)GetStructPointer(data_struct); + return ptr[index]; + } + return 0; +} +sint16 PacketStruct::getType_sint16(DataStruct* data_struct, int32 index, bool force) { + if (data_struct && ((data_struct->GetType() == DATA_STRUCT_SINT16) || force)) { + sint16* ptr = (sint16*)GetStructPointer(data_struct); + return ptr[index]; + } + return 0; +} +sint32 PacketStruct::getType_sint32(DataStruct* data_struct, int32 index, bool force) { + if (data_struct && ((data_struct->GetType() == DATA_STRUCT_SINT32) || force)) { + sint32* ptr = (sint32*)GetStructPointer(data_struct); + return ptr[index]; + } + return 0; +} +sint64 PacketStruct::getType_sint64(DataStruct* data_struct, int32 index, bool force) { + if (data_struct && ((data_struct->GetType() == DATA_STRUCT_SINT64) || force)) { + sint64* ptr = (sint64*)GetStructPointer(data_struct); + return ptr[index]; + } + return 0; +} +float PacketStruct::getType_float(DataStruct* data_struct, int32 index, bool force) { + if (data_struct && ((data_struct->GetType() == DATA_STRUCT_FLOAT) || force)) { + float* ptr = (float*)GetStructPointer(data_struct); + return ptr[index]; + } + return 0; +} +double PacketStruct::getType_double(DataStruct* data_struct, int32 index, bool force) { + if (data_struct && ((data_struct->GetType() == DATA_STRUCT_DOUBLE) || force)) { + double* ptr = (double*)GetStructPointer(data_struct); + return ptr[index]; + } + return 0; +} +char PacketStruct::getType_char(DataStruct* data_struct, int32 index, bool force) { + if (data_struct && ((data_struct->GetType() == DATA_STRUCT_CHAR) || force)) { + char* ptr = (char*)GetStructPointer(data_struct); + return ptr[index]; + } + return 0; +} +EQ2_8BitString PacketStruct::getType_EQ2_8BitString(DataStruct* data_struct, int32 index, bool force) { + if (data_struct && ((data_struct->GetType() == DATA_STRUCT_EQ2_8BIT_STRING) || force)) { + EQ2_8BitString* ptr = (EQ2_8BitString*)GetStructPointer(data_struct); + return ptr[index]; + } + EQ2_8BitString ret; + ret.size = 0; + return ret; +} +EQ2_16BitString PacketStruct::getType_EQ2_16BitString(DataStruct* data_struct, int32 index, bool force) { + if (data_struct && ((data_struct->GetType() == DATA_STRUCT_EQ2_16BIT_STRING) || force)) { + EQ2_16BitString* ptr = (EQ2_16BitString*)GetStructPointer(data_struct); + return ptr[index]; + } + EQ2_16BitString ret; + ret.size = 0; + return ret; +} +EQ2_32BitString PacketStruct::getType_EQ2_32BitString(DataStruct* data_struct, int32 index, bool force) { + if (data_struct && ((data_struct->GetType() == DATA_STRUCT_EQ2_32BIT_STRING) || force)) { + EQ2_32BitString* ptr = (EQ2_32BitString*)GetStructPointer(data_struct); + return ptr[index]; + } + EQ2_32BitString ret; + ret.size = 0; + return ret; +} +EQ2_Color PacketStruct::getType_EQ2_Color(DataStruct* data_struct, int32 index, bool force) { + if (data_struct && ((data_struct->GetType() == DATA_STRUCT_COLOR) || force)) { + EQ2_Color* ptr = (EQ2_Color*)GetStructPointer(data_struct); + return ptr[index]; + } + EQ2_Color ret; + ret.blue = 0; + ret.red = 0; + ret.green = 0; + return ret; +} +EQ2_EquipmentItem PacketStruct::getType_EQ2_EquipmentItem(DataStruct* data_struct, int32 index, bool force) { + if (data_struct && ((data_struct->GetType() == DATA_STRUCT_EQUIPMENT) || force)) { + EQ2_EquipmentItem* ptr = (EQ2_EquipmentItem*)GetStructPointer(data_struct); + return ptr[index]; + } + EQ2_EquipmentItem ret; + ret.color.blue = 0; + ret.color.red = 0; + ret.color.green = 0; + ret.highlight.blue = 0; + ret.highlight.red = 0; + ret.highlight.green = 0; + ret.type = 0; + return ret; +} + +bool PacketStruct::SetOpcode(const char* new_opcode) { + opcode = EQOpcodeManager[0]->NameSearch(new_opcode); + if (opcode == OP_Unknown) { +#ifndef MINILOGIN + LogWrite(PACKET__ERROR, 0, "Packet", "Warning: PacketStruct '%s' uses an unknown opcode named '%s', this struct cannot be serialized directly.", GetName(), new_opcode); +#endif + return false; + } + return true; +} + +EQ2Packet* PacketStruct::serialize() { + serializePacket(); + + if (GetOpcode() != OP_Unknown) + return new EQ2Packet(GetOpcode(), getData(), getDataSize()); + else { +#ifndef MINILOGIN + LogWrite(PACKET__ERROR, 0, "Packet", "Warning: PacketStruct '%s' uses an unknown opcode and cannot be serialized directly.", GetName()); +#endif + return 0; + } +} + +EQ2Packet* PacketStruct::serializeCountPacket(int16 version, int8 offset, uchar* orig_packet, uchar* xor_packet) { + string* packet_data = serializeString(); + uchar* data = (uchar*)packet_data->c_str(); + int32 size = packet_data->size(); + uchar* packed_data = new uchar[size + 1000]; // this size + 20 is poorly defined, depending on the packet data, we could use a additional length of 1K+ + memset(packed_data, 0, size + 1000); + if (orig_packet && xor_packet) { + memcpy(xor_packet, data + 6, size - 6 - offset); + Encode(xor_packet, orig_packet, size - 6 - offset); + size = Pack(packed_data, xor_packet, size - 6 - offset, size + 1000, version); + } + else + size = Pack(packed_data, data + 6, packet_data->size() - 6 - offset, packet_data->size() + 1000, version); + uchar* combined = new uchar[size + sizeof(int16) + offset]; + memset(combined, 0, size + sizeof(int16) + offset); + uchar* ptr = combined; + memcpy(ptr, data, sizeof(int16)); + ptr += sizeof(int16); + memcpy(ptr, packed_data, size); + if (offset > 0) { + ptr += size; + uchar* ptr2 = data; + ptr2 += packet_data->size() - offset; + memcpy(ptr, ptr2, offset); + } + EQ2Packet* app = new EQ2Packet(GetOpcode(), combined, size + sizeof(int16) + offset); + safe_delete_array(packed_data); + safe_delete_array(combined); + return app; +} + +bool PacketStruct::IsSubPacket() { + return sub_packet; +} +void PacketStruct::IsSubPacket(bool new_val) { + sub_packet = new_val; +} +int32 PacketStruct::GetSubPacketSize() { + return sub_packet_size; +} +void PacketStruct::SetSubPacketSize(int32 new_size) { + sub_packet_size = new_size; +} +void* PacketStruct::GetStructPointer(DataStruct* data_struct, bool erase) { + try { + map::iterator tmpitr = struct_data.find(data_struct); + if (tmpitr != struct_data.end()) { + if (erase) + struct_data.erase(data_struct); + return tmpitr->second; + } + else { + PacketStruct* packet = 0; + vector::iterator itr2; + for (itr2 = arrays.begin(); itr2 != arrays.end(); itr2++) { + packet = *itr2; + if (packet) { + void* tmp = packet->GetStructPointer(data_struct, erase); + if (tmp != 0) + return tmp; + } + } + } + } + catch (...) { + cout << "Caught Exception...\n"; + } + return 0; +} + +vector PacketStruct::GetDataStructs() { + vector ret; + DataStruct* ds = 0; + vector::iterator itr; + for (itr = structs.begin(); itr != structs.end(); itr++) { + ds = *itr; + if (ds->GetType() == DATA_STRUCT_ARRAY) { + int32 size = GetArraySize(ds, 0); + PacketStruct* ps = GetPacketStructByName(ds->GetName()); + if (ps && ps->GetSubPacketSize() != size) { + ps->reAddAll(size); + } + if (ps) { + vector ret2 = ps->GetDataStructs(); + vector::iterator itr2; + for (itr2 = ret2.begin(); itr2 != ret2.end(); itr2++) { + ret.push_back(*itr2); + } + } + } + else if (ds->GetLength() == 0 && ds->GetType() == DATA_STRUCT_ARRAY) { + int32 size = GetArraySize(ds, 0); + PacketStruct* ps = GetPacketStructByName(ds->GetName()); + if (ps && ps->GetSubPacketSize() != size) { + ps->reAddAll(size); + } + if (ps) { + vector ret2 = ps->GetDataStructs(); + vector::iterator itr2; + for (itr2 = ret2.begin(); itr2 != ret2.end(); itr2++) { + ret.push_back(*itr2); + } + } + } + else + ret.push_back(ds); + } + return ret; +} + +void PacketStruct::PrintPacket() { + DataStruct* ds = 0; + vector::iterator itr; + for (itr = structs.begin(); itr != structs.end(); itr++) { + ds = *itr; + if (!ds->AddToStruct()) + continue; + for (int16 i = 0; i < ds->GetLength(); i++) { + cout << "Name: " << ds->GetName() << " \tIndex: " << i << " \tType: "; + switch (ds->GetType()) { + case DATA_STRUCT_INT8: + printf("int8\t\tData: %i", getType_int8_ByName(ds->GetName(), i)); + break; + case DATA_STRUCT_INT16: + printf("int16\t\tData: %i", getType_int16_ByName(ds->GetName(), i)); + break; + case DATA_STRUCT_INT32: + printf("int32\t\tData: %u", getType_int32_ByName(ds->GetName(), i)); + break; + case DATA_STRUCT_INT64: + printf("int64\t\tData: %llu", getType_int64_ByName(ds->GetName(), i)); + break; + case DATA_STRUCT_SINT8: + printf("sint8\t\tData: %i", getType_sint8_ByName(ds->GetName(), i)); + break; + case DATA_STRUCT_SINT16: + printf("sint16\t\tData: %i", getType_sint16_ByName(ds->GetName(), i)); + break; + case DATA_STRUCT_SINT32: + printf("sint32\t\tData: %i", getType_sint32_ByName(ds->GetName(), i)); + break; + case DATA_STRUCT_SINT64: + printf("sint64\t\tData: %lli", getType_sint64_ByName(ds->GetName(), i)); + break; + case DATA_STRUCT_CHAR: + printf("char\t\tData: %c", getType_char_ByName(ds->GetName(), i)); + break; + case DATA_STRUCT_FLOAT: + printf("float\t\tData: %f", getType_float_ByName(ds->GetName(), i)); + break; + case DATA_STRUCT_DOUBLE: + printf("double\t\tData: %f", getType_double_ByName(ds->GetName(), i)); + break; + case DATA_STRUCT_EQ2_8BIT_STRING: + printf("EQ2_8BitString\tData: %s", getType_EQ2_8BitString_ByName(ds->GetName(), i).data.c_str()); + break; + case DATA_STRUCT_EQ2_16BIT_STRING: + printf("EQ2_16BitString\tData: %s", getType_EQ2_16BitString_ByName(ds->GetName(), i).data.c_str()); + break; + case DATA_STRUCT_EQ2_32BIT_STRING: { + printf("EQ2_32BitString\tData: %s", getType_EQ2_32BitString_ByName(ds->GetName(), i).data.c_str()); + break; + } + case DATA_STRUCT_ITEM: { + if (ds->GetItemSize() > 0) { + DumpPacket((uchar*)GetStructPointer(ds), ds->GetItemSize()); + } + break; + } + case DATA_STRUCT_ARRAY: { + int32 size = GetArraySize(ds, 0); + PacketStruct* ps = GetPacketStructByName(ds->GetName()); + if (ps && ps->GetSubPacketSize() != size) { + ps->reAddAll(size); + } + if (ps) { + cout << "Array:\tData: \n"; + ps->PrintPacket(); + } + break; + } + case DATA_STRUCT_COLOR: { + cout.unsetf(ios_base::dec); + cout.setf(ios_base::hex); + printf("EQ2_Color\tData: "); + EQ2_Color tmp = getType_EQ2_Color_ByName(ds->GetName(), i); + printf("R: %i", tmp.red); + printf(", G: %i", tmp.green); + printf(", B: %i", tmp.blue); + break; + } + case DATA_STRUCT_EQUIPMENT: { + cout.unsetf(ios_base::dec); + cout.setf(ios_base::hex); + printf("EQ2_EquipmentItem\tData: "); + EQ2_EquipmentItem tmp = getType_EQ2_EquipmentItem_ByName(ds->GetName(), i); + printf("type: "); + printf(" ,color R: %i", tmp.color.red); + printf(" ,color G: %i", tmp.color.green); + printf(" ,color B: %i", tmp.color.blue); + printf(" ,hl R: %i", tmp.highlight.red); + printf(" ,hl G: %i", tmp.highlight.green); + printf(" ,hl B: %i", tmp.highlight.blue); + break; + } + } + cout << endl; + } + if (ds->GetLength() == 0 && ds->GetType() == DATA_STRUCT_ARRAY) { + int32 size = GetArraySize(ds, 0); + PacketStruct* ps = GetPacketStructByName(ds->GetName()); + if (ps && ps->GetSubPacketSize() != size) { + ps->reAddAll(size); + } + if (ps) { + cout << "Array:\tData: \n"; + ps->PrintPacket(); + } + } + } +} + +void PacketStruct::LoadFromPacketStruct(PacketStruct* packet, char* substruct_name) { + vector::iterator itr; + DataStruct* ds = 0; + char name[512]; + + //scatman (1/30/2012): these declarations are here to get rid of a linux compile error "taking address of temporary" + EQ2_8BitString str8; + EQ2_16BitString str16; + EQ2_32BitString str32; + EQ2_EquipmentItem equip; + + for (itr = structs.begin(); itr != structs.end(); itr++) { + ds = *itr; + for (int16 i = 0; i < ds->GetLength(); i++) { + memset(name, 0, sizeof(name)); + + if (substruct_name) + snprintf(name, sizeof(name), "%s_%s_0", substruct_name, ds->GetName()); + else + strncpy(name, ds->GetName(), sizeof(name)); + name[sizeof(name) - 1] = '\0'; + + switch (ds->GetType()) { + case DATA_STRUCT_INT8: + setData(ds, packet->getType_int8_ByName(name, i), i); + break; + case DATA_STRUCT_SINT8: + setData(ds, packet->getType_sint8_ByName(name, i), i); + break; + case DATA_STRUCT_CHAR: + setData(ds, packet->getType_char_ByName(name, i), i); + break; + case DATA_STRUCT_SINT16: + setData(ds, packet->getType_sint16_ByName(name, i), i); + break; + case DATA_STRUCT_INT16: + setData(ds, packet->getType_int16_ByName(name, i), i); + break; + case DATA_STRUCT_INT32: + setData(ds, packet->getType_int32_ByName(name, i), i); + break; + case DATA_STRUCT_INT64: + setData(ds, packet->getType_int64_ByName(name, i), i); + break; + case DATA_STRUCT_SINT32: + setData(ds, packet->getType_sint32_ByName(name, i), i); + break; + case DATA_STRUCT_SINT64: + setData(ds, packet->getType_sint64_ByName(name, i), i); + break; + case DATA_STRUCT_FLOAT: + setData(ds, packet->getType_float_ByName(name, i), i); + break; + case DATA_STRUCT_DOUBLE: + setData(ds, packet->getType_double_ByName(name, i), i); + break; + case DATA_STRUCT_EQ2_8BIT_STRING: + str8 = packet->getType_EQ2_8BitString_ByName(name, i); + setData(ds, &str8, i); + break; + case DATA_STRUCT_EQ2_16BIT_STRING: + str16 = packet->getType_EQ2_16BitString_ByName(name, i); + setData(ds, &str16, i); + break; + case DATA_STRUCT_EQ2_32BIT_STRING: + str32 = packet->getType_EQ2_32BitString_ByName(name, i); + setData(ds, &str32, i); + break; + case DATA_STRUCT_ARRAY: { + int32 size = GetArraySize(ds, 0); + PacketStruct* ps = GetPacketStructByName(ds->GetName()); + if (ps && size > 0) + ps->LoadFromPacketStruct(packet, substruct_name); + break; + } + case DATA_STRUCT_COLOR: + setColor(ds, packet->getType_EQ2_Color_ByName(name, i), i); + break; + case DATA_STRUCT_EQUIPMENT: + equip = packet->getType_EQ2_EquipmentItem_ByName(name, i); + setEquipmentByName(ds->GetName(), &equip, i); + break; + default: + break; + } + } + } +} + +#ifdef WORLD +void PacketStruct::setItem(DataStruct* ds, Item* item, Player* player, int32 index, sint8 offset, bool loot_item, bool make_empty_item_packet, bool inspect) { + if (!ds) + return; + uchar* ptr = (uchar*)GetStructPointer(ds); + + if(!item) { + if(make_empty_item_packet) { + if(client_version <= 373) { + // for player inspection this will offset the parts of the packet that have no items + uchar bogusItemBuffer[] = {0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x64,0x3C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + int sizeOfArray = sizeof(bogusItemBuffer) / sizeof(bogusItemBuffer[0]); + ds->SetItemSize(sizeOfArray); + memcpy(ptr, bogusItemBuffer, sizeOfArray); + } + else if(client_version <= 561) { + // for player inspection this will offset the parts of the packet that have no items + uchar bogusItemBuffer[] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x8C,0x5A,0xF1,0xD2,0x8C,0x5A,0xF1,0xD2,0x01,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00}; + int sizeOfArray = sizeof(bogusItemBuffer) / sizeof(bogusItemBuffer[0]); + ds->SetItemSize(sizeOfArray); + memcpy(ptr, bogusItemBuffer, sizeOfArray); + } + else { + // for player inspection this will offset the parts of the packet that have no items + uchar bogusItemBuffer[] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00 /*0x68 was item flags*/,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + int sizeOfArray = sizeof(bogusItemBuffer) / sizeof(bogusItemBuffer[0]); + ds->SetItemSize(sizeOfArray); + memcpy(ptr, bogusItemBuffer, sizeOfArray); + } + } + return; + } + PacketStruct* packet = item->PrepareItem(client_version, false, loot_item, inspect); + if (packet) { + int16 item_version = GetItemPacketType(packet->GetVersion()); + // newer clients can handle the item structure without the loot_item flag set to true, older clients like DoF need a smaller subpacket of item + item->serialize(packet, true, player, item_version, 0, (packet->GetVersion() <= 561) ? loot_item : false, inspect); + + string* generic_string_data = packet->serializeString(); + int32 size = generic_string_data->length(); // had to set to 81 + int32 actual_length = size; + + if(client_version <= 373 && make_empty_item_packet && inspect) { + size += 2; // end padding for the specialized item (used for player inspection) + } + else if(offset > 12) { + size += 6; // end padding for the specialized item (used for player inspection) + } + //DumpPacket((uchar*)generic_string_data->c_str(), size); + if (size <= 13) + { + ds->SetIsSet(false); + return; + } + size -= (9 + offset); + if (client_version > 561 && item->IsBag() == false && item->IsBauble() == false && item->IsFood() == false && (offset == 0 || offset == -1 || offset == 2)) + size = (size * 2) - 5; + uchar* out_data = new uchar[size + 1]; + memset(out_data, 0, size+1); + uchar* out_ptr = out_data; + memcpy(out_ptr, (uchar*)generic_string_data->c_str() + (9 + offset), actual_length - (9 + offset)); + //DumpPacket((uchar*)generic_string_data->c_str() + (9 + offset), size); + //without these it will prompt for your character name + if (offset == 0 || offset == -1 || offset == 2) { + if (client_version <= 561 && item->details.count > 0) + out_data[0] = item->details.count; + else + out_data[0] = 1; + } + // + out_ptr += generic_string_data->length() - (10 + offset); + if (client_version > 561 && item->IsBag() == false && item->IsBauble() == false && item->IsFood() == false && (offset == 0 || offset == -1 || offset == 2)) { + out_data[4] = 0x80; + memcpy(out_ptr, (uchar*)generic_string_data->c_str() + (13 + offset), generic_string_data->length() - (13 + offset)); + } + ds->SetItemSize(size); + memcpy(ptr, out_data, size); + safe_delete_array(out_data); + delete packet; + } + //DumpPacket(ptr2, ds->GetItemSize()); +} +void PacketStruct::setItemByName(const char* name, Item* item, Player* player, int32 index, sint8 offset, bool loot_item, bool make_empty_item_packet, bool inspect) { + setItem(findStruct(name, index), item, player, index, offset, loot_item, make_empty_item_packet, inspect); +} +void PacketStruct::setItemArrayDataByName(const char* name, Item* item, Player* player, int32 index1, int32 index2, sint8 offset, bool loot_item, bool make_empty_item_packet, bool inspect) { + char tmp[10] = { 0 }; + sprintf(tmp, "_%i", index1); + string name2 = string(name).append(tmp); + setItem(findStruct(name2.c_str(), index1, index2), item, player, index2, offset, loot_item, make_empty_item_packet, inspect); +} + +void PacketStruct::ResetData() { + if (this == nullptr) { + LogWrite(PACKET__ERROR, 0, "PacketStruct", "ResetData called on a null PacketStruct pointer"); + return; + } + vector::iterator itr; + for (itr = structs.begin(); itr != structs.end(); itr++) { + DataStruct* ds = *itr; + void* ptr = GetStructPointer(ds); + if (!ptr) + continue; + switch (ds->GetType()) + { + case DATA_STRUCT_EQ2_8BIT_STRING: { + EQ2_8BitString* real_ptr = (EQ2_8BitString*)ptr; + real_ptr->size = 0; + real_ptr->data.clear(); + break; + } + case DATA_STRUCT_EQ2_16BIT_STRING: { + EQ2_16BitString* real_ptr = (EQ2_16BitString*)ptr; + real_ptr->size = 0; + real_ptr->data.clear(); + break; + } + case DATA_STRUCT_EQ2_32BIT_STRING: { + EQ2_32BitString* real_ptr = (EQ2_32BitString*)ptr; + real_ptr->size = 0; + real_ptr->data.clear(); + break; + } + case DATA_STRUCT_EQUIPMENT: { + memset(ptr, 0, sizeof(EQ2_EquipmentItem) * ds->GetLength()); + break; + } + case DATA_STRUCT_DOUBLE: { + memset(ptr, 0, sizeof(double) * ds->GetLength()); + break; + } + case DATA_STRUCT_FLOAT: { + memset(ptr, 0, sizeof(float) * ds->GetLength()); + break; + } + case DATA_STRUCT_INT8: { + memset(ptr, 0, sizeof(int8) * ds->GetLength()); + break; + } + case DATA_STRUCT_INT16: { + memset(ptr, 0, sizeof(int16) * ds->GetLength()); + break; + } + case DATA_STRUCT_INT32: { + memset(ptr, 0, sizeof(int32) * ds->GetLength()); + break; + } + case DATA_STRUCT_INT64: { + memset(ptr, 0, sizeof(int64) * ds->GetLength()); + break; + } + case DATA_STRUCT_SINT8: { + memset(ptr, 0, sizeof(sint8) * ds->GetLength()); + break; + } + case DATA_STRUCT_SINT16: { + memset(ptr, 0, sizeof(sint16) * ds->GetLength()); + break; + } + case DATA_STRUCT_SINT32: { + memset(ptr, 0, sizeof(sint32) * ds->GetLength()); + break; + } + case DATA_STRUCT_SINT64: { + memset(ptr, 0, sizeof(sint64) * ds->GetLength()); + break; + } + case DATA_STRUCT_ITEM: { + memset(ptr, 0, 10000 * ds->GetLength()); + break; + } + case DATA_STRUCT_CHAR: { + memset(ptr, 0, sizeof(char) * ds->GetLength()); + break; + } + case DATA_STRUCT_COLOR: { + memset(ptr, 0, sizeof(EQ2_Color) * ds->GetLength()); + break; + } + } + } + vector::iterator itr2; + for (itr2 = arrays.begin(); itr2 != arrays.end(); itr2++) + (*itr2)->ResetData(); +} +#endif diff --git a/old/PacketStruct.h b/old/PacketStruct.h new file mode 100644 index 0000000..98bca7d --- /dev/null +++ b/old/PacketStruct.h @@ -0,0 +1,513 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#ifndef __EQ2_PACKETSTRUCT__ +#define __EQ2_PACKETSTRUCT__ +#include "types.h" +#include "DataBuffer.h" +#include "opcodemgr.h" + +#include +#include +#ifdef WORLD +class Item; +class Player; +#endif +extern mapEQOpcodeManager; +using namespace std; + +#define DATA_STRUCT_NONE 0 +#define DATA_STRUCT_INT8 1 +#define DATA_STRUCT_INT16 2 +#define DATA_STRUCT_INT32 3 +#define DATA_STRUCT_INT64 4 +#define DATA_STRUCT_FLOAT 5 +#define DATA_STRUCT_DOUBLE 6 +#define DATA_STRUCT_COLOR 7 +#define DATA_STRUCT_SINT8 8 +#define DATA_STRUCT_SINT16 9 +#define DATA_STRUCT_SINT32 10 +#define DATA_STRUCT_CHAR 11 +#define DATA_STRUCT_EQ2_8BIT_STRING 12 +#define DATA_STRUCT_EQ2_16BIT_STRING 13 +#define DATA_STRUCT_EQ2_32BIT_STRING 14 +#define DATA_STRUCT_EQUIPMENT 15 +#define DATA_STRUCT_ARRAY 16 +#define DATA_STRUCT_ITEM 17 +#define DATA_STRUCT_SINT64 18 + +class DataStruct { +public: + DataStruct(); + DataStruct(DataStruct* data_struct); + DataStruct(const char* new_name, int8 new_type, int32 new_length = 1, int8 new_type2 = DATA_STRUCT_NONE); + DataStruct(const char* new_name, const char* new_type, int32 new_length = 1, const char* new_type2 = 0); + DataStruct(const char* new_name, int32 new_length); + void SetType(const char* new_type, int8* output_type); + void SetType(int8 new_type); + void SetName(const char* new_name); + void SetLength(int32 new_length); + void SetArraySizeVariable(const char* new_name); + void SetDefaultValue(int8 new_val); + void SetMaxArraySize(int8 size); + void SetOversized(int8 val); + void SetOversizedByte(int8 val); + void SetAddToStruct(bool val); + void SetAddType(int8 new_type); + void SetPackedIndex(int8 new_index); + void SetPackedSizeVariable(const char* new_name); + void SetPacked(const char* value); + void SetItemSize(int32 val); + void SetIfSetVariable(const char* variable); + void SetIfNotSetVariable(const char* variable); + void SetIfEqualsVariable(const char* variable); + void SetIfNotEqualsVariable(const char* variable); + void SetIfFlagSetVariable(const char* variable); + void SetIfFlagNotSetVariable(const char* variable); + void SetIsSet(bool val); + void SetIsOptional(bool val); + + int8 GetPackedIndex(); + const char* GetPackedSizeVariable(); + const char* GetArraySizeVariable(); + int8 GetDefaultValue(); + int8 GetOversized(); + int8 GetOversizedByte(); + int8 GetMaxArraySize(); + int8 GetType(); + int8 GetType2(); + const char* GetName(); + string GetStringName(); + int32 GetLength(); + bool AddToStruct(); + int8 GetAddType(); + int32 GetItemSize(); + bool GetIfSet(); + const char* GetIfSetVariable(); + bool GetIfNotSet(); + const char* GetIfNotSetVariable(); + bool GetIfEquals(); + const char* GetIfEqualsVariable(); + bool GetIfNotEquals(); + const char* GetIfNotEqualsVariable(); + bool GetIfFlagSet(); + const char* GetIfFlagSetVariable(); + bool GetIfFlagNotSet(); + const char* GetIfFlagNotSetVariable(); + bool IsSet(); + bool IsOptional(); + int32 GetDataSizeInBytes(); + string AppendVariable(string orig, const char* val); + void AddIfSetVariable(const char* val) { + if (val) { + if_set_variable = AppendVariable(if_set_variable, val); + is_set = true; + } + } + void AddIfNotSetVariable(const char* val) { + if (val) { + if_not_set_variable = AppendVariable(if_not_set_variable, val); + if_not_set = true; + } + } + +private: + bool is_set; + bool if_not_set; + bool optional; + bool if_set; + bool if_not_equals; + bool if_equals; + bool if_flag_set; + bool if_flag_not_set; + string if_flag_not_set_variable; + string if_flag_set_variable; + string if_not_equals_variable; + string if_equals_variable; + string if_not_set_variable; + string if_set_variable; + int8 oversized; + int8 oversized_byte; + bool add; + int8 addType; + int8 maxArraySize; + string array_size_variable; + string name; + int8 type; + int8 default_value; + int8 type2; + int32 length; + int32 item_size; +}; +class PacketStruct : public DataBuffer { +public: + PacketStruct(); + PacketStruct(PacketStruct* packet, bool sub); + PacketStruct(PacketStruct* packet, int16 in_client_version); + ~PacketStruct(); + void add(DataStruct* data); + void reAddAll(int32 length); + void add(PacketStruct* packet_struct); + void addPacketArrays(PacketStruct* packet); + void deletePacketArrays(PacketStruct* packet); + void deleteDataStructs(vector* data_structs); + void setSmallStringByName(const char* name, const char* text, int32 index = 0); + void setMediumStringByName(const char* name, const char* text, int32 index = 0); + void setLargeStringByName(const char* name, const char* text, int32 index = 0); + void setSmallString(DataStruct* data_struct, const char* text, int32 index = 0); + void setMediumString(DataStruct* data_struct, const char* text, int32 index = 0); + void setLargeString(DataStruct* data_struct, const char* text, int32 index = 0); + void renameSubstructArray(const char* substruct, int32 index); + template void setSubstructSubstructDataByName(const char* substruct_name1, const char* substruct_name2, const char* name, Data data, int32 substruct_index1 = 0, int32 substruct_index2 = 0, int32 index = 0) { + char tmp[15] = { 0 }; + sprintf(tmp, "_%i_%i", substruct_index1, substruct_index2); + string name2 = string(substruct_name1).append("_").append(substruct_name2).append("_").append(name).append(tmp); + setData(findStruct(name2.c_str(), index), data, index); + } + template void setSubstructDataByName(const char* substruct_name, const char* name, Data data, int32 substruct_index = 0, int32 index = 0) { + char tmp[10] = { 0 }; + sprintf(tmp, "_%i", substruct_index); + string name2 = string(substruct_name).append("_").append(name).append(tmp); + setData(findStruct(name2.c_str(), index), data, index); + } + template void setSubstructColorByName(const char* substruct_name, const char* name, Data data, int32 substruct_index = 0, int32 index = 0) { + char tmp[10] = { 0 }; + sprintf(tmp, "_%i", substruct_index); + string name2 = string(substruct_name).append("_").append(name).append(tmp); + setColor(findStruct(name2.c_str(), index), data, index); + } + template void setSubstructArrayDataByName(const char* substruct_name, const char* name, Data data, int32 index = 0, int32 substruct_index = 0) { + char tmp[10] = { 0 }; + sprintf(tmp, "_%i", substruct_index); + string name2 = string(substruct_name).append("_").append(name).append(tmp); + setData(findStruct(name2.c_str(), substruct_index, index), data, index); + } + template void setSubstructArrayColorByName(const char* substruct_name, const char* name, Data data, int32 substruct_index = 0, int32 index = 0) { + char tmp[10] = { 0 }; + sprintf(tmp, "_%i", substruct_index); + string name2 = string(substruct_name).append("_").append(name).append(tmp); + setColor(findStruct(name2.c_str(), index, substruct_index), data, index); + } + template void setDataByName(const char* name, Data data, int32 index = 0, bool use_second_type = false) { + setData(findStruct(name, index), data, index, use_second_type); + } + template void setDataByName(const char* name, Data* data, int32 index = 0, bool use_second_type = false) { + setData(findStruct(name, index), data, index, use_second_type); + } + template void setSubArrayDataByName(const char* name, Data data, int32 index1 = 0, int32 index2 = 0, int32 index3 = 0) { + char tmp[20] = { 0 }; + sprintf(tmp, "%i_%i", index1, index2); + string name2 = string(name).append(tmp); + setData(findStruct(name2.c_str(), index2, index3), data, index3); + } + template void setArrayDataByName(const char* name, Data data, int32 index1 = 0, int32 index2 = 0, bool use_second_type = false) { + char tmp[10] = { 0 }; + sprintf(tmp, "_%i", index1); + string name2 = string(name).append(tmp); + setData(findStruct(name2.c_str(), index1, index2), data, index2, use_second_type); + } + void setArrayAddToPacketByName(const char* name, bool new_val, int32 index1 = 0, int32 index2 = 0) { + char tmp[10] = { 0 }; + sprintf(tmp, "_%i", index1); + string name2 = string(name).append(tmp); + DataStruct* data = findStruct(name2.c_str(), index2); + if (data) + data->SetAddToStruct(new_val); + } + void setAddToPacketByName(const char* name, bool new_val, int32 index = 0) { + DataStruct* data = findStruct(name, index); + if (data) + data->SetAddToStruct(new_val); + } + void setAddTypePacketByName(const char* name, int8 new_val, int32 index = 0) { + DataStruct* data = findStruct(name, index); + if (data) + data->SetAddType(new_val); + } + const char* GetOpcodeType(); + bool IsSubPacket(); + void IsSubPacket(bool new_val); + int32 GetSubPacketSize(); + void SetSubPacketSize(int32 new_size); + void SetOpcodeType(const char* opcodeType); + int32 GetArraySizeByName(const char* name, int32 index); + int32 GetArraySize(DataStruct* data_struct, int32 index); + int32 GetArraySize(const char* name, int32 index); + void LoadFromPacketStruct(PacketStruct* packet, char* substruct_name = 0); + bool GetVariableIsSet(const char* name); + bool GetVariableIsNotSet(const char* name); + + int8 getType_int8_ByName(const char* name, int32 index = 0, bool force = false); + int16 getType_int16_ByName(const char* name, int32 index = 0, bool force = false); + int32 getType_int32_ByName(const char* name, int32 index = 0, bool force = false); + int64 getType_int64_ByName(const char* name, int32 index = 0, bool force = false); + sint8 getType_sint8_ByName(const char* name, int32 index = 0, bool force = false); + sint16 getType_sint16_ByName(const char* name, int32 index = 0, bool force = false); + sint32 getType_sint32_ByName(const char* name, int32 index = 0, bool force = false); + sint64 getType_sint64_ByName(const char* name, int32 index = 0, bool force = false); + float getType_float_ByName(const char* name, int32 index = 0, bool force = false); + double getType_double_ByName(const char* name, int32 index = 0, bool force = false); + char getType_char_ByName(const char* name, int32 index = 0, bool force = false); + EQ2_8BitString getType_EQ2_8BitString_ByName(const char* name, int32 index = 0, bool force = false); + EQ2_16BitString getType_EQ2_16BitString_ByName(const char* name, int32 index = 0, bool force = false); + EQ2_32BitString getType_EQ2_32BitString_ByName(const char* name, int32 index = 0, bool force = false); + EQ2_Color getType_EQ2_Color_ByName(const char* name, int32 index = 0, bool force = false); + EQ2_EquipmentItem getType_EQ2_EquipmentItem_ByName(const char* name, int32 index = 0, bool force = false); + + int8 getType_int8(DataStruct* data_struct, int32 index = 0, bool force = false); + int16 getType_int16(DataStruct* data_struct, int32 index = 0, bool force = false); + int32 getType_int32(DataStruct* data_struct, int32 index = 0, bool force = false); + int64 getType_int64(DataStruct* data_struct, int32 index = 0, bool force = false); + sint8 getType_sint8(DataStruct* data_struct, int32 index = 0, bool force = false); + sint16 getType_sint16(DataStruct* data_struct, int32 index = 0, bool force = false); + sint32 getType_sint32(DataStruct* data_struct, int32 index = 0, bool force = false); + sint64 getType_sint64(DataStruct* data_struct, int32 index = 0, bool force = false); + float getType_float(DataStruct* data_struct, int32 index = 0, bool force = false); + double getType_double(DataStruct* data_struct, int32 index = 0, bool force = false); + char getType_char(DataStruct* data_struct, int32 index = 0, bool force = false); + EQ2_8BitString getType_EQ2_8BitString(DataStruct* data_struct, int32 index = 0, bool force = false); + EQ2_16BitString getType_EQ2_16BitString(DataStruct* data_struct, int32 index = 0, bool force = false); + EQ2_32BitString getType_EQ2_32BitString(DataStruct* data_struct, int32 index = 0, bool force = false); + EQ2_Color getType_EQ2_Color(DataStruct* data_struct, int32 index = 0, bool force = false); + EQ2_EquipmentItem getType_EQ2_EquipmentItem(DataStruct* data_struct, int32 index = 0, bool force = false); + + void setDataType(DataStruct* data_struct, char data, int32 index); + void setDataType(DataStruct* data_struct, int8 data, int32 index); + void setDataType(DataStruct* data_struct, int16 data, int32 index); + void setDataType(DataStruct* data_struct, int32 data, int32 index); + void setDataType(DataStruct* data_struct, int64 data, int32 index); + void setDataType(DataStruct* data_struct, sint8 data, int32 index); + void setDataType(DataStruct* data_struct, sint16 data, int32 index); + void setDataType(DataStruct* data_struct, sint32 data, int32 index); + void setDataType(DataStruct* data_struct, sint64 data, int32 index); + void setDataType(DataStruct* data_struct, float data, int32 index); + void setDataType(DataStruct* data_struct, double data, int32 index); + void setData(DataStruct* data_struct, EQ2_8BitString* input_string, int32 index, bool use_second_type = false); + void setData(DataStruct* data_struct, EQ2_16BitString* input_string, int32 index, bool use_second_type = false); + void setData(DataStruct* data_struct, EQ2_32BitString* input_string, int32 index, bool use_second_type = false); + + template void setData(DataStruct* data_struct, Data* data, int32 index, bool use_second_type = false) { + if (!data_struct) + return; + data_struct->SetIsOptional(false); + int8 type_to_use = (use_second_type) ? data_struct->GetType2() : data_struct->GetType(); + if (type_to_use >= DATA_STRUCT_EQ2_8BIT_STRING && type_to_use <= DATA_STRUCT_EQ2_32BIT_STRING) { + if (type_to_use == DATA_STRUCT_EQ2_8BIT_STRING) { + setSmallString(data_struct, data, index); + } + else if (type_to_use == DATA_STRUCT_EQ2_16BIT_STRING) { + setMediumString(data_struct, data, index); + } + else { + setLargeString(data_struct, data, index); + } + } + else { + if (data_struct && index == 0 && data_struct->GetLength() > 1) { + if (type_to_use == DATA_STRUCT_CHAR) { + for (int32 i = 0; data && i < data_struct->GetLength() && i < strlen(data); i++) + setData(data_struct, data[i], i); + } + else { + for (int32 i = 0; i < data_struct->GetLength(); i++) + setData(data_struct, data[i], i); + } + } + else + setData(data_struct, *data, index); + } + } + template void setData(DataStruct* data_struct, Data data, int32 index, bool use_second_type = false) { + if (data_struct && index < data_struct->GetLength()) { + data_struct->SetIsOptional(false); + int8 type_to_use = (use_second_type) ? data_struct->GetType2() : data_struct->GetType(); + if (use_second_type) { + // Need to figure out why type2 always seems to be 205 + // since only items use type2 for now just hardcoded the value needed (BAD!!!) + //type_to_use = DATA_STRUCT_SINT16; // 9; + data_struct->SetType(type_to_use); + } + switch (type_to_use) { + case DATA_STRUCT_INT8: + setDataType(data_struct, (int8)data, index); + break; + case DATA_STRUCT_INT16: + setDataType(data_struct, (int16)data, index); + break; + case DATA_STRUCT_INT32: + setDataType(data_struct, (int32)data, index); + break; + case DATA_STRUCT_INT64: + setDataType(data_struct, (int64)data, index); + break; + case DATA_STRUCT_SINT8: + setDataType(data_struct, (sint8)data, index); + break; + case DATA_STRUCT_SINT16: + setDataType(data_struct, (sint16)data, index); + break; + case DATA_STRUCT_SINT32: + setDataType(data_struct, (sint32)data, index); + break; + case DATA_STRUCT_SINT64: + setDataType(data_struct, (sint64)data, index); + break; + case DATA_STRUCT_CHAR: + setDataType(data_struct, (char)data, index); + break; + case DATA_STRUCT_FLOAT: + setDataType(data_struct, (float)data, index); + break; + case DATA_STRUCT_DOUBLE: + setDataType(data_struct, (double)data, index); + break; + case DATA_STRUCT_COLOR: + setColor(data_struct, *((EQ2_Color*)&data), index); + break; + case DATA_STRUCT_EQUIPMENT: + setEquipmentByName(data_struct, *((EQ2_EquipmentItem*)&data), index); + break; + case DATA_STRUCT_ITEM: + break; + } + } + } + + template void setSubArrayLengthByName(const char* name, Data data, int32 index1 = 0, int32 index2 = 0) { + char tmp[10] = { 0 }; + sprintf(tmp, "_%i", index1); + string name2 = string(name).append(tmp); + DataStruct* data_struct = findStruct(name2.c_str(), index2); + setData(data_struct, data, index2); + UpdateArrayByArrayLength(data_struct, index2, data); + } + template void setArrayLengthByName(const char* name, Data data, int32 index = 0) { + DataStruct* data_struct = findStruct(name, index); + setData(data_struct, data, index); + UpdateArrayByArrayLength(data_struct, index, data); + } + template void setSubstructArrayLengthByName(const char* substruct_name, const char* name, Data data, int32 substruct_index = 0, int32 index = 0) { + char tmp[10] = { 0 }; + sprintf(tmp, "_%i", substruct_index); + string name2 = string(substruct_name).append("_").append(name).append(tmp); + + DataStruct* data_struct = findStruct(name2.c_str(), index); + setData(data_struct, data, index); + UpdateArrayByArrayLength(data_struct, index, data); + } + void UpdateArrayByArrayLengthName(const char* name, int32 index, int32 size); + void UpdateArrayByArrayLength(DataStruct* data_struct, int32 index, int32 size); + bool StructLoadData(DataStruct* data_struct, void* data, int32 len, bool useType2 = false, bool create_color = false); + bool LoadPacketData(uchar* data, int32 data_len, bool create_color = false); + bool CheckFlagExists(const char* name); + + void setColorByName(const char* name, EQ2_Color* data, int32 index = 0) { + if (data) + setColorByName(name, data->red, data->green, data->blue, index); + } + void setColorByName(const char* name, EQ2_Color data, int32 index = 0) { + setColorByName(name, data.red, data.green, data.blue, index); + } + void setColor(DataStruct* data_struct, EQ2_Color data, int32 index = 0) { + if (data_struct) { + EQ2_Color* ptr = (EQ2_Color*)struct_data[data_struct]; + ptr[index] = data; + } + } + void setColorByName(const char* name, int8 red, int8 green, int8 blue, int32 index = 0) { + setColor(findStruct(name, index), red, green, blue, index); + } + void setColor(DataStruct* data, int8 red, int8 green, int8 blue, int32 index); + void setEquipmentByName(DataStruct* data_struct, EQ2_EquipmentItem data, int32 index = 0) { + if (data_struct) { + EQ2_EquipmentItem* ptr = (EQ2_EquipmentItem*)struct_data[data_struct]; + ptr[index] = data; + } + } +#ifdef WORLD + void setItem(DataStruct* ds, Item* item, Player* player, int32 index, sint8 offset = 0, bool loot_item = false, bool make_empty_item_packet = false, bool inspect = false); + void setItemByName(const char* name, Item* item, Player* player, int32 index = 0, sint8 offset = 0, bool loot_item = false, bool make_empty_item_packet = false, bool inspect = false); + void setItemArrayDataByName(const char* name, Item* item, Player* player, int32 index1 = 0, int32 index2 = 0, sint8 offset = 0, bool loot_item = false, bool make_empty_item_packet = false, bool inspect = false); +#endif + void setEquipmentByName(const char* name, EQ2_EquipmentItem data, int32 index = 0) { + setEquipmentByName(findStruct(name, index), data, index); + } + void setEquipmentByName(const char* name, EQ2_EquipmentItem* data, int32 size) { + DataStruct* data_struct = findStruct(name, 0); + if (data_struct) { + for (int32 i = 0; i < size; i++) + setEquipmentByName(data_struct, data[i], i); + } + } + void setEquipmentByName(const char* name, int32 type, int8 c_red, int8 c_blue, int8 c_green, int8 h_red, int8 h_blue, int8 h_green, int32 index = 0) { + setEquipment(findStruct(name, index), type, c_red, c_blue, c_green, h_red, h_blue, h_green, index); + } + void setEquipment(DataStruct* data, int16 type, int8 c_red, int8 c_blue, int8 c_green, int8 h_red, int8 h_blue, int8 h_green, int32 index); + void remove(DataStruct* data); + vector* getStructs() { return &structs; } + DataStruct* findStruct(const char* name, int32 index); + DataStruct* findStruct(const char* name, int32 index1, int32 index2); + void remove(const char* name); + void remove(int32 position); + void serializePacket(bool clear = true); + + void AddSerializedData(DataStruct* data, int32 index = 0, string* datastring = 0); + EQ2Packet* serialize(); + EQ2Packet* serializeCountPacket(int16 version, int8 offset = 0, uchar* orig_packet = 0, uchar* xor_packet = 0); + string* serializeString(); + int32 GetVersion() { return version; } + void SetVersion(int32 in_version) { version = in_version; } + bool SetOpcode(const char* new_opcode); + EmuOpcode GetOpcode() { return opcode; } + int16 GetOpcodeValue(int16 client_version); + const char* GetName() { return name.c_str(); } + void SetName(const char* in_name) { name = string(in_name); } + bool LoadedSuccessfully() { return loadedSuccessfully; } + bool IsStringValueType(string in_name, int32 index); + bool IsColorValueType(string in_name, int32 index); + int32 GetTotalPacketSize(); + PacketStruct* GetPacketStructByName(const char* name); + void* GetStructPointer(DataStruct* data_struct, bool erase = false); + void PrintPacket(); + string GetSQLQuery(const char* table_name); + vector GetDataStructs(); + void AddPackedData(); + void ResetData(); + void AddFlag(const char* name); + +private: + PacketStruct* parent; + int32 sub_packet_size; + string opcode_type; + bool sub_packet; + bool loadedSuccessfully; + string name; + EmuOpcode opcode; + int16 version; + int16 client_version; + vector arrays; + vector flags; + map struct_data; + map packed_data; + map struct_map; + vector structs; + vector orig_structs; + vector orig_packets; +}; +#endif \ No newline at end of file diff --git a/old/RC4.cpp b/old/RC4.cpp new file mode 100644 index 0000000..15d9f78 --- /dev/null +++ b/old/RC4.cpp @@ -0,0 +1,93 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#include "RC4.h" +#include + +static bool g_bInitStateInitialized = false; +static uchar g_byInitState[256]; + +RC4::RC4(int64 nKey) +{ + if( !g_bInitStateInitialized ) + { + for(int16 i = 0; i < 256; i++ ) + g_byInitState[i] = i; + } + Init(nKey); +} + +RC4::~RC4() +{ +} + +void RC4::Init(int64 nKey) +{ + memcpy(m_state, g_byInitState, 256); + m_x = 0; + m_y = 0; + + ulong dwKeyIndex = 0; + ulong dwStateIndex = 0; + uchar* pKey = (uchar*)&nKey; + for(int16 i = 0; i < 256; i++ ) + { + ulong dwTemp = m_state[i]; + dwStateIndex += pKey[dwKeyIndex] + dwTemp; + dwStateIndex &= 0xFF; + m_state[i] = m_state[dwStateIndex]; + m_state[dwStateIndex] = (uchar)dwTemp; + dwKeyIndex++; + dwKeyIndex &= 7; + } +} + +// A = m_state[X + 1] +// B = m_state[Y + A] +// C ^= m_state[(A + B)] + +// X = 20 +// Y = ? +// C = 0 +// m_state[(A + B)] = Cypher Byte + +void RC4::Cypher(uchar* pBuffer, int32 nLength) +{ + int32 nOffset = 0; + uchar byKey1 = m_x; + uchar byKey2 = m_y; + if( nLength > 0 ) + { + do + { + byKey1++; + uchar byKeyVal1 = m_state[byKey1]; + + byKey2 += byKeyVal1; + uchar byKeyVal2 = m_state[byKey2]; + + m_state[byKey1] = byKeyVal2; + m_state[byKey2] = byKeyVal1; + + pBuffer[nOffset++] ^= m_state[(byKeyVal1 + byKeyVal2) & 0xFF]; + } while( nOffset < nLength ); + } + m_x = byKey1; + m_y = byKey2; +} diff --git a/old/RC4.h b/old/RC4.h new file mode 100644 index 0000000..52a3ea9 --- /dev/null +++ b/old/RC4.h @@ -0,0 +1,38 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#ifndef _EQ2_RC4_H +#define _EQ2_RC4_H +#include "../common/types.h" +class RC4 +{ +public: + RC4(int64 nKey); + ~RC4(); + + void Init(int64 nKey); + void Cypher(uchar* pData, int32 nLen); + +private: + uchar m_state[256]; + uchar m_x; + uchar m_y; +}; +#endif + diff --git a/old/TCPConnection.cpp b/old/TCPConnection.cpp new file mode 100644 index 0000000..81493b2 --- /dev/null +++ b/old/TCPConnection.cpp @@ -0,0 +1,1729 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#include "../common/debug.h" + +#include +using namespace std; +#include +#include +#include +using namespace std; + +#include "TCPConnection.h" +#include "../common/servertalk.h" +#include "../common/timer.h" +#include "../common/packet_dump.h" +#include "Log.h" + +#ifdef FREEBSD //Timothy Whitman - January 7, 2003 +#define MSG_NOSIGNAL 0 +#endif + +#ifdef WIN32 +InitWinsock winsock; +#endif + +#define LOOP_GRANULARITY 3 //# of ms between checking our socket/queues +#define SERVER_LOOP_GRANULARITY 3 //# of ms between checking our socket/queues + +#define TCPN_DEBUG 0 +#define TCPN_DEBUG_Console 0 +#define TCPN_DEBUG_Memory 0 +#define TCPN_LOG_PACKETS 0 +#define TCPN_LOG_RAW_DATA_OUT 0 +#define TCPN_LOG_RAW_DATA_IN 0 + +TCPConnection::TCPNetPacket_Struct* TCPConnection::MakePacket(ServerPacket* pack, int32 iDestination) { + sint32 size = sizeof(TCPNetPacket_Struct) + pack->size; + if (pack->compressed) { + size += 4; + } + if (iDestination) { + size += 4; + } + TCPNetPacket_Struct* tnps = (TCPNetPacket_Struct*) new uchar[size]; + tnps->size = size; + tnps->opcode = pack->opcode; + *((int8*) &tnps->flags) = 0; + uchar* buffer = tnps->buffer; + if (pack->compressed) { + tnps->flags.compressed = 1; + *((sint32*) buffer) = pack->InflatedSize; + buffer += 4; + } + if (iDestination) { + tnps->flags.destination = 1; + *((sint32*) buffer) = iDestination; + buffer += 4; + } + memcpy(buffer, pack->pBuffer, pack->size); + return tnps; +} + +TCPConnection::TCPConnection(bool iOldFormat, TCPServer* iRelayServer, eTCPMode iMode) { + id = 0; + Server = iRelayServer; + if (Server) + RelayServer = true; + else + RelayServer = false; + RelayLink = 0; + RelayCount = 0; + RemoteID = 0; + pOldFormat = iOldFormat; + ConnectionType = Outgoing; + TCPMode = iMode; + pState = TCPS_Ready; + pFree = false; + pEcho = false; + sock = 0; + rIP = 0; + rPort = 0; + keepalive_timer = new Timer(SERVER_TIMEOUT); + timeout_timer = new Timer(SERVER_TIMEOUT * 2); + recvbuf = 0; + sendbuf = 0; + pRunLoop = false; + charAsyncConnect = 0; + pAsyncConnect = false; + connection_socket = 0; + recvbuf_size = 0; + recvbuf_used = 0; + recvbuf_echo = 0; + sendbuf_size = 0; + sendbuf_used = 0; +#if TCPN_DEBUG_Memory >= 7 + cout << "Constructor #1 on outgoing TCP# " << GetID() << endl; +#endif +} + +TCPConnection::TCPConnection(TCPServer* iServer, SOCKET in_socket, int32 irIP, int16 irPort, bool iOldFormat) { + Server = iServer; + RelayLink = 0; + RelayServer = false; + RelayCount = 0; + RemoteID = 0; + id = Server->GetNextID(); + ConnectionType = Incomming; + pOldFormat = iOldFormat; + TCPMode = modePacket; + pState = TCPS_Connected; + pFree = false; + pEcho = false; + sock = 0; + connection_socket = in_socket; + rIP = irIP; + rPort = irPort; + keepalive_timer = new Timer(SERVER_TIMEOUT); + timeout_timer = new Timer(SERVER_TIMEOUT * 2); + recvbuf = 0; + sendbuf = 0; + pRunLoop = false; + charAsyncConnect = 0; + pAsyncConnect = false; + recvbuf_size = 0; + recvbuf_used = 0; + recvbuf_echo = 0; + sendbuf_size = 0; + sendbuf_used = 0; +#if TCPN_DEBUG_Memory >= 7 + cout << "Constructor #2 on outgoing TCP# " << GetID() << endl; +#endif +} + +TCPConnection::TCPConnection(TCPServer* iServer, TCPConnection* iRelayLink, int32 iRemoteID, int32 irIP, int16 irPort) { + Server = iServer; + RelayLink = iRelayLink; + RelayServer = true; + id = Server->GetNextID(); + RelayCount = 0; + RemoteID = iRemoteID; + if (!RemoteID) + ThrowError("Error: TCPConnection: RemoteID == 0 on RelayLink constructor"); + pOldFormat = false; + ConnectionType = Incomming; + TCPMode = modePacket; + pState = TCPS_Connected; + pFree = false; + pEcho = false; + sock = 0; + connection_socket = 0; + rIP = irIP; + rPort = irPort; + keepalive_timer = 0; + timeout_timer = 0; + recvbuf = 0; + sendbuf = 0; + pRunLoop = false; + charAsyncConnect = 0; + pAsyncConnect = false; + recvbuf_size = 0; + recvbuf_used = 0; + recvbuf_echo = 0; + sendbuf_size = 0; + sendbuf_used = 0; +#if TCPN_DEBUG_Memory >= 7 + cout << "Constructor #3 on outgoing TCP# " << GetID() << endl; +#endif +} + +TCPConnection::~TCPConnection() { + Disconnect(); + ClearBuffers(); + if (ConnectionType == Outgoing) { + MRunLoop.lock(); + pRunLoop = false; + MRunLoop.unlock(); + MLoopRunning.lock(); + MLoopRunning.unlock(); +#if TCPN_DEBUG_Memory >= 6 + cout << "Deconstructor on outgoing TCP# " << GetID() << endl; +#endif + } +#if TCPN_DEBUG_Memory >= 5 + else { + cout << "Deconstructor on incomming TCP# " << GetID() << endl; + } +#endif + safe_delete(keepalive_timer); + safe_delete(timeout_timer); + safe_delete_array(recvbuf); + safe_delete_array(sendbuf); + safe_delete_array(charAsyncConnect); +} + +void TCPConnection::SetState(int8 in_state) { + MState.lock(); + pState = in_state; + MState.unlock(); +} + +int8 TCPConnection::GetState() { + int8 ret; + MState.lock(); + ret = pState; + MState.unlock(); + return ret; +} + +void TCPConnection::Free() { + if (ConnectionType == Outgoing) { + ThrowError("TCPConnection::Free() called on an Outgoing connection"); + } +#if TCPN_DEBUG_Memory >= 5 + cout << "Free on TCP# " << GetID() << endl; +#endif + Disconnect(); + pFree = true; +} + +bool TCPConnection::SendPacket(ServerPacket* pack, int32 iDestination) { + LockMutex lock(&MState); + if (!Connected()) + return false; + eTCPMode tmp = GetMode(); + if (tmp != modePacket && tmp != modeTransition) + return false; + if (RemoteID) + return RelayLink->SendPacket(pack, RemoteID); + else { + TCPNetPacket_Struct* tnps = MakePacket(pack, iDestination); + if (tmp == modeTransition) { + InModeQueuePush(tnps); + } + else { +#if TCPN_LOG_PACKETS >= 1 + if (pack && pack->opcode != 0) { + struct in_addr in; + in.s_addr = GetrIP(); + CoutTimestamp(true); + cout << ": Logging outgoing TCP packet. OPCode: 0x" << hex << setw(4) << setfill('0') << pack->opcode << dec << ", size: " << setw(5) << setfill(' ') << pack->size << " " << inet_ntoa(in) << ":" << GetrPort() << endl; +#if TCPN_LOG_PACKETS == 2 + if (pack->size >= 32) + DumpPacket(pack->pBuffer, 32); + else + DumpPacket(pack); +#endif +#if TCPN_LOG_PACKETS >= 3 + DumpPacket(pack); +#endif + } +#endif + ServerSendQueuePushEnd((uchar**) &tnps, tnps->size); + } + } + return true; +} + +bool TCPConnection::SendPacket(TCPNetPacket_Struct* tnps) { + LockMutex lock(&MState); + if (RemoteID) + return false; + if (!Connected()) + return false; + eTCPMode tmp = GetMode(); + if (tmp == modeTransition) { + TCPNetPacket_Struct* tnps2 = (TCPNetPacket_Struct*) new uchar[tnps->size]; + memcpy(tnps2, tnps, tnps->size); + InModeQueuePush(tnps2); + return true; + } + if (GetMode() != modePacket) + return false; +#if TCPN_LOG_PACKETS >= 1 + if (tnps && tnps->opcode != 0) { + struct in_addr in; + in.s_addr = GetrIP(); + CoutTimestamp(true); + cout << ": Logging outgoing TCP NetPacket. OPCode: 0x" << hex << setw(4) << setfill('0') << tnps->opcode << dec << ", size: " << setw(5) << setfill(' ') << tnps->size << " " << inet_ntoa(in) << ":" << GetrPort(); + if (pOldFormat) + cout << " (OldFormat)"; + cout << endl; +#if TCPN_LOG_PACKETS == 2 + if (tnps->size >= 32) + DumpPacket((uchar*) tnps, 32); + else + DumpPacket((uchar*) tnps, tnps->size); +#endif +#if TCPN_LOG_PACKETS >= 3 + DumpPacket((uchar*) tnps, tnps->size); +#endif + } +#endif + ServerSendQueuePushEnd((const uchar*) tnps, tnps->size); + return true; +} + +bool TCPConnection::Send(const uchar* data, sint32 size) { + if (!Connected()) + return false; + if (GetMode() != modeConsole) + return false; + if (!size) + return true; + ServerSendQueuePushEnd(data, size); + return true; +} + +void TCPConnection::InModeQueuePush(TCPNetPacket_Struct* tnps) { + MSendQueue.lock(); + InModeQueue.push(tnps); + MSendQueue.unlock(); +} + +void TCPConnection::ServerSendQueuePushEnd(const uchar* data, sint32 size) { + MSendQueue.lock(); + if (sendbuf == 0) { + sendbuf = new uchar[size]; + sendbuf_size = size; + sendbuf_used = 0; + } + else if (size > (sendbuf_size - sendbuf_used)) { + sendbuf_size += size + 1024; + uchar* tmp = new uchar[sendbuf_size]; + memcpy(tmp, sendbuf, sendbuf_used); + safe_delete_array(sendbuf); + sendbuf = tmp; + } + memcpy(&sendbuf[sendbuf_used], data, size); + sendbuf_used += size; + MSendQueue.unlock(); +} + +void TCPConnection::ServerSendQueuePushEnd(uchar** data, sint32 size) { + MSendQueue.lock(); + if (sendbuf == 0) { + sendbuf = *data; + sendbuf_size = size; + sendbuf_used = size; + MSendQueue.unlock(); + *data = 0; + return; + } + if (size > (sendbuf_size - sendbuf_used)) { + sendbuf_size += size; + uchar* tmp = new uchar[sendbuf_size]; + memcpy(tmp, sendbuf, sendbuf_used); + safe_delete_array(sendbuf); + sendbuf = tmp; + } + memcpy(&sendbuf[sendbuf_used], *data, size); + sendbuf_used += size; + MSendQueue.unlock(); + delete[] (TCPNetPacket_Struct*)*data; +} + +void TCPConnection::ServerSendQueuePushFront(uchar* data, sint32 size) { + MSendQueue.lock(); + if (sendbuf == 0) { + sendbuf = new uchar[size]; + sendbuf_size = size; + sendbuf_used = 0; + } + else if (size > (sendbuf_size - sendbuf_used)) { + sendbuf_size += size; + uchar* tmp = new uchar[sendbuf_size]; + memcpy(&tmp[size], sendbuf, sendbuf_used); + safe_delete_array(sendbuf); + sendbuf = tmp; + } + memcpy(sendbuf, data, size); + sendbuf_used += size; + MSendQueue.unlock(); +} + +bool TCPConnection::ServerSendQueuePop(uchar** data, sint32* size) { + bool ret; + if (!MSendQueue.trylock()) + return false; + if (sendbuf) { + *data = sendbuf; + *size = sendbuf_used; + sendbuf = 0; + ret = true; + } + else { + ret = false; + } + MSendQueue.unlock(); + return ret; +} + +ServerPacket* TCPConnection::PopPacket() { + ServerPacket* ret; + if (!MOutQueueLock.trylock()) + return 0; + ret = OutQueue.pop(); + MOutQueueLock.unlock(); + return ret; +} + +char* TCPConnection::PopLine() { + char* ret; + if (!MOutQueueLock.trylock()) + return 0; + ret = (char*) LineOutQueue.pop(); + MOutQueueLock.unlock(); + return ret; +} + +void TCPConnection::OutQueuePush(ServerPacket* pack) { + MOutQueueLock.lock(); + OutQueue.push(pack); + MOutQueueLock.unlock(); +} + +void TCPConnection::LineOutQueuePush(char* line) { +#if defined(GOTFRAGS) && 0 + if (strcmp(line, "**CRASHME**") == 0) { + int i = 0; + cout << (5 / i) << endl; + } +#endif + if (strcmp(line, "**PACKETMODE**") == 0) { + MSendQueue.lock(); + safe_delete_array(sendbuf); + if (TCPMode == modeConsole) + Send((const uchar*) "\0**PACKETMODE**\r", 16); + TCPMode = modePacket; + TCPNetPacket_Struct* tnps = 0; + while ((tnps = InModeQueue.pop())) { + SendPacket(tnps); + safe_delete_array(tnps); + } + MSendQueue.unlock(); + safe_delete_array(line); + return; + } + MOutQueueLock.lock(); + LineOutQueue.push(line); + MOutQueueLock.unlock(); +} + +void TCPConnection::Disconnect(bool iSendRelayDisconnect) { + if (connection_socket != INVALID_SOCKET && connection_socket != 0) { + MState.lock(); + if (pState == TCPS_Connected || pState == TCPS_Disconnecting || pState == TCPS_Disconnected) + SendData(); + pState = TCPS_Closing; + MState.unlock(); + shutdown(connection_socket, 0x01); + shutdown(connection_socket, 0x00); +#ifdef WIN32 + closesocket(connection_socket); +#else + close(connection_socket); +#endif + connection_socket = 0; + rIP = 0; + rPort = 0; + ClearBuffers(); + } + SetState(TCPS_Ready); + if (RelayLink) { + RelayLink->RemoveRelay(this, iSendRelayDisconnect); + RelayLink = 0; + } +} + +bool TCPConnection::GetAsyncConnect() { + bool ret; + MAsyncConnect.lock(); + ret = pAsyncConnect; + MAsyncConnect.unlock(); + return ret; +} + +bool TCPConnection::SetAsyncConnect(bool iValue) { + bool ret; + MAsyncConnect.lock(); + ret = pAsyncConnect; + pAsyncConnect = iValue; + MAsyncConnect.unlock(); + return ret; +} + +void TCPConnection::AsyncConnect(char* irAddress, int16 irPort) { + if (ConnectionType != Outgoing) { + // If this code runs, we got serious problems + // Crash and burn. + ThrowError("TCPConnection::AsyncConnect() call on a Incomming connection object!"); + return; + } + if (GetState() != TCPS_Ready) + return; + MAsyncConnect.lock(); + if (pAsyncConnect) { + MAsyncConnect.unlock(); + return; + } + pAsyncConnect = true; + safe_delete_array(charAsyncConnect); + charAsyncConnect = new char[strlen(irAddress) + 1]; + strcpy(charAsyncConnect, irAddress); + rPort = irPort; + MAsyncConnect.unlock(); + if (!pRunLoop) { + pRunLoop = true; +#ifdef WIN32 + _beginthread(TCPConnectionLoop, 0, this); +#else + pthread_t thread; + pthread_create(&thread, NULL, TCPConnectionLoop, this); + pthread_detach(thread); +#endif + } + return; +} + +void TCPConnection::AsyncConnect(int32 irIP, int16 irPort) { + if (ConnectionType != Outgoing) { + // If this code runs, we got serious problems + // Crash and burn. + ThrowError("TCPConnection::AsyncConnect() call on a Incomming connection object!"); + return; + } + if (GetState() != TCPS_Ready) + return; + MAsyncConnect.lock(); + if (pAsyncConnect) { + MAsyncConnect.unlock(); + return; + } + pAsyncConnect = true; + safe_delete(charAsyncConnect); + rIP = irIP; + rPort = irPort; + MAsyncConnect.unlock(); + if (!pRunLoop) { + pRunLoop = true; +#ifdef WIN32 + _beginthread(TCPConnectionLoop, 0, this); +#else + pthread_t thread; + pthread_create(&thread, NULL, TCPConnectionLoop, this); + pthread_detach(thread); +#endif + } + return; +} + +bool TCPConnection::Connect(char* irAddress, int16 irPort, char* errbuf) { + if (errbuf) + errbuf[0] = 0; + int32 tmpIP = ResolveIP(irAddress); + if (!tmpIP) { + if (errbuf) { +#ifdef WIN32 + snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::Connect(): Couldnt resolve hostname. Error: %i", WSAGetLastError()); +#else + snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::Connect(): Couldnt resolve hostname. Error #%i: %s", errno, strerror(errno)); +#endif + } + return false; + } + return Connect(tmpIP, irPort, errbuf); +} + +bool TCPConnection::Connect(int32 in_ip, int16 in_port, char* errbuf) { + if (errbuf) + errbuf[0] = 0; + if (ConnectionType != Outgoing) { + // If this code runs, we got serious problems + // Crash and burn. + ThrowError("TCPConnection::Connect() call on a Incomming connection object!"); + return false; + } + MState.lock(); + if (pState == TCPS_Ready) { + pState = TCPS_Connecting; + } + else { + MState.unlock(); + SetAsyncConnect(false); + return false; + } + MState.unlock(); + if (!pRunLoop) { + pRunLoop = true; +#ifdef WIN32 + _beginthread(TCPConnectionLoop, 0, this); +#else + pthread_t thread; + pthread_create(&thread, NULL, TCPConnectionLoop, this); + pthread_detach(thread); +#endif + } + + connection_socket = INVALID_SOCKET; + struct sockaddr_in server_sin; + // struct in_addr in; + + if ((connection_socket = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET || connection_socket == 0) { +#ifdef WIN32 + if (errbuf) + snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::Connect(): Allocating socket failed. Error: %i", WSAGetLastError()); +#else + if (errbuf) + snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::Connect(): Allocating socket failed. Error: %s", strerror(errno)); +#endif + SetState(TCPS_Ready); + SetAsyncConnect(false); + return false; + } + server_sin.sin_family = AF_INET; + server_sin.sin_addr.s_addr = in_ip; + server_sin.sin_port = htons(in_port); + + // Establish a connection to the server socket. +#ifdef WIN32 + if (connect(connection_socket, (PSOCKADDR) &server_sin, sizeof (server_sin)) == SOCKET_ERROR) { + if (errbuf) + snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::Connect(): connect() failed. Error: %i", WSAGetLastError()); + closesocket(connection_socket); + connection_socket = 0; + SetState(TCPS_Ready); + SetAsyncConnect(false); + return false; + } +#else + if (connect(connection_socket, (struct sockaddr *) &server_sin, sizeof (server_sin)) == SOCKET_ERROR) { + if (errbuf) + snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::Connect(): connect() failed. Error: %s", strerror(errno)); + close(connection_socket); + connection_socket = 0; + SetState(TCPS_Ready); + SetAsyncConnect(false); + return false; + } +#endif + int bufsize = 64 * 1024; // 64kbyte recieve buffer, up from default of 8k + setsockopt(connection_socket, SOL_SOCKET, SO_RCVBUF, (char*) &bufsize, sizeof(bufsize)); +#ifdef WIN32 + unsigned long nonblocking = 1; + ioctlsocket(connection_socket, FIONBIO, &nonblocking); +#else + fcntl(connection_socket, F_SETFL, O_NONBLOCK); +#endif + + SetEcho(false); + MSendQueue.lock(); + ClearBuffers(); + TCPMode = modePacket; + + MSendQueue.unlock(); + + rIP = in_ip; + rPort = in_port; + SetState(TCPS_Connected); + SetAsyncConnect(false); + return true; +} + +void TCPConnection::ClearBuffers() { + LockMutex lock1(&MSendQueue); + LockMutex lock2(&MOutQueueLock); + LockMutex lock3(&MRunLoop); + LockMutex lock4(&MState); + safe_delete_array(recvbuf); + safe_delete_array(sendbuf); + ServerPacket* pack = 0; + while ((pack = PopPacket())) + safe_delete(pack); + TCPNetPacket_Struct* tnps = 0; + while ((tnps = InModeQueue.pop())) + safe_delete(tnps); + char* line = 0; + while ((line = LineOutQueue.pop())) + safe_delete_array(line); + keepalive_timer->Start(); + timeout_timer->Start(); +} + +bool TCPConnection::CheckNetActive() { + MState.lock(); + if (pState == TCPS_Connected || pState == TCPS_Disconnecting) { + MState.unlock(); + return true; + } + MState.unlock(); + return false; +} + +bool TCPConnection::Process() { + char errbuf[TCPConnection_ErrorBufferSize]; + if (!CheckNetActive()) { + if (ConnectionType == Outgoing) { + if (GetAsyncConnect()) { + if (charAsyncConnect) + rIP = ResolveIP(charAsyncConnect); + Connect(rIP, rPort); + } + } + if (GetState() == TCPS_Disconnected) { + Disconnect(); + return false; + } + else if (GetState() == TCPS_Connecting) + return true; + else + return false; + } + if (!SendData(errbuf)) { + struct in_addr in; + in.s_addr = GetrIP(); + cout << inet_ntoa(in) << ":" << GetrPort() << ": " << errbuf << endl; + return false; + } + if (!Connected()) + return false; + if (!RecvData(errbuf)) { + struct in_addr in; + in.s_addr = GetrIP(); + cout << inet_ntoa(in) << ":" << GetrPort() << ": " << errbuf << endl; + return false; + } + return true; +} + +bool TCPConnection::RecvData(char* errbuf) { + if (errbuf) + errbuf[0] = 0; + if (!Connected()) { + return false; + } + + int status = 0; + if (recvbuf == 0) { + recvbuf = new uchar[5120]; + recvbuf_size = 5120; + recvbuf_used = 0; + recvbuf_echo = 0; + } + else if ((recvbuf_size - recvbuf_used) < 2048) { + uchar* tmpbuf = new uchar[recvbuf_size + 5120]; + memcpy(tmpbuf, recvbuf, recvbuf_used); + recvbuf_size += 5120; + safe_delete_array(recvbuf); + recvbuf = tmpbuf; + if (recvbuf_size >= MaxTCPReceiveBufferSize) { + if (errbuf) + snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::RecvData(): recvbuf_size >= MaxTCPReceiveBufferSize"); + return false; + } + } + + status = recv(connection_socket, (char *) &recvbuf[recvbuf_used], (recvbuf_size - recvbuf_used), 0); + if (status >= 1) { +#if TCPN_LOG_RAW_DATA_IN >= 1 + struct in_addr in; + in.s_addr = GetrIP(); + CoutTimestamp(true); + cout << ": Read " << status << " bytes from network. (recvbuf_used = " << recvbuf_used << ") " << inet_ntoa(in) << ":" << GetrPort(); + if (pOldFormat) + cout << " (OldFormat)"; + cout << endl; +#if TCPN_LOG_RAW_DATA_IN == 2 + sint32 tmp = status; + if (tmp > 32) + tmp = 32; + DumpPacket(&recvbuf[recvbuf_used], status); +#elif TCPN_LOG_RAW_DATA_IN >= 3 + DumpPacket(&recvbuf[recvbuf_used], status); +#endif +#endif + recvbuf_used += status; + timeout_timer->Start(); + if (!ProcessReceivedData(errbuf)) + return false; + } + else if (status == SOCKET_ERROR) { +#ifdef WIN32 + if (!(WSAGetLastError() == WSAEWOULDBLOCK)) { + if (errbuf) + snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::RecvData(): Error: %i", WSAGetLastError()); + return false; + } +#else + if (!(errno == EWOULDBLOCK)) { + if (errbuf) + snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::RecvData(): Error: %s", strerror(errno)); + return false; + } +#endif + } + if ((TCPMode == modePacket || TCPMode == modeTransition) && timeout_timer->Check()) { + if (errbuf) + snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::RecvData(): Connection timeout"); + return false; + } + + return true; +} + + +bool TCPConnection::GetEcho() { + bool ret; + MEcho.lock(); + ret = pEcho; + MEcho.unlock(); + return ret; +} + +void TCPConnection::SetEcho(bool iValue) { + MEcho.lock(); + pEcho = iValue; + MEcho.unlock(); +} + +bool TCPConnection::ProcessReceivedData(char* errbuf) { + if (errbuf) + errbuf[0] = 0; + if (!recvbuf) + return true; + if (TCPMode == modePacket) { + //if (pOldFormat) + // return ProcessReceivedDataAsOldPackets(errbuf); + //else + return ProcessReceivedDataAsPackets(errbuf); + } + else { +#if TCPN_DEBUG_Console >= 4 + if (recvbuf_used) { + cout << "Starting Processing: recvbuf=" << recvbuf_used << endl; + DumpPacket(recvbuf, recvbuf_used); + } +#endif + for (int i=0; i < recvbuf_used; i++) { + if (GetEcho() && i >= recvbuf_echo) { + Send(&recvbuf[i], 1); + recvbuf_echo = i + 1; + } + switch(recvbuf[i]) { + case 0: { // 0 is the code for clear buffer + if (i==0) { + recvbuf_used--; + recvbuf_echo--; + memcpy(recvbuf, &recvbuf[1], recvbuf_used); + i = -1; + } else { + if (i == recvbuf_used) { + safe_delete_array(recvbuf); + i = -1; + } + else { + uchar* tmpdel = recvbuf; + recvbuf = new uchar[recvbuf_size]; + memcpy(recvbuf, &tmpdel[i+1], recvbuf_used-i); + recvbuf_used -= i + 1; + recvbuf_echo -= i + 1; + safe_delete(tmpdel); + i = -1; + } + } +#if TCPN_DEBUG_Console >= 5 + cout << "Removed 0x00" << endl; + if (recvbuf_used) { + cout << "recvbuf left: " << recvbuf_used << endl; + DumpPacket(recvbuf, recvbuf_used); + } + else + cout << "recbuf left: None" << endl; +#endif + break; + } + case 10: + case 13: // newline marker + { + if (i==0) { // empty line + recvbuf_used--; + recvbuf_echo--; + memcpy(recvbuf, &recvbuf[1], recvbuf_used); + i = -1; + } else { + char* line = new char[i+1]; + memset(line, 0, i+1); + memcpy(line, recvbuf, i); +#if TCPN_DEBUG_Console >= 3 + cout << "Line Out: " << endl; + DumpPacket((uchar*) line, i); +#endif + //line[i] = 0; + uchar* tmpdel = recvbuf; + recvbuf = new uchar[recvbuf_size]; + recvbuf_used -= i+1; + recvbuf_echo -= i+1; + memcpy(recvbuf, &tmpdel[i+1], recvbuf_used); +#if TCPN_DEBUG_Console >= 5 + cout << "i+1=" << i+1 << endl; + if (recvbuf_used) { + cout << "recvbuf left: " << recvbuf_used << endl; + DumpPacket(recvbuf, recvbuf_used); + } + else + cout << "recbuf left: None" << endl; +#endif + safe_delete(tmpdel); + if (strlen(line) > 0) + LineOutQueuePush(line); + else + safe_delete_array(line); + if (TCPMode == modePacket) { + return ProcessReceivedDataAsPackets(errbuf); + } + i = -1; + } + break; + } + case 8: // backspace + { + if (i==0) { // nothin to backspace + recvbuf_used--; + recvbuf_echo--; + memcpy(recvbuf, &recvbuf[1], recvbuf_used); + i = -1; + } else { + uchar* tmpdel = recvbuf; + recvbuf = new uchar[recvbuf_size]; + memcpy(recvbuf, tmpdel, i-1); + memcpy(&recvbuf[i-1], &tmpdel[i+1], recvbuf_used-i); + recvbuf_used -= 2; + recvbuf_echo -= 2; + safe_delete(tmpdel); + i -= 2; + } + break; + } + } + } + if (recvbuf_used < 0) + safe_delete_array(recvbuf); + } + return true; +} + +bool TCPConnection::ProcessReceivedDataAsPackets(char* errbuf) { + if (errbuf) + errbuf[0] = 0; + sint32 base = 0; + sint32 size = 0; + uchar* buffer; + sint32 sizeReq = sizeof(TCPNetPacket_Struct); + ServerPacket* pack = 0; + while ((recvbuf_used - base) >= size) { + TCPNetPacket_Struct* tnps = (TCPNetPacket_Struct*) &recvbuf[base]; + buffer = tnps->buffer; + size = tnps->size; + + if (size < sizeReq || recvbuf_used < sizeReq || size >= MaxTCPReceiveBufferSize) { +#if TCPN_DEBUG_Memory >= 1 + cout << "TCPConnection[" << GetID() << "]::ProcessReceivedDataAsPackets(): size[" << size << "] >= MaxTCPReceiveBufferSize" << endl; +#endif + if (errbuf) + snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::ProcessReceivedDataAsPackets(): size provided %i, recvbuf_used %i, checks failed: struct_size < %i || recvbuf_used < sizeReq || size >= MaxTCPReceiveBufferSize", size, recvbuf_used, sizeReq); + return false; + } + if ((recvbuf_used - base) >= size) { + // ok, we got enough data to make this packet! + safe_delete(pack); + pack = new ServerPacket; + pack->size = size - sizeof(TCPNetPacket_Struct); + // read headers + pack->opcode = tnps->opcode; + if (tnps->flags.compressed) { + sizeReq += 4; + if(size < sizeReq || recvbuf_used < sizeReq) + { + if (errbuf) + snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::ProcessReceivedDataAsPackets(Flags.Compressed): size provided %i, recvbuf_used %i, checks failed: struct_size < %i || recvbuf_used < sizeReq", size, recvbuf_used, sizeReq); + safe_delete(pack); + return false; + } + pack->compressed = true; + pack->InflatedSize = *((sint32*)buffer); + pack->size -= 4; + buffer += 4; + } + if (tnps->flags.destination) { + sizeReq += 4; + if(size < sizeReq || recvbuf_used < sizeReq) + { + if (errbuf) + snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::ProcessReceivedDataAsPackets(Flags.Destination): size provided %i, recvbuf_used %i, checks failed: struct_size < %i || recvbuf_used < sizeReq", size, recvbuf_used, sizeReq); + safe_delete(pack); + return false; + } + pack->destination = *((sint32*)buffer); + pack->size -= 4; + buffer += 4; + } + // end read headers + if (pack->size > 0) { + if (tnps->flags.compressed) { + // Lets decompress the packet here + pack->compressed = false; + if(pack->InflatedSize < MaxTCPReceiveBufferSize) + { + pack->pBuffer = new uchar[pack->InflatedSize]; + pack->size = InflatePacket(buffer, pack->size, pack->pBuffer, pack->InflatedSize); + if(!pack->size) + { + if (errbuf) + snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::ProcessReceivedDataAsPackets(InflatePacket): size provided %i, recvbuf_used: %i, sizeReq: %i, could not inflate packet", size, recvbuf_used, sizeReq); + + safe_delete(pack); + return false; + } + } + else + { + cout << "Invalid inflated packet." << endl; + safe_delete(pack); + return false; + } + } + else { + pack->pBuffer = new uchar[pack->size]; + memcpy(pack->pBuffer, buffer, pack->size); + } + } + if (pack->opcode == 0) { + if (pack->size) { +#if TCPN_DEBUG >= 2 + cout << "Received TCP Network layer packet" << endl; +#endif + ProcessNetworkLayerPacket(pack); + } +#if TCPN_DEBUG >= 5 + else { + cout << "Received TCP keepalive packet. (opcode=0)" << endl; + } +#endif + } + else { +#if TCPN_LOG_PACKETS >= 1 + if (pack && pack->opcode != 0) { + struct in_addr in; + in.s_addr = GetrIP(); + CoutTimestamp(true); + cout << ": Logging incoming TCP packet. OPCode: 0x" << hex << setw(4) << setfill('0') << pack->opcode << dec << ", size: " << setw(5) << setfill(' ') << pack->size << " " << inet_ntoa(in) << ":" << GetrPort() << endl; +#if TCPN_LOG_PACKETS == 2 + if (pack->size >= 32) + DumpPacket(pack->pBuffer, 32); + else + DumpPacket(pack); +#endif +#if TCPN_LOG_PACKETS >= 3 + DumpPacket(pack); +#endif + } +#endif + if (RelayServer && Server && pack->destination) { + TCPConnection* con = Server->GetConnection(pack->destination); + if (!con) { +#if TCPN_DEBUG >= 1 + cout << "Error relaying packet: con = 0" << endl; +#endif + } + else{ + con->OutQueuePush(pack); + pack = 0; + } + } + else{ + OutQueuePush(pack); + pack = 0; + } + } + base += size; + size = 7; + } + } + safe_delete(pack); + if (base != 0) { + if (base >= recvbuf_used) { + safe_delete_array(recvbuf); + } + else { + uchar* tmpbuf = new uchar[recvbuf_size - base]; + memcpy(tmpbuf, &recvbuf[base], recvbuf_used - base); + safe_delete_array(recvbuf); + recvbuf = tmpbuf; + recvbuf_used -= base; + recvbuf_size -= base; + } + } + return true; +} + +bool TCPConnection::ProcessReceivedDataAsOldPackets(char* errbuf) { + sint32 base = 0; + sint32 size = 4; + uchar* buffer; + ServerPacket* pack = 0; + while ((recvbuf_used - base) >= size) { + buffer = &recvbuf[base]; + memcpy(&size, &buffer[2], 2); + if (size >= MaxTCPReceiveBufferSize) { +#if TCPN_DEBUG_Memory >= 1 + cout << "TCPConnection[" << GetID() << "]::ProcessReceivedDataAsPackets(): size[" << size << "] >= MaxTCPReceiveBufferSize" << endl; +#endif + if (errbuf) + snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::ProcessReceivedDataAsPackets(): size >= MaxTCPReceiveBufferSize"); + return false; + } + if ((recvbuf_used - base) >= size) { + // ok, we got enough data to make this packet! + pack = new ServerPacket; + memcpy(&pack->opcode, &buffer[0], 2); + pack->size = size - 4; + + LogWrite(MISC__TODO, 1, "TODO", "Checksum or size check or something similar\n\t(%s, function: %s, line #: %i)", __FILE__, __FUNCTION__, __LINE__); + /* + if () { // TODO: Checksum or size check or something similar + // Datastream corruption, get the hell outta here! + delete pack; + return false; + } + */ + + if (pack->size > 0) { + pack->pBuffer = new uchar[pack->size]; + memcpy(pack->pBuffer, &buffer[4], pack->size); + } + if (pack->opcode == 0) { + // keepalive, no need to process + safe_delete(pack); + } + else { +#if TCPN_LOG_PACKETS >= 1 + if (pack && pack->opcode != 0) { + struct in_addr in; + in.s_addr = GetrIP(); + CoutTimestamp(true); + cout << ": Logging incoming TCP OldPacket. OPCode: 0x" << hex << setw(4) << setfill('0') << pack->opcode << dec << ", size: " << setw(5) << setfill(' ') << pack->size << " " << inet_ntoa(in) << ":" << GetrPort() << endl; +#if TCPN_LOG_PACKETS == 2 + if (pack->size >= 32) + DumpPacket(pack->pBuffer, 32); + else + DumpPacket(pack); +#endif +#if TCPN_LOG_PACKETS >= 3 + DumpPacket(pack); +#endif + } +#endif + OutQueuePush(pack); + } + base += size; + size = 4; + } + } + if (base != 0) { + if (base >= recvbuf_used) { + safe_delete_array(recvbuf); + } + else { + uchar* tmpbuf = new uchar[recvbuf_size - base]; + memcpy(tmpbuf, &recvbuf[base], recvbuf_used - base); + safe_delete_array(recvbuf); + recvbuf = tmpbuf; + recvbuf_used -= base; + recvbuf_size -= base; + } + } + return true; +} + +void TCPConnection::ProcessNetworkLayerPacket(ServerPacket* pack) { + int8 opcode = pack->pBuffer[0]; + + + /** disabling RELAY capabilities, this functionality is poorly implemented + even if such a feature needs to be re-used need authentication BEFORE allowing relay to take place + secondly we need to protect the LS accepting new connections as bogus data can be passed to open + fake TCP connections + opcode 0 is OK, that is Keep-Alive + **/ + + if (opcode > 0) + { + Disconnect(); + return; + } + + int8* data = &pack->pBuffer[1]; + switch (opcode) { + case 0: { + break; + } + case 1: { // Switch to RelayServer mode + if (pack->size != 1) { + SendNetErrorPacket("New RelayClient: wrong size, expected 1"); + break; + } + if (RelayServer) { + SendNetErrorPacket("Switch to RelayServer mode when already in RelayServer mode"); + break; + } + if (RemoteID) { + SendNetErrorPacket("Switch to RelayServer mode by a Relay Client"); + break; + } + if (ConnectionType != Incomming) { + SendNetErrorPacket("Switch to RelayServer mode on outgoing connection"); + break; + } +#if TCPC_DEBUG >= 3 + struct in_addr in; + in.s_addr = GetrIP(); + cout << "Switching to RelayServer mode: " << inet_ntoa(in) << ":" << GetPort() << endl; +#endif + RelayServer = true; + break; + } + case 2: { // New Relay Client + if (!RelayServer) { + SendNetErrorPacket("New RelayClient when not in RelayServer mode"); + break; + } + if (pack->size != 11) { + SendNetErrorPacket("New RelayClient: wrong size, expected 11"); + break; + } + if (ConnectionType != Incomming) { + SendNetErrorPacket("New RelayClient: illegal on outgoing connection"); + break; + } + TCPConnection* con = new TCPConnection(Server, this, *((int32*) data), *((int32*) &data[4]), *((int16*) &data[8])); + Server->AddConnection(con); + RelayCount++; + break; + } + case 3: { // Delete Relay Client + if (!RelayServer) { + SendNetErrorPacket("Delete RelayClient when not in RelayServer mode"); + break; + } + if (pack->size != 5) { + SendNetErrorPacket("Delete RelayClient: wrong size, expected 5"); + break; + } + TCPConnection* con = Server->GetConnection(*((int32*)data)); + if (con) { + if (ConnectionType == Incomming) { + if (con->GetRelayLink() != this) { + SendNetErrorPacket("Delete RelayClient: RelayLink != this"); + break; + } + } + con->Disconnect(false); + } + break; + } + case 255: { +#if TCPC_DEBUG >= 1 + struct in_addr in; + in.s_addr = GetrIP(); + cout "Received NetError: '"; + if (pack->size > 1) + cout << (char*) data; + cout << "': " << inet_ntoa(in) << ":" << GetPort() << endl; +#endif + break; + } + } +} + +void TCPConnection::SendNetErrorPacket(const char* reason) { +#if TCPC_DEBUG >= 1 + struct in_addr in; + in.s_addr = GetrIP(); + cout "NetError: '"; + if (reason) + cout << reason; + cout << "': " << inet_ntoa(in) << ":" << GetPort() << endl; +#endif + ServerPacket* pack = new ServerPacket(0); + pack->size = 1; + if (reason) + pack->size += strlen(reason) + 1; + pack->pBuffer = new uchar[pack->size]; + memset(pack->pBuffer, 0, pack->size); + pack->pBuffer[0] = 255; + strcpy((char*) &pack->pBuffer[1], reason); + SendPacket(pack); + safe_delete(pack); +} + +void TCPConnection::RemoveRelay(TCPConnection* relay, bool iSendRelayDisconnect) { + if (iSendRelayDisconnect) { + ServerPacket* pack = new ServerPacket(0, 5); + pack->pBuffer[0] = 3; + *((int32*) &pack->pBuffer[1]) = relay->GetRemoteID(); + SendPacket(pack); + safe_delete(pack); + } + RelayCount--; +} + +bool TCPConnection::SendData(char* errbuf) { + if (errbuf) + errbuf[0] = 0; + /************ Get first send packet on queue and send it! ************/ + uchar* data = 0; + sint32 size = 0; + int status = 0; + if (ServerSendQueuePop(&data, &size)) { +#ifdef WIN32 + status = send(connection_socket, (const char *) data, size, 0); +#else + status = send(connection_socket, data, size, MSG_NOSIGNAL); + if(errno==EPIPE) status = SOCKET_ERROR; +#endif + if (status >= 1) { +#if TCPN_LOG_RAW_DATA_OUT >= 1 + struct in_addr in; + in.s_addr = GetrIP(); + CoutTimestamp(true); + cout << ": Wrote " << status << " bytes to network. " << inet_ntoa(in) << ":" << GetrPort(); + if (pOldFormat) + cout << " (OldFormat)"; + cout << endl; +#if TCPN_LOG_RAW_DATA_OUT == 2 + sint32 tmp = status; + if (tmp > 32) + tmp = 32; + DumpPacket(data, status); +#elif TCPN_LOG_RAW_DATA_OUT >= 3 + DumpPacket(data, status); +#endif +#endif + keepalive_timer->Start(); + if (status < (signed)size) { +#if TCPN_LOG_RAW_DATA_OUT >= 1 + struct in_addr in; + in.s_addr = GetrIP(); + CoutTimestamp(true); + cout << ": Pushed " << (size - status) << " bytes back onto the send queue. " << inet_ntoa(in) << ":" << GetrPort(); + if (pOldFormat) + cout << " (OldFormat)"; + cout << endl; +#endif + // If there's network congestion, the number of bytes sent can be less than + // what we tried to give it... Push the extra back on the queue for later + ServerSendQueuePushFront(&data[status], size - status); + } + else if (status > (signed)size) { + ThrowError("TCPConnection::SendData(): WTF! status > size"); + return false; + } + // else if (status == size) {} + } + else { + ServerSendQueuePushFront(data, size); + } + + safe_delete_array(data); + if (status == SOCKET_ERROR) { +#ifdef WIN32 + if (WSAGetLastError() != WSAEWOULDBLOCK) +#else + if (errno != EWOULDBLOCK) +#endif + { + if (errbuf) { +#ifdef WIN32 + snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::SendData(): send(): Errorcode: %i", WSAGetLastError()); +#else + snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::SendData(): send(): Errorcode: %s", strerror(errno)); +#endif + } + return false; + } + } + } + if (TCPMode == modePacket && keepalive_timer->Check()) { + ServerPacket* pack = new ServerPacket(0, 0); + SendPacket(pack); + safe_delete(pack); +#if TCPN_DEBUG >= 5 + cout << "Sending TCP keepalive packet. (timeout=" << timeout_timer->GetRemainingTime() << " remaining)" << endl; +#endif + } + return true; +} + +ThreadReturnType TCPConnectionLoop(void* tmp) { +#ifdef WIN32 + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL); +#endif + if (tmp == 0) { + ThrowError("TCPConnectionLoop(): tmp = 0!"); + THREAD_RETURN(NULL); + } + TCPConnection* tcpc = (TCPConnection*) tmp; + tcpc->MLoopRunning.lock(); + while (tcpc->RunLoop()) { + Sleep(LOOP_GRANULARITY); + if (tcpc->GetState() != TCPS_Ready) { + if (!tcpc->Process()) { + tcpc->Disconnect(); + } + } + else if (tcpc->GetAsyncConnect()) { + if (tcpc->charAsyncConnect) + tcpc->Connect(tcpc->charAsyncConnect, tcpc->GetrPort()); + else + tcpc->Connect(tcpc->GetrIP(), tcpc->GetrPort()); + tcpc->SetAsyncConnect(false); + } + else + Sleep(10); + } + tcpc->MLoopRunning.unlock(); + + THREAD_RETURN(NULL); +} + +bool TCPConnection::RunLoop() { + bool ret; + MRunLoop.lock(); + ret = pRunLoop; + MRunLoop.unlock(); + return ret; +} + + + + + +TCPServer::TCPServer(int16 in_port, bool iOldFormat) { + NextID = 1; + pPort = in_port; + sock = 0; + pOldFormat = iOldFormat; + list = new LinkedList; + pRunLoop = true; +#ifdef WIN32 + _beginthread(TCPServerLoop, 0, this); +#else + pthread_t thread; + pthread_create(&thread, NULL, &TCPServerLoop, this); + pthread_detach(thread); +#endif +} + +TCPServer::~TCPServer() { + MRunLoop.lock(); + pRunLoop = false; + MRunLoop.unlock(); + MLoopRunning.lock(); + MLoopRunning.unlock(); + + while (NewQueue.pop()); // the objects are deleted with the list, clear this queue so it doesnt try to delete them again + safe_delete(list); +} + +bool TCPServer::RunLoop() { + bool ret; + MRunLoop.lock(); + ret = pRunLoop; + MRunLoop.unlock(); + return ret; +} + +ThreadReturnType TCPServerLoop(void* tmp) { +#ifdef WIN32 + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL); +#endif + if (tmp == 0) { + ThrowError("TCPServerLoop(): tmp = 0!"); + THREAD_RETURN(NULL); + } + TCPServer* tcps = (TCPServer*) tmp; + tcps->MLoopRunning.lock(); + while (tcps->RunLoop()) { + Sleep(SERVER_LOOP_GRANULARITY); + tcps->Process(); + } + tcps->MLoopRunning.unlock(); + + THREAD_RETURN(NULL); +} + +void TCPServer::Process() { + CheckInQueue(); + ListenNewConnections(); + LinkedListIterator iterator(*list); + + iterator.Reset(); + while(iterator.MoreElements()) { + if (iterator.GetData()->IsFree() && (!iterator.GetData()->CheckNetActive())) { +#if _DEBUG + LogWrite(NET__DEBUG, 0, "Net", "EQStream Connection deleted."); +#endif + iterator.RemoveCurrent(); + } + else { + if (!iterator.GetData()->Process()) + iterator.GetData()->Disconnect(); + iterator.Advance(); + } + } +} + +void TCPServer::ListenNewConnections() { + SOCKET tmpsock; + struct sockaddr_in from; + struct in_addr in; + unsigned int fromlen; + + TCPConnection* con; + + from.sin_family = AF_INET; + fromlen = sizeof(from); + LockMutex lock(&MSock); + if (!sock) + return; + + // Check for pending connects +#ifdef WIN32 + unsigned long nonblocking = 1; + while ((tmpsock = accept(sock, (struct sockaddr*) &from, (int *) &fromlen)) != INVALID_SOCKET) { + ioctlsocket (tmpsock, FIONBIO, &nonblocking); +#else + while ((tmpsock = accept(sock, (struct sockaddr*) &from, &fromlen)) != INVALID_SOCKET) { + fcntl(tmpsock, F_SETFL, O_NONBLOCK); +#endif + int bufsize = 64 * 1024; // 64kbyte recieve buffer, up from default of 8k + setsockopt(tmpsock, SOL_SOCKET, SO_RCVBUF, (char*) &bufsize, sizeof(bufsize)); + in.s_addr = from.sin_addr.s_addr; + + // New TCP connection + con = new TCPConnection(this, tmpsock, in.s_addr, ntohs(from.sin_port), pOldFormat); +#if TCPN_DEBUG >= 1 + cout << "New TCP connection: " << inet_ntoa(in) << ":" << con->GetrPort() << endl; +#endif + AddConnection(con); + } +} + +bool TCPServer::Open(int16 in_port, char* errbuf) { + if (errbuf) + errbuf[0] = 0; + LockMutex lock(&MSock); + if (sock != 0) { + if (errbuf) + snprintf(errbuf, TCPConnection_ErrorBufferSize, "Listening socket already open"); + return false; + } + if (in_port != 0) { + pPort = in_port; + } + +#ifdef WIN32 + SOCKADDR_IN address; + unsigned long nonblocking = 1; +#else + struct sockaddr_in address; +#endif + int reuse_addr = 1; + + // Setup internet address information. + // This is used with the bind() call + memset((char *) &address, 0, sizeof(address)); + address.sin_family = AF_INET; + address.sin_port = htons(pPort); + address.sin_addr.s_addr = htonl(INADDR_ANY); + + // Setting up TCP port for new TCP connections + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock == INVALID_SOCKET) { + if (errbuf) + snprintf(errbuf, TCPConnection_ErrorBufferSize, "socket(): INVALID_SOCKET"); + return false; + } + + // Quag: dont think following is good stuff for TCP, good for UDP + // Mis: SO_REUSEADDR shouldn't be a problem for tcp--allows you to restart + // without waiting for conns in TIME_WAIT to die + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &reuse_addr, sizeof(reuse_addr)); + + + if (::bind(sock, (struct sockaddr *) &address, sizeof(address)) < 0) { +#ifdef WIN32 + closesocket(sock); +#else + close(sock); +#endif + sock = 0; + if (errbuf) + sprintf(errbuf, "bind(): <0"); + return false; + } + + int bufsize = 64 * 1024; // 64kbyte recieve buffer, up from default of 8k + setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char*) &bufsize, sizeof(bufsize)); +#ifdef WIN32 + ioctlsocket (sock, FIONBIO, &nonblocking); +#else + fcntl(sock, F_SETFL, O_NONBLOCK); +#endif + + if (listen(sock, SOMAXCONN) == SOCKET_ERROR) { +#ifdef WIN32 + closesocket(sock); + if (errbuf) + snprintf(errbuf, TCPConnection_ErrorBufferSize, "listen() failed, Error: %d", WSAGetLastError()); +#else + close(sock); + if (errbuf) + snprintf(errbuf, TCPConnection_ErrorBufferSize, "listen() failed, Error: %s", strerror(errno)); +#endif + sock = 0; + return false; + } + + return true; +} + +void TCPServer::Close() { + LockMutex lock(&MSock); + if (sock) { +#ifdef WIN32 + closesocket(sock); +#else + close(sock); +#endif + } + sock = 0; +} + +bool TCPServer::IsOpen() { + MSock.lock(); + bool ret = (bool) (sock != 0); + MSock.unlock(); + return ret; +} + +TCPConnection* TCPServer::NewQueuePop() { + TCPConnection* ret; + MNewQueue.lock(); + ret = NewQueue.pop(); + MNewQueue.unlock(); + return ret; +} + +void TCPServer::AddConnection(TCPConnection* con) { + list->Append(con); + MNewQueue.lock(); + NewQueue.push(con); + MNewQueue.unlock(); +} + +TCPConnection* TCPServer::GetConnection(int32 iID) { + LinkedListIterator iterator(*list); + + iterator.Reset(); + while(iterator.MoreElements()) { + if (iterator.GetData()->GetID() == iID) + return iterator.GetData(); + iterator.Advance(); + } + return 0; +} + +void TCPServer::SendPacket(ServerPacket* pack) { + TCPConnection::TCPNetPacket_Struct* tnps = TCPConnection::MakePacket(pack); + SendPacket(&tnps); +} + +void TCPServer::SendPacket(TCPConnection::TCPNetPacket_Struct** tnps) { + MInQueue.lock(); + InQueue.push(*tnps); + MInQueue.unlock(); + tnps = 0; +} + +void TCPServer::CheckInQueue() { + LinkedListIterator iterator(*list); + TCPConnection::TCPNetPacket_Struct* tnps = 0; + + while (( tnps = InQueuePop() )) { + iterator.Reset(); + while(iterator.MoreElements()) { + if (iterator.GetData()->GetMode() != modeConsole && iterator.GetData()->GetRemoteID() == 0) + iterator.GetData()->SendPacket(tnps); + iterator.Advance(); + } + safe_delete(tnps); + } +} + +TCPConnection::TCPNetPacket_Struct* TCPServer::InQueuePop() { + TCPConnection::TCPNetPacket_Struct* ret; + MInQueue.lock(); + ret = InQueue.pop(); + MInQueue.unlock(); + return ret; +} + + diff --git a/old/TCPConnection.h b/old/TCPConnection.h new file mode 100644 index 0000000..ae47526 --- /dev/null +++ b/old/TCPConnection.h @@ -0,0 +1,277 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#ifndef TCP_CONNECTION_H +#define TCP_CONNECTION_H +/* + Parent classes for interserver TCP Communication. + -Quagmire +*/ + +#ifdef WIN32 + #define snprintf _snprintf + #define vsnprintf _vsnprintf + #define strncasecmp _strnicmp + #define strcasecmp _stricmp + + #include +#else + #include + #include + #include + #include + #include + #include + #include + #include + #define INVALID_SOCKET -1 + #define SOCKET_ERROR -1 + #include "unix.h" + +#endif + +#include "types.h" +#include "Mutex.h" +#include "linked_list.h" +#include "queue.h" +#include "servertalk.h" +#include "timer.h" +#include "MiscFunctions.h" + +class TCPServer; + +#define TCPConnection_ErrorBufferSize 1024 +#define MaxTCPReceiveBufferSize 524288 + +#define TCPS_Ready 0 +#define TCPS_Connecting 1 +#define TCPS_Connected 100 +#define TCPS_Disconnecting 200 +#define TCPS_Disconnected 201 +#define TCPS_Closing 250 +#define TCPS_Error 255 + +#ifndef DEF_eConnectionType +#define DEF_eConnectionType +enum eConnectionType {Incomming, Outgoing}; +#endif + +#ifdef WIN32 + void TCPServerLoop(void* tmp); + void TCPConnectionLoop(void* tmp); +#else + void* TCPServerLoop(void* tmp); + void* TCPConnectionLoop(void* tmp); +#endif + +enum eTCPMode { modeConsole, modeTransition, modePacket }; +class TCPConnection { +public: +#pragma pack(1) + struct TCPNetPacket_Struct { + int32 size; + struct { + int8 + compressed : 1, + destination : 1, + flag3 : 1, + flag4 : 1, + flag5 : 1, + flag6 : 1, + flag7 : 1, + flag8 : 1; + } flags; + int16 opcode; + uchar buffer[0]; + }; +#pragma pack() + + static TCPNetPacket_Struct* MakePacket(ServerPacket* pack, int32 iDestination = 0); + + TCPConnection(TCPServer* iServer, SOCKET iSock, int32 irIP, int16 irPort, bool iOldFormat = false); + TCPConnection(bool iOldFormat = false, TCPServer* iRelayServer = 0, eTCPMode iMode = modePacket); // for outgoing connections + TCPConnection(TCPServer* iServer, TCPConnection* iRelayLink, int32 iRemoteID, int32 irIP, int16 irPort); // for relay connections + virtual ~TCPConnection(); + + // Functions for outgoing connections + bool Connect(char* irAddress, int16 irPort, char* errbuf = 0); + bool Connect(int32 irIP, int16 irPort, char* errbuf = 0); + void AsyncConnect(char* irAddress, int16 irPort); + void AsyncConnect(int32 irIP, int16 irPort); + virtual void Disconnect(bool iSendRelayDisconnect = true); + + virtual bool SendPacket(ServerPacket* pack, int32 iDestination = 0); + virtual bool SendPacket(TCPNetPacket_Struct* tnps); + bool Send(const uchar* data, sint32 size); + + char* PopLine(); + ServerPacket* PopPacket(); // OutQueuePop() + inline int32 GetrIP() { return rIP; } + inline int16 GetrPort() { return rPort; } + virtual int8 GetState(); + eTCPMode GetMode() { return TCPMode; } + inline bool Connected() { return (GetState() == TCPS_Connected); } + inline bool ConnectReady() { return (bool) (GetState() == TCPS_Ready && ConnectionType == Outgoing); } + void Free(); // Inform TCPServer that this connection object is no longer referanced + + inline int32 GetID() { return id; } + inline bool IsRelayServer() { return RelayServer; } + inline int32 GetRemoteID() { return RemoteID; } + inline TCPConnection* GetRelayLink() { return RelayLink; } + + bool GetEcho(); + void SetEcho(bool iValue); +protected: + friend class TCPServer; + virtual bool Process(); + void SetState(int8 iState); + inline bool IsFree() { return pFree; } + bool CheckNetActive(); + +#ifdef WIN32 + friend void TCPConnectionLoop(void* tmp); +#else + friend void* TCPConnectionLoop(void* tmp); +#endif + SOCKET sock; + bool RunLoop(); + Mutex MLoopRunning; + Mutex MAsyncConnect; + bool GetAsyncConnect(); + bool SetAsyncConnect(bool iValue); + char* charAsyncConnect; + +#ifdef WIN32 + friend class TCPConnection; +#endif + void OutQueuePush(ServerPacket* pack); + void RemoveRelay(TCPConnection* relay, bool iSendRelayDisconnect); +private: + void ProcessNetworkLayerPacket(ServerPacket* pack); + void SendNetErrorPacket(const char* reason = 0); + TCPServer* Server; + TCPConnection* RelayLink; + int32 RemoteID; + sint32 RelayCount; + + bool pOldFormat; + bool SendData(char* errbuf = 0); + bool RecvData(char* errbuf = 0); + bool ProcessReceivedData(char* errbuf = 0); + bool ProcessReceivedDataAsPackets(char* errbuf = 0); + bool ProcessReceivedDataAsOldPackets(char* errbuf = 0); + void ClearBuffers(); + + bool pAsyncConnect; + + eConnectionType ConnectionType; + eTCPMode TCPMode; + bool RelayServer; + Mutex MRunLoop; + bool pRunLoop; + + SOCKET connection_socket; + int32 id; + int32 rIP; + int16 rPort; // host byte order + bool pFree; + + Mutex MState; + int8 pState; + + void LineOutQueuePush(char* line); + MyQueue LineOutQueue; + MyQueue OutQueue; + Mutex MOutQueueLock; + + Timer* keepalive_timer; + Timer* timeout_timer; + + uchar* recvbuf; + sint32 recvbuf_size; + sint32 recvbuf_used; + + sint32 recvbuf_echo; + bool pEcho; + Mutex MEcho; + + void InModeQueuePush(TCPNetPacket_Struct* tnps); + MyQueue InModeQueue; + Mutex MSendQueue; + uchar* sendbuf; + sint32 sendbuf_size; + sint32 sendbuf_used; + bool ServerSendQueuePop(uchar** data, sint32* size); + void ServerSendQueuePushEnd(const uchar* data, sint32 size); + void ServerSendQueuePushEnd(uchar** data, sint32 size); + void ServerSendQueuePushFront(uchar* data, sint32 size); +}; + +class TCPServer { +public: + TCPServer(int16 iPort = 0, bool iOldFormat = false); + virtual ~TCPServer(); + + bool Open(int16 iPort = 0, char* errbuf = 0); // opens the port + void Close(); // closes the port + bool IsOpen(); + inline int16 GetPort() { return pPort; } + + TCPConnection* NewQueuePop(); + + void SendPacket(ServerPacket* pack); + void SendPacket(TCPConnection::TCPNetPacket_Struct** tnps); +protected: +#ifdef WIN32 + friend void TCPServerLoop(void* tmp); +#else + friend void* TCPServerLoop(void* tmp); +#endif + void Process(); + bool RunLoop(); + Mutex MLoopRunning; + + friend class TCPConnection; + inline int32 GetNextID() { return NextID++; } + void AddConnection(TCPConnection* con); + TCPConnection* GetConnection(int32 iID); +private: + void ListenNewConnections(); + + int32 NextID; + bool pOldFormat; + + Mutex MRunLoop; + bool pRunLoop; + + Mutex MSock; + SOCKET sock; + int16 pPort; + + Mutex MNewQueue; + MyQueue NewQueue; + + void CheckInQueue(); + Mutex MInQueue; + TCPConnection::TCPNetPacket_Struct* InQueuePop(); + MyQueue InQueue; + + LinkedList* list; +}; +#endif diff --git a/old/Web/WebServer.cpp b/old/Web/WebServer.cpp new file mode 100644 index 0000000..f24769e --- /dev/null +++ b/old/Web/WebServer.cpp @@ -0,0 +1,364 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ + +#include "WebServer.h" +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "../version.h" + +#ifdef WORLD + #include "../../WorldServer/WorldDatabase.h" + extern WorldDatabase database; +#endif +#ifdef LOGIN + #include "../../LoginServer/LoginDatabase.h" + extern LoginDatabase database; +#endif + +#ifdef WIN32 + #include + #define strncasecmp _strnicmp + #define strcasecmp _stricmp + #include +#else + #include + #include "../unix.h" +#endif + +ThreadReturnType RunWebServer (void* tmp); + +static std::string keypasswd = ""; + +void web_handle_version(const http::request& req, http::response& res) { + res.set(http::field::content_type, "application/json"); + boost::property_tree::ptree pt; + + // Add key-value pairs to the property tree + pt.put("eq2emu_process", std::string(EQ2EMU_MODULE)); + pt.put("version", std::string(CURRENT_VERSION)); + pt.put("compile_date", std::string(COMPILE_DATE)); + pt.put("compile_time", std::string(COMPILE_TIME)); + + // Create an output string stream to hold the JSON string + std::ostringstream oss; + + // Write the property tree to the output string stream as JSON + boost::property_tree::write_json(oss, pt); + + // Get the JSON string from the output string stream + std::string json = oss.str(); + res.body() = json; + res.prepare_payload(); +} + +void web_handle_root(const http::request& req, http::response& res) { + res.set(http::field::content_type, "text/html"); + res.body() = "Hello!"; + res.prepare_payload(); +} + +// this function is called to obtain password info about an encrypted key +std::string WebServer::my_password_callback( + std::size_t max_length, // the maximum length for a password + ssl::context::password_purpose purpose ) // for_reading or for_writing +{ + return keypasswd; +} + +//void handle_root(const http::request& req, http::response& res); + +WebServer::WebServer(const std::string& address, unsigned short port, const std::string& cert_file, const std::string& key_file, const std::string& key_password, const std::string& hardcode_user, const std::string& hardcode_password) + : ioc_(1), + ssl_ctx_(ssl::context::tlsv13_server), + acceptor_(ioc_, {boost_net::ip::make_address(address), port}) { + keypasswd = key_password; + // Initialize SSL context + if(cert_file.size() < 1 || key_file.size() < 1) { + is_ssl = false; + } + else { + ssl_ctx_.set_password_callback(my_password_callback); + ssl_ctx_.use_certificate_chain_file(cert_file); + ssl_ctx_.use_private_key_file(key_file, ssl::context::file_format::pem); + is_ssl = true; + } + keypasswd = ""; // reset no longer needed + + if(hardcode_user.size() > 0 && hardcode_password.size() > 0) + credentials_[hardcode_user] = hardcode_password; + + register_route("/", web_handle_root); + register_route("/version", web_handle_version); +} + +WebServer::~WebServer() { + ioc_.stop(); +} + +ThreadReturnType RunWebServer (void* tmp) { + if(tmp == nullptr) { + THREAD_RETURN(NULL); + } + WebServer* ws = (WebServer*)tmp; + ws->start(); + THREAD_RETURN(NULL); +} + +void WebServer::start() { + do_accept(); + ioc_.run(); +} + +void WebServer::run() { + pthread_t thread; + pthread_create(&thread, NULL, RunWebServer, this); + pthread_detach(thread); +} + + +void WebServer::register_route(const std::string& uri, std::function&, http::response&)> handler, bool auth_req) { + int32 status = database.NoAuthRoute((char*)uri.c_str()); // overrides the default hardcode settings via DB + if(status == 0) { + auth_req = false; + } + if(auth_req) { + routes_[uri] = handler; + } + else { + noauth_routes_[uri] = handler; + } + route_required_status_[uri] = status; +} + +void WebServer::do_accept() { + acceptor_.async_accept( + [this](beast::error_code ec, tcp::socket socket) { + this->on_accept(ec, std::move(socket)); + }); +} + +void WebServer::on_accept(beast::error_code ec, tcp::socket socket) { + if (!ec) { + if(is_ssl) { + std::thread(&WebServer::do_session_ssl, this, std::move(socket)).detach(); + } + else { + std::thread(&WebServer::do_session, this, std::move(socket)).detach(); + } + } + do_accept(); +} + +void WebServer::do_session(tcp::socket socket) { + try { + bool close = false; + beast::flat_buffer buffer; + + while (!close) { + // 1) Read a complete request + http::request req; + http::read(socket, buffer, req); + + // 2) Invoke your handler, giving it a lambda that + // sets up version/keep_alive on the response + handle_request(std::move(req), [&](auto&& response) { + // mirror HTTP version + response.version(req.version()); + // propagate the client’s keep-alive choice + response.keep_alive(req.keep_alive()); + + // if the client asked us to close, mark for shutdown + if (! req.keep_alive()) + close = true; + + http::write(socket, response); + }); + + // 3) Discard anything left in the buffer so the next + // http::read starts fresh + buffer.consume(buffer.size()); + } + + beast::error_code ec; + socket.shutdown(tcp::socket::shutdown_send, ec); + } + catch (const std::exception& e) { + // irrelevant spam for now really + } +} + +void WebServer::do_session_ssl(tcp::socket socket) { + try { + ssl::stream stream(std::move(socket), ssl_ctx_); + stream.handshake(ssl::stream_base::server); + + bool close = false; + beast::flat_buffer buffer; + + while (!close) { + http::request req; + http::read(stream, buffer, req); + + handle_request(std::move(req), [&](auto&& response) { + response.version(req.version()); + response.keep_alive(req.keep_alive()); + if (! req.keep_alive()) + close = true; + http::write(stream, response); + }); + + buffer.consume(buffer.size()); + } + + beast::error_code ec; + stream.next_layer().shutdown(tcp::socket::shutdown_send, ec); + } + catch (const std::exception& e) { + // irrelevant spam for now really + } +} + +template +void WebServer::handle_request(http::request>&& req, std::function&&)> send) { + auto it = noauth_routes_.find(req.target().to_string()); + if (it != noauth_routes_.end()) { + http::response res{http::status::ok, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + it->second(req, res); + return send(std::move(res)); + } + int32 user_status = 0; + std::string session_id = authenticate(req, &user_status); + if (session_id.size() < 1) { + http::response res{http::status::unauthorized, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::www_authenticate, "Basic realm=\"example\""); + res.body() = "Unauthorized"; + res.prepare_payload(); + return send(std::move(res)); + } + + auto status_it = route_required_status_.find(req.target().to_string()); + if (status_it != route_required_status_.end()) { + if(status_it->second > 0 && status_it->second != 0xFFFFFFFF && status_it->second > user_status) { + http::response res{http::status::unauthorized, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.body() = "Unauthorized status"; + res.prepare_payload(); + return send(std::move(res)); + } + } + + it = routes_.find(req.target().to_string()); + if (it != routes_.end()) { + http::response res{http::status::ok, req.version()}; + res.set(http::field::set_cookie, "session_id=" + session_id); + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + it->second(req, res); + return send(std::move(res)); + } +/* + + http::response res{http::status::not_found, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.body() = "Not Found"; + res.prepare_payload(); + return send(std::move(res)); + */ + return send(http::response{http::status::bad_request, req.version()}); +} + + +std::string WebServer::authenticate(const http::request& req, int32* user_status) { + auto it = req.find(http::field::cookie); + if (it != req.end()) { + std::istringstream cookie_stream(it->value().to_string()); + std::string session_id; + std::getline(cookie_stream, session_id, '='); + if (session_id == "session_id") { + std::string id; + std::getline(cookie_stream, id); + if (sessions_.find(id) != sessions_.end()) { + if(sessions_status_.find(id) != sessions_status_.end()) { + *user_status = sessions_status_[id]; + } + return id; + } + } + } + + it = req.find(http::field::authorization); + if (it != req.end()) { + std::string auth_header = it->value().to_string(); + if (auth_header.substr(0, 6) == "Basic ") { + std::string encoded_credentials = auth_header.substr(6); + std::string decoded_credentials; + decoded_credentials.resize(boost::beast::detail::base64::decoded_size(encoded_credentials.size())); + auto result = boost::beast::detail::base64::decode( + &decoded_credentials[0], + encoded_credentials.data(), + encoded_credentials.size() + ); + decoded_credentials.resize(result.first); + + std::istringstream credentials_stream(decoded_credentials); + std::string username, password; + std::getline(credentials_stream, username, ':'); + std::getline(credentials_stream, password); + int32 out_status = 0; + if ((credentials_.find(username) != credentials_.end() && credentials_[username] == password) || (database.AuthenticateWebUser((char*)username.c_str(),(char*)password.c_str(), &out_status) > 0)) { + std::string session_id = generate_session_id(); + sessions_[session_id] = username; + sessions_status_[session_id] = out_status; + *user_status = out_status; + return session_id; + } + } + } + + return std::string(""); +} + +std::string WebServer::generate_session_id() { + static std::mt19937 rng{std::random_device{}()}; + static std::uniform_int_distribution<> dist(0, 15); + std::string session_id; + for (int i = 0; i < 32; ++i) { + session_id += "0123456789abcdef"[dist(rng)]; + } + return session_id; +} + +// Explicit template instantiation +template void WebServer::handle_request>( + http::request>>&&, + std::function&&)> +); \ No newline at end of file diff --git a/old/Web/WebServer.h b/old/Web/WebServer.h new file mode 100644 index 0000000..e30ae90 --- /dev/null +++ b/old/Web/WebServer.h @@ -0,0 +1,56 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../types.h" + +namespace beast = boost::beast; // from +namespace http = beast::http; // from +namespace boost_net = boost::asio; // from +namespace ssl = boost::asio::ssl; // from +using tcp = boost_net::ip::tcp; // from + +class WebServer { +public: + WebServer(const std::string& address, unsigned short port, const std::string& cert_file, const std::string& key_file, const std::string& key_password, const std::string& hardcode_user, const std::string& hardcode_password); + ~WebServer(); + + void run(); + void start(); + + void register_route(const std::string& uri, std::function&, http::response&)> handler, bool auth_required = true); +private: + bool is_ssl; + static std::string my_password_callback(std::size_t max_length, ssl::context::password_purpose purpose); + void do_accept(); + void on_accept(beast::error_code ec, tcp::socket socket); + void do_session_ssl(tcp::socket socket); + void do_session(tcp::socket socket); + + template + void handle_request(http::request>&& req, std::function&&)> send); + + std::string authenticate(const http::request& req, int32* user_status = 0); + std::string generate_session_id(); + + boost_net::io_context ioc_; + ssl::context ssl_ctx_; + tcp::acceptor acceptor_; + std::unordered_map sessions_; // session_id -> username + std::unordered_map sessions_status_; // session_id -> status + + std::unordered_map credentials_; // username -> password + std::unordered_map route_required_status_; // route -> status + std::unordered_map&, http::response&)>> routes_; + std::unordered_map&, http::response&)>> noauth_routes_; +}; diff --git a/old/database.cpp b/old/database.cpp new file mode 100644 index 0000000..866676c --- /dev/null +++ b/old/database.cpp @@ -0,0 +1,602 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#include "../common/debug.h" + +#include +using namespace std; +#include +#include +#include +#include +//#include +#include +#include +#include +#include + +// Disgrace: for windows compile +#ifdef WIN32 +#include +#include +#define snprintf _snprintf +#define strncasecmp _strnicmp +#define strcasecmp _stricmp +#else +#include "unix.h" +#include +#endif + +#include "database.h" +#include "EQStream.h" +#include "packet_functions.h" +#include "emu_opcodes.h" +#ifdef WORLD + #include "../WorldServer/WorldDatabase.h" + #include "../WorldServer/Web/PeerManager.h" + extern WorldDatabase database; + extern PeerManager peer_manager; +#endif +#ifdef LOGIN + #include "../LoginServer/LoginDatabase.h" + extern LoginDatabase database; +#endif +#ifdef PARSER + #include "../PacketParser/ParserDatabase.h" + extern ParserDatabase database; +#endif + +#ifdef PATCHER + #include "../PatchServer/PatcherDatabase.h" + extern PatcherDatabase database; +#endif +#include "../common/EQEMuError.h" +#include "../common/packet_dump.h" +#include "../common/Log.h" + +#ifdef WORLD +ThreadReturnType DBAsyncQueries(void* str) +{ + // allow some buffer for multiple queries to collect + Sleep(10); + DBStruct* data = (DBStruct*)str; + database.RunAsyncQueries(data->queryid); + delete data; + THREAD_RETURN(NULL); +} +#endif + +Database::Database() +{ + InitVars(); +} + +bool Database::Init(bool silentLoad) { + char host[200], user[200], passwd[200], database[200]; + unsigned int port=0; + bool compression = false; + bool items[6] = {false, false, false, false, false, false}; + const char* exampleIni[] = { "[Database]", "host = localhost", "user = root", "password = pass", "database = dbname", "### --- Assure each parameter is on a new line!" }; + + if(!ReadDBINI(host, user, passwd, database, &port, &compression, items)) { + //exit(1); + return false; + } + + if (!items[0] || !items[1] || !items[2] || !items[3]) + { + LogWrite(DATABASE__ERROR, 0, "DB", "Database file %s is incomplete.", DB_INI_FILE); + int i; + for (i = 0; i < 4; i++) + { + if ( !items[i] ) + LogWrite(DATABASE__ERROR, 0, "DB", "Could not find parameter %s", exampleIni[i+1]); // offset by 1 because the [Database] entry + } + LogWrite(DATABASE__ERROR, 0, "DB", "Example File:"); + int length = sizeof exampleIni / sizeof exampleIni[0]; + for(i=0;i Database::GetVersions(){ + map opcodes; + Query query; + MYSQL_ROW row; + MYSQL_RES* result = query.RunQuery2(Q_SELECT, "select distinct version_range1, version_range2 from opcodes"); + while(result && (row = mysql_fetch_row(result))){ + if(row[0] && row[1]) + opcodes[atoi(row[0])] = atoi(row[1]); + } + return opcodes; +} + +map Database::GetOpcodes(int16 version){ + map opcodes; + Query query; + MYSQL_ROW row; + MYSQL_RES* result = query.RunQuery2(Q_SELECT, "select name, opcode from opcodes where %i between version_range1 and version_range2 order by version_range1, id", version); + while(result && (row = mysql_fetch_row(result))){ + opcodes[row[0]] = atoi(row[1]); + } + return opcodes; +} + +int32 Database::AuthenticateWebUser(char* userName, char* passwd, int32* status){ + if(status) { + *status = 0; + } + Query query; + MYSQL_ROW row; + int32 id = 0; + MYSQL_RES* result = query.RunQuery2(Q_SELECT, "select id, status from web_users where username='%s' and passwd = sha2('%s', 512)", getSafeEscapeString(userName).c_str(), getSafeEscapeString(passwd).c_str()); + if(result && (row = mysql_fetch_row(result))){ + id = atoul(row[0]); + if(status) { + *status = atoul(row[1]); + } + } + return id; +} + +int32 Database::NoAuthRoute(char* route){ + Query query; + MYSQL_ROW row; + int32 status = 0xFFFFFFFF; + MYSQL_RES* result = query.RunQuery2(Q_SELECT, "select status from web_routes where route='%s'", getSafeEscapeString(route).c_str()); + if(result && (row = mysql_fetch_row(result))){ + status = atoul(row[0]); + } + return status; +} + +void Database::HandleMysqlError(int32 errnum) { + switch(errnum) { + case 0: + break; + case 1045: // Access Denied + case 2001: { + AddEQEMuError(EQEMuError_Mysql_1405, true); + break; + } + case 2003: { // Unable to connect + AddEQEMuError(EQEMuError_Mysql_2003, true); + break; + } + case 2005: { // Unable to connect + AddEQEMuError(EQEMuError_Mysql_2005, true); + break; + } + case 2007: { // Unable to connect + AddEQEMuError(EQEMuError_Mysql_2007, true); + break; + } + } +} + +void Database::InitVars() { + +} + +Database::~Database() +{ +#ifdef WORLD + DBQueryMutex.writelock(__FUNCTION__, __LINE__); + activeQuerySessions.clear(); + DBQueryMutex.releasewritelock(__FUNCTION__, __LINE__); + + DBAsyncMutex.writelock(); + continueAsync = false; + map>::iterator itr; + for (itr = asyncQueries.begin(); itr != asyncQueries.end(); itr++) + { + asyncQueriesMutex[itr->first]->writelock(); + deque queries = itr->second; + while (queries.size() > 0) + { + Query* cur = queries.front(); + queries.pop_front(); + safe_delete(cur); + } + asyncQueriesMutex[itr->first]->releasewritelock(); + Mutex* mutex = asyncQueriesMutex[itr->first]; + asyncQueriesMutex.erase(itr->first); + safe_delete(mutex); + } + asyncQueries.clear(); + + asyncQueriesMutex.clear(); + DBAsyncMutex.releasewritelock(); + + PurgeDBInstances(); +#endif +} + +#ifdef WORLD +void Query::AddQueryAsync(int32 queryID, Database* db, QUERY_TYPE type, const char* format, ...) { + in_type = type; + va_list args; + va_start(args, format); +#ifdef WIN32 + char* buffer; + int buf_len = _vscprintf(format, args) + 1; + buffer = new char[buf_len]; + vsprintf(buffer, format, args); +#else + char* buffer; + int buf_len; + va_list argcopy; + va_copy(argcopy, args); + buf_len = vsnprintf(NULL, 0, format, argcopy) + 1; + va_end(argcopy); + + buffer = new char[buf_len]; + vsnprintf(buffer, buf_len, format, args); +#endif + va_end(args); + query = string(buffer); + + Query* asyncQuery = new Query(this, queryID, query); + + safe_delete_array(buffer); + + db->AddAsyncQuery(asyncQuery); +} + +void Query::RunQueryAsync(Database* db) { + db->RunQuery(query.c_str(), query.length(), errbuf, &result, affected_rows, last_insert_id, &errnum, retry); +} +#endif + +MYSQL_RES* Query::RunQuery2(QUERY_TYPE type, const char* format, ...){ + va_list args; + va_start( args, format ); + #ifdef WIN32 + char * buffer; + int buf_len = _vscprintf( format, args ) + 1; + buffer = new char[buf_len]; + vsprintf( buffer, format, args ); + #else + char* buffer; + int buf_len; + va_list argcopy; + va_copy(argcopy, args); + buf_len = vsnprintf(NULL, 0, format, argcopy) + 1; + va_end(argcopy); + + buffer = new char[buf_len]; + vsnprintf(buffer, buf_len, format, args); + #endif + va_end(args); + query = string(buffer); + + + safe_delete_array( buffer ); + + + return RunQuery2(query.c_str(), type); +} +MYSQL_RES* Query::RunQuery2(string in_query, QUERY_TYPE type){ + switch(type){ + case Q_SELECT: + break; + case Q_DBMS: + case Q_REPLACE: + case Q_DELETE: + case Q_UPDATE: + safe_delete(affected_rows); + affected_rows = new int32; + break; + case Q_INSERT: + safe_delete(last_insert_id); + last_insert_id = new int32; + } + if(result){ + if(!multiple_results) + multiple_results = new vector(); + multiple_results->push_back(result); + } + query = in_query; + +#if defined WORLD && defined _DEBUG + if (type == Q_UPDATE || type == Q_INSERT || type == Q_DELETE || type == Q_REPLACE) + { + char* filteredTables[] = { " characters", " character_", " `character_", " statistics", " variables", " char_colors", " `guild", " bugs" }; + + bool match = false; + for (int i = 0; i < sizeof(filteredTables) / sizeof(filteredTables[0]); i++) + { + if (query.find(filteredTables[i]) != std::string::npos) { + match = true; + break; + } + } + try + { + if (!match) + { + FILE* pFile; + pFile = fopen("sql_updates.sql", "a+"); + fwrite(query.c_str(), 1, query.length(), pFile); + fwrite(";", sizeof(char), 1, pFile); + fwrite("\n", sizeof(char), 1, pFile); + fclose(pFile); + } + } + catch (...) {} + } +#endif + + + database.RunQuery(query.c_str(), query.length(), errbuf, &result, affected_rows, last_insert_id, &errnum, retry); + return result; +} + +#ifdef WORLD +void Database::RunAsyncQueries(int32 queryid) +{ + Database* asyncdb = FindFreeInstance(); + DBAsyncMutex.writelock(); + map>::iterator itr = asyncQueries.find(queryid); + if (itr == asyncQueries.end()) + { + DBAsyncMutex.releasewritelock(); + return; + } + + asyncQueriesMutex[queryid]->writelock(); + deque queries; + while (itr->second.size()) + { + Query* cur = itr->second.front(); + queries.push_back(cur); + itr->second.pop_front(); + } + itr->second.clear(); + asyncQueries.erase(itr); + DBAsyncMutex.releasewritelock(); + asyncQueriesMutex[queryid]->releasewritelock(); + + int32 count = 0; + while (queries.size() > 0) + { + Query* cur = queries.front(); + cur->RunQueryAsync(asyncdb); + this->RemoveActiveQuery(cur); + queries.pop_front(); + safe_delete(cur); + } + FreeDBInstance(asyncdb); + + bool isActive = LocalIsActiveQuery(queryid); + if (isActive) + { + continueAsync = true; + DBStruct* tmp = new DBStruct; + tmp->queryid = queryid; +#ifdef WIN32 + _beginthread(DBAsyncQueries, 0, (void*)tmp); +#else + pthread_t t1; + pthread_create(&t1, NULL, DBAsyncQueries, (void*)tmp); + pthread_detach(t1); +#endif + } +} + +void Database::AddAsyncQuery(Query* query) +{ + DBAsyncMutex.writelock(); + map::iterator mutexItr = asyncQueriesMutex.find(query->GetQueryID()); + if (mutexItr == asyncQueriesMutex.end()) + { + Mutex* queryMutex = new Mutex(); + queryMutex->SetName("AsyncQuery" + query->GetQueryID()); + asyncQueriesMutex.insert(make_pair(query->GetQueryID(), queryMutex)); + } + map>::iterator itr = asyncQueries.find(query->GetQueryID()); + asyncQueriesMutex[query->GetQueryID()]->writelock(); + + if ( itr != asyncQueries.end()) + itr->second.push_back(query); + else + { + deque queue; + queue.push_back(query); + asyncQueries.insert(make_pair(query->GetQueryID(), queue)); + } + + AddActiveQuery(query); + + asyncQueriesMutex[query->GetQueryID()]->releasewritelock(); + DBAsyncMutex.releasewritelock(); + + bool isActive = LocalIsActiveQuery(query->GetQueryID(), query); + if (!isActive) + { + continueAsync = true; + DBStruct* tmp = new DBStruct; + tmp->queryid = query->GetQueryID(); +#ifdef WIN32 + _beginthread(DBAsyncQueries, 0, (void*)tmp); +#else + pthread_t t1; + pthread_create(&t1, NULL, DBAsyncQueries, (void*)tmp); + pthread_detach(t1); +#endif + } +} + +Database* Database::FindFreeInstance() +{ + Database* db_inst = 0; + map::iterator itr; + DBInstanceMutex.writelock(__FUNCTION__, __LINE__); + for (itr = dbInstances.begin(); itr != dbInstances.end(); itr++) { + if (!itr->second) + { + db_inst = itr->first; + itr->second = true; + break; + } + } + + if (!db_inst) + { + WorldDatabase* tmp = new WorldDatabase(); + db_inst = (Database*)tmp; + tmp->Init(); + tmp->ConnectNewDatabase(); + dbInstances.insert(make_pair(db_inst, true)); + } + DBInstanceMutex.releasewritelock(__FUNCTION__, __LINE__); + + return db_inst; +} + +void Database::PurgeDBInstances() +{ + map::iterator itr; + DBInstanceMutex.writelock(__FUNCTION__, __LINE__); + for (itr = dbInstances.begin(); itr != dbInstances.end(); itr++) { + WorldDatabase* tmpInst = (WorldDatabase*)itr->first; + safe_delete(tmpInst); + } + dbInstances.clear(); + DBInstanceMutex.releasewritelock(__FUNCTION__, __LINE__); +} + + +void Database::PingAsyncDatabase() +{ + map::iterator itr; + DBInstanceMutex.readlock(__FUNCTION__, __LINE__); + for (itr = dbInstances.begin(); itr != dbInstances.end(); itr++) { + Database* tmpInst = itr->first; + tmpInst->ping(); + } + DBInstanceMutex.releasereadlock(__FUNCTION__, __LINE__); +} + +void Database::FreeDBInstance(Database* cur) +{ + DBInstanceMutex.writelock(__FUNCTION__, __LINE__); + dbInstances[cur] = false; + DBInstanceMutex.releasewritelock(__FUNCTION__, __LINE__); +} + +void Database::RemoveActiveQuery(Query* query) +{ + DBQueryMutex.writelock(__FUNCTION__, __LINE__); + + vector::iterator itr; + for (itr = activeQuerySessions.begin(); itr != activeQuerySessions.end(); itr++) + { + Query* curQuery = *itr; + if (query == curQuery) + { + activeQuerySessions.erase(itr); + break; + } + } + DBQueryMutex.releasewritelock(__FUNCTION__, __LINE__); + + bool isActive = LocalIsActiveQuery(query->GetQueryID()); + if(!isActive) { + peer_manager.sendPeersActiveQuery(query->GetQueryID(), true); + } +} + +void Database::AddActiveQuery(Query* query) +{ + peer_manager.sendPeersActiveQuery(query->GetQueryID(), false); + DBQueryMutex.writelock(__FUNCTION__, __LINE__); + activeQuerySessions.push_back(query); + DBQueryMutex.releasewritelock(__FUNCTION__, __LINE__); +} + +bool Database::IsActiveQuery(int32 id, Query* skip) { + if (LocalIsActiveQuery(id, skip)) + return true; + + { + auto now = std::chrono::steady_clock::now(); + std::lock_guard lock(_peerMtx); + + // remove any entries older than timeout + for (auto it = _peerActive.begin(); it != _peerActive.end(); ) { + if (now - it->second > kStaleTimeout) + it = _peerActive.erase(it); + else + ++it; + } + + // if this id is still in the map, it's active + if (_peerActive.find(id) != _peerActive.end()) + return true; + } + + return false; +} + +bool Database::LocalIsActiveQuery(int32 id, Query* skip) +{ + bool isActive = false; + DBQueryMutex.readlock(__FUNCTION__, __LINE__); + for (auto query : activeQuerySessions) { + if (query == skip) continue; + if (query->GetQueryID() == id) { + isActive = true; + break; + } + } + DBQueryMutex.releasereadlock(__FUNCTION__, __LINE__); + return isActive; +} + +void Database::AddPeerActiveQuery(int32 charID) { + auto now = std::chrono::steady_clock::now(); + std::lock_guard lock(_peerMtx); + _peerActive[charID] = now; // inserts or updates timestamp +} + +void Database::RemovePeerActiveQuery(int32 charID) { + std::lock_guard lock(_peerMtx); + _peerActive.erase(charID); +} +#endif \ No newline at end of file diff --git a/old/database.h b/old/database.h new file mode 100644 index 0000000..c5aa88f --- /dev/null +++ b/old/database.h @@ -0,0 +1,192 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#ifndef EQ2EMU_DATABASE_H +#define EQ2EMU_DATABASE_H + +#ifdef WIN32 + #include + #include +#endif +#include + +#include "dbcore.h" +#include "types.h" +#include "linked_list.h" +#include "EQStream.h" +#include "MiscFunctions.h" +#include "Mutex.h" +#include +#include +#include +#include +#include +#include + +using namespace std; +class Query; + +class Database : public DBcore +{ +public: + Database(); + ~Database(); + bool Init(bool silentLoad=false); + bool LoadVariables(); + void HandleMysqlError(int32 errnum); + map GetOpcodes(int16 version); + int32 AuthenticateWebUser(char* userName, char* passwd,int32* status = 0); + int32 NoAuthRoute(char* route); + map GetVersions(); + +#ifdef WORLD + void AddAsyncQuery(Query* query); + void RunAsyncQueries(int32 queryid); + Database* FindFreeInstance(); + void RemoveActiveQuery(Query* query); + void AddActiveQuery(Query* query); + bool IsActiveQuery(int32 id, Query* skip=0); + void PingAsyncDatabase(); + + void AddPeerActiveQuery(int32 charID); + void RemovePeerActiveQuery(int32 charID); +#endif +protected: + +private: + void InitVars(); + bool LocalIsActiveQuery(int32 id, Query* skip = nullptr); +#ifdef WORLD + void PurgeDBInstances(); + void FreeDBInstance(Database* cur); + bool continueAsync; + map> asyncQueries; + map asyncQueriesMutex; + map dbInstances; + vector activeQuerySessions; + Mutex DBAsyncMutex; + Mutex DBInstanceMutex; + Mutex DBQueryMutex; + std::unordered_map _peerActive; + std::mutex _peerMtx; + static constexpr std::chrono::seconds kStaleTimeout{30}; +#endif +}; + +typedef struct { + int32 queryid; +}DBStruct; + +class Query{ +public: + Query() { + result = 0; + affected_rows = 0; + last_insert_id = 0; + errnum = 0; + row = 0; + retry = true; + escaped_name = 0; + escaped_pass = 0; + escaped_data1 = 0; + multiple_results = 0; + memset(errbuf, 0, sizeof(errbuf)); + queryID = 0; + } + Query(Query* queryPtr, int32 in_id, std::string in_query) { + result = 0; + affected_rows = 0; + last_insert_id = 0; + errnum = 0; + row = 0; + retry = true; + escaped_name = 0; + escaped_pass = 0; + escaped_data1 = 0; + multiple_results = 0; + memset(errbuf, 0, sizeof(errbuf)); + query = std::move(in_query); + in_type = queryPtr->GetQueryType(); + queryID = in_id; + } + + ~Query(){ + if(result) + mysql_free_result(result); + result = 0; + safe_delete(affected_rows); + safe_delete(last_insert_id); + safe_delete_array(escaped_name); + safe_delete_array(escaped_pass); + safe_delete_array(escaped_data1); + if(multiple_results){ + vector::iterator itr; + for(itr = multiple_results->begin(); itr != multiple_results->end(); itr++){ + mysql_free_result(*itr); + } + safe_delete(multiple_results); + } + } + int32 GetLastInsertedID() { return *last_insert_id; } + int32 GetAffectedRows() { return *affected_rows; } + MYSQL_RES* GetResult() { return result; } + MYSQL_RES* RunQuery2(string in_query, QUERY_TYPE type); + char* GetError() { return errbuf; } + int32 GetErrorNumber(){ return errnum; } + const char* GetQuery() { return query.c_str(); } + char* GetField(int8 field_num) { + if(!row && result) + *row = mysql_fetch_row(result); + if(row && result && field_num < mysql_num_fields(result)) + return *row[field_num]; + else + return NULL; + } + void NextRow(){ + if(result) + *row = mysql_fetch_row(result); + } + void AddQueryAsync(int32 queryID, Database* db, QUERY_TYPE type, const char* format, ...); + void RunQueryAsync(Database* db); + MYSQL_RES* RunQuery2(QUERY_TYPE type, const char* format, ...); + + QUERY_TYPE GetQueryType() { + return in_type; + } + + int32 GetQueryID() { return queryID; } + + char* escaped_name; + char* escaped_pass; + char* escaped_data1; +private: + string query; + char errbuf[MYSQL_ERRMSG_SIZE]; + MYSQL_RES *result; + vector* multiple_results; + int32* affected_rows; + int32* last_insert_id; + int32 errnum; + QUERY_TYPE in_type; + bool retry; + MYSQL_ROW* row; + MYSQL mysql; + int32 queryID; +}; +#endif diff --git a/old/dbcore.cpp b/old/dbcore.cpp new file mode 100644 index 0000000..e200e9d --- /dev/null +++ b/old/dbcore.cpp @@ -0,0 +1,368 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#include "debug.h" + +#include +using namespace std; +#include +//#include +#include +#include "dbcore.h" +#include +#include +#include +#include "types.h" +#include "MiscFunctions.h" +#include "Log.h" + +#ifdef WIN32 + #define snprintf _snprintf + #define strncasecmp _strnicmp + #define strcasecmp _stricmp + #include +#else + #include "unix.h" + #include +#endif + +#ifdef _EQDEBUG + #define DEBUG_MYSQL_QUERIES 0 +#else + #define DEBUG_MYSQL_QUERIES 0 +#endif + +DBcore::DBcore() { + mysql_init(&mysql); + pHost = 0; + pPort = 0; + pUser = 0; + pPassword = 0; + pDatabase = 0; + pCompress = false; +pSSL = false; +pStatus = Closed; +} + +DBcore::~DBcore() { + pStatus = Closed; + mysql_close(&mysql); +#if MYSQL_VERSION_ID >= 50003 + mysql_library_end(); +#else + mysql_server_end(); +#endif + safe_delete_array(pHost); + safe_delete_array(pUser); + safe_delete_array(pPassword); + safe_delete_array(pDatabase); +} + + +bool DBcore::ReadDBINI(char* host, char* user, char* passwd, char* database, unsigned int* port, bool* compress, bool* items) { + char line[256], * key, * val; + bool on_database_section = false; + FILE* f; + + if ((f = fopen(DB_INI_FILE, "r")) == NULL) { + LogWrite(DATABASE__ERROR, 0, "DBCore", "Unable to open '%s' for reading", DB_INI_FILE); + return false; + } + + //read each line + while (fgets(line, sizeof(line), f) != NULL) { + + //remove any new line or carriage return + while ((key = strstr(line, "\n")) != NULL) + *key = '\0'; + while ((key = strstr(line, "\r")) != NULL) + *key = '\0'; + + //ignore blank lines and commented lines + if (strlen(line) == 0 || line[0] == '#') + continue; + + key = strtok(line, "="); + + if (key == NULL) + continue; + + //don't do anything until we find the [Database] section + if (!on_database_section && strncasecmp(key, "[Database]", 10) == 0) + on_database_section = true; + else { + val = strtok(NULL, "="); + + if (val == NULL) + { + if (strcasecmp(key, "password") == 0) { + strcpy(passwd, ""); + items[2] = true; + } + continue; + } + if (strcasecmp(key, "host") == 0) { + strcpy(host, val); + items[0] = true; + } + else if (strcasecmp(key, "user") == 0) { + strcpy(user, val); + items[1] = true; + } + else if (strcasecmp(key, "password") == 0) { + strcpy(passwd, val); + items[2] = true; + } + else if (strcasecmp(key, "database") == 0) { + strcpy(database, val); + items[3] = true; + } + else if (strcasecmp(key, "port") == 0 && port) { + *port = atoul(val); + items[4] = true; + } + else if (strcasecmp(key, "compression") == 0) { + if (strcasecmp(val, "on") == 0) { + if(compress) { + *compress = true; + items[5] = true; + LogWrite(DATABASE__INFO, 0, "DBCore", "DB Compression on."); + } + } + } + } + } + + fclose(f); + + if (!on_database_section) { + LogWrite(DATABASE__ERROR, 0, "DBCore", "[Database] section not found in '%s'", DB_INI_FILE); + return false; + } + + return true; +} + + +// Sends the MySQL server a keepalive +void DBcore::ping() { + if (!MDatabase.trylock()) { + // well, if's it's locked, someone's using it. If someone's using it, it doesnt need a keepalive + return; + } + mysql_ping(&mysql); + + int32* errnum = new int32; + *errnum = mysql_errno(&mysql); + + switch (*errnum) + { + case CR_COMMANDS_OUT_OF_SYNC: + case CR_SERVER_GONE_ERROR: + case CR_UNKNOWN_ERROR: + { + LogWrite(DATABASE__ERROR, 0, "DBCore", "[Database] We lost connection to the database., errno: %i", errno); + break; + } + } + + safe_delete(errnum); + MDatabase.unlock(); +} + +bool DBcore::RunQuery(const char* query, int32 querylen, char* errbuf, MYSQL_RES** result, int32* affected_rows, int32* last_insert_id, int32* errnum, bool retry) { + if (errnum) + *errnum = 0; + if (errbuf) + errbuf[0] = 0; + bool ret = false; + LockMutex lock(&MDatabase); + if (pStatus != Connected) + Open(); + + LogWrite(DATABASE__QUERY, 0, "DBCore", query); + if (mysql_real_query(&mysql, query, querylen)) { + if (mysql_errno(&mysql) == CR_SERVER_GONE_ERROR) + pStatus = Error; + if (mysql_errno(&mysql) == CR_SERVER_LOST || mysql_errno(&mysql) == CR_SERVER_GONE_ERROR) { + if (retry) { + LogWrite(DATABASE__ERROR, 0, "DBCore", "Lost connection, attempting to recover..."); + ret = RunQuery(query, querylen, errbuf, result, affected_rows, last_insert_id, errnum, false); + } + else { + pStatus = Error; + if (errnum) + *errnum = mysql_errno(&mysql); + if (errbuf) + snprintf(errbuf, MYSQL_ERRMSG_SIZE, "#%i: %s", mysql_errno(&mysql), mysql_error(&mysql)); + LogWrite(DATABASE__ERROR, 0, "DBCore", "#%i: %s\nQuery:\n%s", mysql_errno(&mysql), mysql_error(&mysql), query); + ret = false; + } + } + else { + if (errnum) + *errnum = mysql_errno(&mysql); + if (errbuf) + snprintf(errbuf, MYSQL_ERRMSG_SIZE, "#%i: %s", mysql_errno(&mysql), mysql_error(&mysql)); + LogWrite(DATABASE__ERROR, 0, "DBCore", "#%i: %s\nQuery:\n%s", mysql_errno(&mysql), mysql_error(&mysql), query); + ret = false; + } + } + else { + if (result && mysql_field_count(&mysql)) { + *result = mysql_store_result(&mysql); + } + else if (result) + *result = 0; + if (affected_rows) + *affected_rows = mysql_affected_rows(&mysql); + if (last_insert_id) + *last_insert_id = mysql_insert_id(&mysql); + if (result) { + if (*result) { + ret = true; + } + else { + if (errnum) + *errnum = UINT_MAX; + if (errbuf){ + if((!affected_rows || (affected_rows && *affected_rows == 0)) && (!last_insert_id || (last_insert_id && *last_insert_id == 0))) + LogWrite(DATABASE__RESULT, 1, "DBCore", "No Result."); + } + ret = false; + } + } + else { + ret = true; + } + } + + if (ret) + { + char tmp1[200] = {0}; + char tmp2[200] = {0}; + if (result && (*result)) + snprintf(tmp1, sizeof(tmp1), ", %i rows returned", (int) mysql_num_rows(*result)); + if (affected_rows) + snprintf(tmp2, sizeof(tmp2), ", %i rows affected", (*affected_rows)); + + LogWrite(DATABASE__DEBUG, 0, "DBCore", "Query Successful%s%s", tmp1, tmp2); + } + else + LogWrite(DATABASE__DEBUG, 0, "DBCore", "Query returned no results in %s!\n%s", __FUNCTION__, query); + + return ret; +} + +int32 DBcore::DoEscapeString(char* tobuf, const char* frombuf, int32 fromlen) { + LockMutex lock(&MDatabase); + return mysql_real_escape_string(&mysql, tobuf, frombuf, fromlen); +} + +bool DBcore::Open(const char* iHost, const char* iUser, const char* iPassword, const char* iDatabase,int32 iPort, int32* errnum, char* errbuf, bool iCompress, bool iSSL) { + LockMutex lock(&MDatabase); + safe_delete_array(pHost); + safe_delete_array(pUser); + safe_delete_array(pPassword); + safe_delete_array(pDatabase); + pHost = new char[strlen(iHost) + 1]; + strcpy(pHost, iHost); + pUser = new char[strlen(iUser) + 1]; + strcpy(pUser, iUser); + pPassword = new char[strlen(iPassword) + 1]; + strcpy(pPassword, iPassword); + pDatabase = new char[strlen(iDatabase) + 1]; + strcpy(pDatabase, iDatabase); + pCompress = iCompress; + pPort = iPort; + pSSL = iSSL; + return Open(errnum, errbuf); +} + +bool DBcore::Open(int32* errnum, char* errbuf) { + if (errbuf) + errbuf[0] = 0; + LockMutex lock(&MDatabase); + if (GetStatus() == Connected) + return true; + if (GetStatus() == Error) + mysql_close(&mysql); + if (!pHost) + return false; + /* + Quagmire - added CLIENT_FOUND_ROWS flag to the connect + otherwise DB update calls would say 0 rows affected when the value already equalled + what the function was tring to set it to, therefore the function would think it failed + */ + int32 flags = CLIENT_FOUND_ROWS; + if (pCompress) + flags |= CLIENT_COMPRESS; + if (pSSL) + flags |= CLIENT_SSL; + if (mysql_real_connect(&mysql, pHost, pUser, pPassword, pDatabase, pPort, 0, flags)) { + pStatus = Connected; + return true; + } + else { + if (errnum) + *errnum = mysql_errno(&mysql); + if (errbuf) + snprintf(errbuf, MYSQL_ERRMSG_SIZE, "#%i: %s", mysql_errno(&mysql), mysql_error(&mysql)); + pStatus = Error; + return false; + } +} + +char* DBcore::getEscapeString(const char* from_string){ + if(!from_string) + from_string =""; + int orig_size = strlen(from_string); + int escape_size = (orig_size * 2) + 1; + char* escaped = new char[escape_size]; + memset(escaped, 0, escape_size); + DoEscapeString(escaped, from_string, orig_size); + return escaped; +} + +string DBcore::getSafeEscapeString(const char* from_string){ + if(!from_string) + from_string =""; + int orig_size = strlen(from_string); + int escape_size = (orig_size * 2) + 1; + char* escaped = new char[escape_size]; + memset(escaped, 0, escape_size); + DoEscapeString(escaped, from_string, orig_size); + string ret = string(escaped); + safe_delete_array(escaped); + return ret; +} + +string DBcore::getSafeEscapeString(string* from_string){ + if(!from_string) + return ""; + int orig_size = from_string->length(); + int escape_size = (orig_size * 2) + 1; + char* escaped = new char[escape_size]; + memset(escaped, 0, escape_size); + DoEscapeString(escaped, from_string->c_str(), orig_size); + string ret = string(escaped); + safe_delete_array(escaped); + return ret; +} + diff --git a/old/dbcore.h b/old/dbcore.h new file mode 100644 index 0000000..b6cbfd2 --- /dev/null +++ b/old/dbcore.h @@ -0,0 +1,80 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#ifndef DBCORE_H +#define DBCORE_H + +#ifdef WIN32 + #include + #include + //#include +#endif +#include +#include "../common/types.h" +#include "../common/Mutex.h" +#include "../common/linked_list.h" +#include "../common/queue.h" +#include "../common/timer.h" +#include "../common/Condition.h" +#ifdef LOGIN + #define DB_INI_FILE "login_db.ini" +#endif +#ifdef WORLD + #define DB_INI_FILE "world_db.ini" +#endif +#ifdef PARSER + #define DB_INI_FILE "parser_db.ini" +#endif +#ifdef PATCHER + #define DB_INI_FILE "patcher_db.ini" +#endif +class DBcore{ +public: + enum eStatus { Closed, Connected, Error }; + DBcore(); + ~DBcore(); + eStatus GetStatus() { return pStatus; } + bool RunQuery(const char* query, int32 querylen, char* errbuf = 0, MYSQL_RES** result = 0, int32* affected_rows = 0, int32* last_insert_id = 0, int32* errnum = 0, bool retry = true); + int32 DoEscapeString(char* tobuf, const char* frombuf, int32 fromlen); + void ping(); + char* getEscapeString(const char* from_string); + string getSafeEscapeString(const char* from_string); + string getSafeEscapeString(string* from_string); + +protected: + bool Open(const char* iHost, const char* iUser, const char* iPassword, const char* iDatabase, int32 iPort, int32* errnum = 0, char* errbuf = 0, bool iCompress = false, bool iSSL = false); + bool ReadDBINI(char *host, char *user, char *pass, char *db, unsigned int* port, bool* compress, bool *items); +private: + bool Open(int32* errnum = 0, char* errbuf = 0); + + MYSQL mysql; + Mutex MDatabase; + eStatus pStatus; + + char* pHost; + char* pUser; + char* pPassword; + char* pDatabase; + bool pCompress; + int32 pPort; + bool pSSL; +}; +#endif + + diff --git a/old/debug.cpp b/old/debug.cpp new file mode 100644 index 0000000..3f2f98b --- /dev/null +++ b/old/debug.cpp @@ -0,0 +1,336 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ + +/* + JA: File rendered obsolete (2011-08-12) + +#include "debug.h" + +#include +using namespace std; +#include +#include +#ifdef WIN32 + #include + + #define snprintf _snprintf + #define vsnprintf _vsnprintf + #define strncasecmp _strnicmp + #define strcasecmp _stricmp +#else + #include + #include + #include +#endif +#include "../common/MiscFunctions.h" + +EQEMuLog* LogFile = new EQEMuLog; +AutoDelete adlf(&LogFile); + +static const char* FileNames[EQEMuLog::MaxLogID] = { "logs/eq2emu", "logs/eq2emu", "logs/eq2emu_error", "logs/eq2emu_debug", "logs/eq2emu_quest", "logs/eq2emu_commands" }; +static const char* LogNames[EQEMuLog::MaxLogID] = { "Status", "Normal", "Error", "Debug", "Quest", "Command" }; + +EQEMuLog::EQEMuLog() { + for (int i=0; i= 2 + pLogStatus[i] = 1 | 2; +#else + pLogStatus[i] = 0; +#endif + logCallbackFmt[i] = NULL; + logCallbackBuf[i] = NULL; + } +#if EQDEBUG < 2 + pLogStatus[Status] = 3; + pLogStatus[Error] = 3; + pLogStatus[Debug] = 3; + pLogStatus[Quest] = 2; + pLogStatus[Commands] = 2; +#endif +} + +EQEMuLog::~EQEMuLog() { + for (int i=0; i= MaxLogID) { + return false; + } + LockMutex lock(&MOpen); + if (pLogStatus[id] & 4) { + return false; + } + if (fp[id]) { + return true; + } + + char exename[200] = ""; +#if defined(WORLD) + snprintf(exename, sizeof(exename), "_world"); +#elif defined(ZONE) + snprintf(exename, sizeof(exename), "_zone"); +#endif + char filename[200]; +#ifndef NO_PIDLOG + snprintf(filename, sizeof(filename), "%s%s_%04i.log", FileNames[id], exename, getpid()); +#else + snprintf(filename, sizeof(filename), "%s%s.log", FileNames[id], exename); +#endif + fp[id] = fopen(filename, "a"); + if (!fp[id]) { + cerr << "Failed to open log file: " << filename << endl; + pLogStatus[id] |= 4; // set file state to error + return false; + } + fputs("---------------------------------------------\n",fp[id]); + return true; +} + +bool EQEMuLog::write(LogIDs id, const char *fmt, ...) { + char buffer[4096]; + + if (!this) { + return false; + } + if (id >= MaxLogID) { + return false; + } + bool dofile = false; + if (pLogStatus[id] & 1) { + dofile = open(id); + } + if (!(dofile || pLogStatus[id] & 2)) + return false; + LockMutex lock(&MLog[id]); + + time_t aclock; + struct tm *newtime; + + time( &aclock ); //Get time in seconds + newtime = localtime( &aclock ); //Convert time to struct + + if (dofile){ +#ifndef NO_PIDLOG + fprintf(fp[id], "[%04d%02d%02d %02d:%02d:%02d] ", newtime->tm_year+1900, newtime->tm_mon+1, newtime->tm_mday, newtime->tm_hour, newtime->tm_min, newtime->tm_sec); +#else + fprintf(fp[id], "%04i [%04d%02d%02d %02d:%02d:%02d] ", getpid(), newtime->tm_year+1900, newtime->tm_mon+1, newtime->tm_mday, newtime->tm_hour, newtime->tm_min, newtime->tm_sec); +#endif + } + + va_list argptr; + va_start(argptr, fmt); + vsnprintf(buffer, sizeof(buffer), fmt, argptr); + va_end(argptr); + if (dofile) + fprintf(fp[id], "%s\n", buffer); + if(logCallbackFmt[id]) { + msgCallbackFmt p = logCallbackFmt[id]; + p(id, fmt, argptr ); + } + + if (pLogStatus[id] & 2) { + if (pLogStatus[id] & 8) { + fprintf(stderr, "[%04d%02d%02d %02d:%02d:%02d] [%s] ", newtime->tm_year+1900, newtime->tm_mon+1, newtime->tm_mday, newtime->tm_hour, newtime->tm_min, newtime->tm_sec, LogNames[id]); + fprintf(stderr, "%s\n", buffer); + } + else { + fprintf(stdout, "[%04d%02d%02d %02d:%02d:%02d] [%s] ", newtime->tm_year+1900, newtime->tm_mon+1, newtime->tm_mday, newtime->tm_hour, newtime->tm_min, newtime->tm_sec, LogNames[id]); + fprintf(stdout, "%s\n", buffer); + } + } + if (dofile) + fprintf(fp[id], "\n"); + if (pLogStatus[id] & 2) { + if (pLogStatus[id] & 8) + fprintf(stderr, "\n"); + else + fprintf(stdout, "\n"); + } + if(dofile) + fflush(fp[id]); + return true; +} + +bool EQEMuLog::writebuf(LogIDs id, const char *buf, int8 size, int32 count) { + if (!this) { + return false; + } + if (id >= MaxLogID) { + return false; + } + bool dofile = false; + if (pLogStatus[id] & 1) { + dofile = open(id); + } + if (!(dofile || pLogStatus[id] & 2)) + return false; + LockMutex lock(&MLog[id]); + + time_t aclock; + struct tm *newtime; + + time( &aclock ); // Get time in seconds + newtime = localtime( &aclock ); // Convert time to struct + + if (dofile){ +#ifndef NO_PIDLOG + fprintf(fp[id], "[%02d.%02d. - %02d:%02d:%02d] ", newtime->tm_mon+1, newtime->tm_mday, newtime->tm_hour, newtime->tm_min, newtime->tm_sec); +#else + fprintf(fp[id], "%04i [%02d.%02d. - %02d:%02d:%02d] ", getpid(), newtime->tm_mon+1, newtime->tm_mday, newtime->tm_hour, newtime->tm_min, newtime->tm_sec); +#endif + } + + if (dofile) { + fwrite(buf, size, count, fp[id]); + fprintf(fp[id], "\n"); + } + if(logCallbackBuf[id]) { + msgCallbackBuf p = logCallbackBuf[id]; + p(id, buf, size, count); + } + if (pLogStatus[id] & 2) { + if (pLogStatus[id] & 8) { + fprintf(stderr, "[%s] ", LogNames[id]); + fwrite(buf, size, count, stderr); + fprintf(stderr, "\n"); + } else { + fprintf(stdout, "[%s] ", LogNames[id]); + fwrite(buf, size, count, stdout); + fprintf(stdout, "\n"); + } + } + if(dofile) + fflush(fp[id]); + return true; +} + +bool EQEMuLog::writeNTS(LogIDs id, bool dofile, const char *fmt, ...) { + char buffer[4096]; + va_list argptr; + va_start(argptr, fmt); + vsnprintf(buffer, sizeof(buffer), fmt, argptr); + va_end(argptr); + if (dofile) + fprintf(fp[id], "%s\n", buffer); + if (pLogStatus[id] & 2) { + if (pLogStatus[id] & 8) + fprintf(stderr, "%s\n", buffer); + else + fprintf(stdout, "%s\n", buffer); + } + return true; +}; + +bool EQEMuLog::Dump(LogIDs id, int8* data, int32 size, int32 cols, int32 skip) { + if (!this) { +#if EQDEBUG >= 10 + cerr << "Error: Dump() from null pointer"<= MaxLogID) + return false; + bool dofile = false; + if (pLogStatus[id] & 1) { + dofile = open(id); + } + if (!(dofile || pLogStatus[id] & 2)) + return false; + LockMutex lock(&MLog[id]); + write(id, "Dumping Packet: %i", size); + // Output as HEX + int j = 0; char* ascii = new char[cols+1]; memset(ascii, 0, cols+1); + int32 i; + for(i=skip; i= 32 && data[i] < 127) + ascii[j++] = data[i]; + else + ascii[j++] = '.'; + } + int32 k = ((i-skip)-1)%cols; + if (k < 8) + writeNTS(id, dofile, " "); + for (int32 h = k+1; h < cols; h++) { + writeNTS(id, dofile, " "); + } + writeNTS(id, dofile, " | %s\n", ascii); + if (dofile) + fflush(fp[id]); + safe_delete_array(ascii); + return true; +} + +void EQEMuLog::SetCallback(LogIDs id, msgCallbackFmt proc) { + if (!this) + return; + if (id >= MaxLogID) { + return; + } + logCallbackFmt[id] = proc; +} + +void EQEMuLog::SetCallback(LogIDs id, msgCallbackBuf proc) { + if (!this) + return; + if (id >= MaxLogID) { + return; + } + logCallbackBuf[id] = proc; +} + +void EQEMuLog::SetAllCallbacks(msgCallbackFmt proc) { + if (!this) + return; + int r; + for(r = Status; r < MaxLogID; r++) { + SetCallback((LogIDs)r, proc); + } +} + +void EQEMuLog::SetAllCallbacks(msgCallbackBuf proc) { + if (!this) + return; + int r; + for(r = Status; r < MaxLogID; r++) { + SetCallback((LogIDs)r, proc); + } +} +*/ \ No newline at end of file diff --git a/old/debug.h b/old/debug.h new file mode 100644 index 0000000..7422d5e --- /dev/null +++ b/old/debug.h @@ -0,0 +1,143 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#ifndef EQDEBUG_H +#define EQDEBUG_H + +// Debug Levels +/* + 1 = Normal + 3 = Some extended debug info + 5 = Light DETAIL info + 7 = Heavy DETAIL info + 9 = DumpPacket/PrintPacket + You should use even numbers too, to define any subset of the above basic template +*/ +#ifndef EQDEBUG + #define EQDEBUG 1 +#endif + + +#if defined(DEBUG) && defined(WIN32) + //#ifndef _CRTDBG_MAP_ALLOC + #include + #include + #if (_MSC_VER < 1300) + #include + #include + #define _CRTDBG_MAP_ALLOC + #define new new(_NORMAL_BLOCK, __FILE__, __LINE__) + #define malloc(s) _malloc_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__) + #endif + //#endif +#endif + +#ifndef ThrowError + void CatchSignal(int); + #if defined(CATCH_CRASH) || defined(_EQDEBUG) + #define ThrowError(errstr) { cout << "Fatal error: " << errstr << " (" << __FILE__ << ", line " << __LINE__ << ")" << endl; LogWrite(WORLD__ERROR, 0, "Debug", "Thrown Error: %s (%s:%i)", errstr, __FILE__, __LINE__); throw errstr; } + #else + #define ThrowError(errstr) { cout << "Fatal error: " << errstr << " (" << __FILE__ << ", line " << __LINE__ << ")" << endl; LogWrite(WORLD__ERROR, 0, "Debug", "Thrown Error: %s (%s:%i)", errstr, __FILE__, __LINE__); CatchSignal(0); } + #endif +#endif + +#ifdef WIN32 + // VS6 doesn't like the length of STL generated names: disabling + #pragma warning(disable:4786) +#endif + +#ifndef WIN32 + #define DebugBreak() if(0) {} +#endif + +#ifdef WIN32 + #include + #include +#endif + +#include "../common/Mutex.h" +#include +#include + + +class EQEMuLog { +public: + EQEMuLog(); + ~EQEMuLog(); + + enum LogIDs { + Status = 0, //this must stay the first entry in this list + Normal, + Error, + Debug, + Quest, + Commands, + MaxLogID + }; + + //these are callbacks called for each + typedef void (* msgCallbackBuf)(LogIDs id, const char *buf, int8 size, int32 count); + typedef void (* msgCallbackFmt)(LogIDs id, const char *fmt, va_list ap); + + void SetAllCallbacks(msgCallbackFmt proc); + void SetAllCallbacks(msgCallbackBuf proc); + void SetCallback(LogIDs id, msgCallbackFmt proc); + void SetCallback(LogIDs id, msgCallbackBuf proc); + + bool writebuf(LogIDs id, const char *buf, int8 size, int32 count); + bool write(LogIDs id, const char *fmt, ...); + bool Dump(LogIDs id, int8* data, int32 size, int32 cols=16, int32 skip=0); +private: + bool open(LogIDs id); + bool writeNTS(LogIDs id, bool dofile, const char *fmt, ...); // no error checking, assumes is open, no locking, no timestamp, no newline + + Mutex MOpen; + Mutex MLog[MaxLogID]; + FILE* fp[MaxLogID]; +/* LogStatus: bitwise variable + 1 = output to file + 2 = output to stdout + 4 = fopen error, dont retry + 8 = use stderr instead (2 must be set) +*/ + int8 pLogStatus[MaxLogID]; + + msgCallbackFmt logCallbackFmt[MaxLogID]; + msgCallbackBuf logCallbackBuf[MaxLogID]; +}; + +//extern EQEMuLog* LogFile; + +#ifdef _EQDEBUG +class PerformanceMonitor { +public: + PerformanceMonitor(sint64* ip) { + p = ip; + QueryPerformanceCounter(&tmp); + } + ~PerformanceMonitor() { + LARGE_INTEGER tmp2; + QueryPerformanceCounter(&tmp2); + *p += tmp2.QuadPart - tmp.QuadPart; + } + LARGE_INTEGER tmp; + sint64* p; +}; +#endif +#endif diff --git a/old/emu_opcodes.cpp b/old/emu_opcodes.cpp new file mode 100644 index 0000000..a07135e --- /dev/null +++ b/old/emu_opcodes.cpp @@ -0,0 +1,39 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ + +#include "emu_opcodes.h" + +const char *OpcodeNames[_maxEmuOpcode+1] = { + "OP_Unknown", + +//a preprocessor hack so we dont have to maintain two lists +#define N(x) #x +#if !defined(LOGIN) + #include "emu_oplist.h" +#endif +#ifdef LOGIN + #include "login_oplist.h" +#endif +#undef N + + "" +}; + + diff --git a/old/emu_opcodes.h b/old/emu_opcodes.h new file mode 100644 index 0000000..9011de1 --- /dev/null +++ b/old/emu_opcodes.h @@ -0,0 +1,56 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#ifndef EMU_OPCODES_H +#define EMU_OPCODES_H + +//this is the highest opcode possibly used in the regular EQ protocol +#define MAX_EQ_OPCODE 0xFFFF +/* + + +the list of opcodes is in emu_oplist.h + +we somewhat rely on the fact that we have more than 255 opcodes, +so we know the enum type for the opcode defines must be at least +16 bits, so we can use the protocol flags on them. + +*/ + +typedef enum { //EQEmu internal opcodes list + OP_Unknown=0, + +//a preprocessor hack so we dont have to maintain two lists +#define N(x) x +#if !defined(LOGIN) + #include "emu_oplist.h" +#endif +#ifdef LOGIN + #include "login_oplist.h" +#endif +#undef N + + _maxEmuOpcode +} EmuOpcode; + +extern const char *OpcodeNames[_maxEmuOpcode+1]; + +#endif + + diff --git a/old/emu_oplist.h b/old/emu_oplist.h new file mode 100644 index 0000000..1784af6 --- /dev/null +++ b/old/emu_oplist.h @@ -0,0 +1,505 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +N(OP_LoginReplyMsg), +N(OP_LoginByNumRequestMsg), +N(OP_WSLoginRequestMsg), +N(OP_ESInitMsg), +N(OP_ESReadyForClientsMsg), +N(OP_CreateZoneInstanceMsg), +N(OP_ZoneInstanceCreateReplyMsg), +N(OP_ZoneInstanceDestroyedMsg), +N(OP_ExpectClientAsCharacterRequest), +N(OP_ExpectClientAsCharacterReplyMs), +N(OP_ZoneInfoMsg), +N(OP_CreateCharacterRequestMsg), +N(OP_DoneLoadingZoneResourcesMsg), +N(OP_DoneSendingInitialEntitiesMsg), +N(OP_DoneLoadingEntityResourcesMsg), +N(OP_DoneLoadingUIResourcesMsg), +N(OP_PredictionUpdateMsg), +N(OP_RemoteCmdMsg), +N(OP_SetRemoteCmdsMsg), +N(OP_GameWorldTimeMsg), +N(OP_MOTDMsg), +N(OP_ZoneMOTDMsg), +N(OP_GuildRecruitingMemberInfo), +N(OP_GuildRecruiting), +N(OP_GuildRecruitingDetails), +N(OP_GuildRecruitingImage), +N(OP_AvatarCreatedMsg), +N(OP_AvatarDestroyedMsg), +N(OP_RequestCampMsg), +N(OP_MapRequest), +N(OP_CampStartedMsg), +N(OP_CampAbortedMsg), +N(OP_WhoQueryRequestMsg), +N(OP_WhoQueryReplyMsg), +N(OP_MonitorReplyMsg), +N(OP_MonitorCharacterListMsg), +N(OP_MonitorCharacterListRequestMsg), +N(OP_ClientCmdMsg), +N(OP_Lottery), +N(OP_DispatchClientCmdMsg), +N(OP_DispatchESMsg), +N(OP_UpdateTargetMsg), +N(OP_UpdateOpportunityMsg), +N(OP_UpdateTargetLocMsg), +N(OP_UpdateCharacterSheetMsg), +N(OP_UpdateSpellBookMsg), +N(OP_UpdateInventoryMsg), +N(OP_UpdateRecipeBookMsg), +N(OP_RequestRecipeDetailsMsg), +N(OP_RecipeDetailsMsg), +N(OP_UpdateSkillBookMsg), +N(OP_UpdateSkillsMsg), +N(OP_ChangeZoneMsg), +N(OP_ClientTeleportRequestMsg), +N(OP_TeleportWithinZoneMsg), +N(OP_TeleportWithinZoneNoReloadMsg), +N(OP_MigrateClientToZoneRequestMsg), +N(OP_MigrateClientToZoneReplyMsg), +N(OP_ReadyToZoneMsg), +//N(OP_AddClientToGroupMsg), +//N(OP_AddGroupToGroupMsg), +N(OP_RemoveClientFromGroupMsg), +N(OP_RemoveGroupFromGroupMsg), +N(OP_MakeGroupLeaderMsg), +N(OP_GroupCreatedMsg), +N(OP_GroupDestroyedMsg), +N(OP_GroupMemberAddedMsg), +N(OP_GroupMemberRemovedMsg), +N(OP_GroupRemovedFromGroupMsg), +N(OP_GroupLeaderChangedMsg), +N(OP_GroupSettingsChangedMsg), +N(OP_SendLatestRequestMsg), +N(OP_ClearDataMsg), +N(OP_SetSocialMsg), +N(OP_ESStatusMsg), +N(OP_ESZoneInstanceStatusMsg), +N(OP_ZonesStatusRequestMsg), +N(OP_ZonesStatusMsg), +N(OP_ESWeatherRequestMsg), +N(OP_ESWeatherRequestEndMsg), +//N(OP_WSWeatherUpdateMsg), +N(OP_DialogSelectMsg), +N(OP_DialogCloseMsg), +N(OP_RemoveSpellEffectMsg), +N(OP_RemoveConcentrationMsg), +N(OP_QuestJournalOpenMsg), +N(OP_QuestJournalInspectMsg), +//N(OP_SkillSlotMapping), +N(OP_QuestJournalSetVisibleMsg), +N(OP_QuestJournalWaypointMsg), +N(OP_CreateGuildRequestMsg), +N(OP_CreateGuildReplyMsg), +N(OP_GuildsayMsg), +//N(OP_GuildKickMsg), +N(OP_GuildUpdateMsg), +N(OP_FellowshipExpMsg), +N(OP_ConsignmentCloseStoreMsg), +N(OP_ConsignItemRequestMsg), +N(OP_ConsignItemResponseMsg), +//N(OP_PurchaseConsignmentRequestMsg), +N(OP_PurchaseConsignmentLoreCheckRe), +N(OP_QuestReward), +//N(OP_PurchaseConsignmentResponseMsg), +//N(OP_ProcessScriptMsg), +//N(OP_ProcessWorkspaceMsg), +N(OP_HouseDeletedRemotelyMsg), +N(OP_UpdateHouseDataMsg), +N(OP_UpdateHouseAccessDataMsg), +N(OP_PlayerHouseBaseScreenMsg), +N(OP_PlayerHousePurchaseScreenMsg), +N(OP_PlayerHouseAccessUpdateMsg), +N(OP_PlayerHouseDisplayStatusMsg), +N(OP_PlayerHouseCloseUIMsg), +N(OP_BuyPlayerHouseMsg), +N(OP_BuyPlayerHouseTintMsg), +N(OP_CollectAllHouseItemsMsg), +N(OP_RelinquishHouseMsg), +N(OP_EnterHouseMsg), +N(OP_ExitHouseMsg), +N(OP_ExamineConsignmentRequestMsg), +N(OP_MoveableObjectPlacementCriteri), +N(OP_EnterMoveObjectModeMsg), +N(OP_PositionMoveableObject), +N(OP_CancelMoveObjectModeMsg), +N(OP_ShaderCustomizationMsg), +N(OP_ReplaceableSubMeshesMsg), +N(OP_ExamineConsignmentResponseMsg), +N(OP_HouseDefaultAccessSetMsg), +N(OP_HouseAccessSetMsg), +N(OP_HouseAccessRemoveMsg), +N(OP_PayHouseUpkeepMsg), +N(OP_TintWidgetsMsg), +N(OP_UISettingsResponseMsg), +N(OP_UIResetMsg), +N(OP_KeymapLoadMsg), +N(OP_KeymapNoneMsg), +N(OP_KeymapDataMsg), +N(OP_KeymapSaveMsg), +N(OP_DispatchSpellCmdMsg), +N(OP_HouseCustomizationScreenMsg), +N(OP_CustomizationPurchaseRequestMs), +N(OP_CustomizationSetRequestMsg), +N(OP_CustomizationReplyMsg), +N(OP_EntityVerbsRequestMsg), +N(OP_EntityVerbsReplyMsg), +N(OP_EntityVerbsVerbMsg), +N(OP_ChatRelationshipUpdateMsg), +N(OP_ChatCreateChannelMsg), +N(OP_ChatJoinChannelMsg), +N(OP_ChatWhoChannelMsg), +N(OP_ChatLeaveChannelMsg), +N(OP_ChatTellChannelMsg), +N(OP_ChatTellUserMsg), +N(OP_ChatToggleFriendMsg), +N(OP_ChatToggleIgnoreMsg), +N(OP_ChatSendFriendsMsg), +N(OP_ChatSendIgnoresMsg), +N(OP_ChatFiltersMsg), +N(OP_LootItemsRequestMsg), +N(OP_StoppedLootingMsg), +N(OP_SitMsg), +N(OP_StandMsg), +N(OP_SatMsg), +N(OP_StoodMsg), +//N(OP_QuickbarAddMsg), +N(OP_DefaultGroupOptionsRequestMsg), +N(OP_DefaultGroupOptionsMsg), +N(OP_GroupOptionsMsg), +N(OP_DisplayGroupOptionsScreenMsg), +N(OP_DisplayInnVisitScreenMsg), +N(OP_DumpSchedulerMsg), +//N(OP_LSRequestPlayerDescMsg), +N(OP_LSCheckAcctLockMsg), +N(OP_WSAcctLockStatusMsg), +N(OP_RequestHelpRepathMsg), +N(OP_UpdateMotdMsg), +N(OP_RequestTargetLocMsg), +N(OP_PerformPlayerKnockbackMsg), +N(OP_PerformCameraShakeMsg), +N(OP_PopulateSkillMapsMsg), +N(OP_CancelledFeignMsg), +N(OP_SignalMsg), +N(OP_SkillInfoRequest), +N(OP_SkillInfoResponse), +N(OP_ShowCreateFromRecipeUIMsg), +N(OP_CancelCreateFromRecipeMsg), +N(OP_BeginItemCreationMsg), +N(OP_StopItemCreationMsg), +N(OP_ShowItemCreationProcessUIMsg), +N(OP_UpdateItemCreationProcessUIMsg), +N(OP_DisplayTSEventReactionMsg), +N(OP_ShowRecipeBookMsg), +N(OP_KnowledgebaseRequestMsg), +N(OP_KnowledgebaseResponseMsg), +N(OP_CSTicketHeaderRequestMsg), +N(OP_CSTicketInfoMsg), +N(OP_CSTicketCommentRequestMsg), +N(OP_CSTicketCommentResponseMsg), +N(OP_CSTicketCreateMsg), +N(OP_CSTicketAddCommentMsg), +N(OP_CSTicketDeleteMsg), +N(OP_CSTicketChangeNotificationMsg), +N(OP_WorldDataUpdateMsg), +N(OP_WorldDataChangeMsg), +N(OP_KnownLanguagesMsg), +N(OP_LsRequestClientCrashLogMsg), +N(OP_LsClientBaselogReplyMsg), +N(OP_LsClientCrashlogReplyMsg), +N(OP_LsClientAlertlogReplyMsg), +N(OP_LsClientVerifylogReplyMsg), +N(OP_ClientTeleportToLocationMsg), +N(OP_UpdateClientPredFlagsMsg), +N(OP_ChangeServerControlFlagMsg), +N(OP_CSToolsRequestMsg), +N(OP_CSToolsResponseMsg), +N(OP_CreateBoatTransportsMsg), +N(OP_PositionBoatTransportMsg), +N(OP_MigrateBoatTransportMsg), +N(OP_MigrateBoatTransportReplyMsg), +N(OP_DisplayDebugNLLPointsMsg), +N(OP_ExamineInfoRequestMsg), +N(OP_QuickbarInitMsg), +N(OP_QuickbarUpdateMsg), +N(OP_MacroInitMsg), +N(OP_MacroUpdateMsg), +N(OP_QuestionnaireMsg), +N(OP_LevelChangedMsg), +N(OP_SpellGainedMsg), +N(OP_EncounterBrokenMsg), +N(OP_OnscreenMsgMsg), +N(OP_DisplayWarningMsg), +N(OP_ModifyGuildMsg), +N(OP_GuildEventMsg), +N(OP_GuildEventAddMsg), +N(OP_GuildEventActionMsg), +N(OP_GuildEventListMsg), +N(OP_RequestGuildEventDetailsMsg), +N(OP_GuildEventDetailsMsg), +N(OP_RequestGuildBankEventDetailsMs), +N(OP_GuildBankUpdateMsg), +N(OP_RewardPackMsg), +N(OP_RenameGuildMsg), +N(OP_ZoneToFriendRequestMsg), +N(OP_ZoneToFriendReplyMsg), +N(OP_WaypointRequestMsg), +N(OP_WaypointReplyMsg), +N(OP_WaypointSelectMsg), +N(OP_WaypointUpdateMsg), +N(OP_CharNameChangedMsg), +N(OP_ShowZoneTeleporterDestinations), +N(OP_SelectZoneTeleporterDestinatio), +N(OP_ReloadLocalizedTxtMsg), +N(OP_RequestGuildMembershipMsg), +N(OP_GuildMembershipResponseMsg), +N(OP_LeaveGuildNotifyMsg), +N(OP_JoinGuildNotifyMsg), +N(OP_RequestGuildInfoMsg), +N(OP_GuildBankEventListMsg), +N(OP_AvatarUpdateMsg), +N(OP_BioUpdateMsg), +N(OP_InspectPlayerMsg), +N(OP_WSServerLockMsg), +N(OP_WSServerHideMsg), +N(OP_LSServerLockMsg), +N(OP_CsCategoryRequestMsg), +N(OP_CsCategoryResponseMsg), +N(OP_KnowledgeWindowSlotMappingMsg), +N(OP_LFGUpdateMsg), +N(OP_AFKUpdateMsg), +N(OP_AnonUpdateMsg), +N(OP_UpdateActivePublicZonesMsg), +N(OP_UnknownNpcMsg), +N(OP_PromoFlagsDetailsMsg), +N(OP_ConsignViewCreateMsg), +N(OP_ConsignViewGetPageMsg), +N(OP_ConsignViewReleaseMsg), +N(OP_UpdateDebugRadiiMsg), +N(OP_ConsignRemoveItemsMsg), +//N(OP_SnoopMsg), +N(OP_ReportMsg), +N(OP_UpdateRaidMsg), +N(OP_ConsignViewSortMsg), +N(OP_TitleUpdateMsg), +N(OP_FlightPathsMsg), +N(OP_ClientFellMsg), +N(OP_ClientInDeathRegionMsg), +N(OP_CampClientMsg), +N(OP_GetAvatarAccessRequestForCSToo), +N(OP_CSToolAccessResponseMsg), +N(OP_DeleteGuildMsg), +N(OP_TrackingUpdateMsg), +N(OP_BeginTrackingMsg), +N(OP_StopTrackingMsg), +N(OP_AdvancementRequestMsg), +N(OP_MapFogDataInitMsg), +N(OP_MapFogDataUpdateMsg), +//N(OP_UpdateAvgFrameTimeMsg), +N(OP_CloseGroupInviteWindowMsg), +N(OP_UpdateGroupMemberDataMsg), +N(OP_WorldPingMsg), +N(OP_MoveLogUpdateMsg), +N(OP_OfferQuestMsg), +//N(OP_MailGetHeadersMsg), +N(OP_MailGetMessageMsg), +N(OP_MailSendMessageMsg), +N(OP_MailDeleteMessageMsg), +N(OP_MailGetHeadersReplyMsg), +N(OP_MailGetMessageReplyMsg), +N(OP_MailSendMessageReplyMsg), +N(OP_MailCommitSendMessageMsg), +N(OP_MailSendSystemMessageMsg), +N(OP_MailRemoveAttachFromMailMsg), +N(OP_WorldShutdownUpdateMsg), +N(OP_ClientIdleBeginMsg), +N(OP_ClientIdleEndMsg), +N(OP_DisplayMailScreenMsg), +N(OP_NotifyApprenticeStoppedMentori), +N(OP_CorruptedClientMsg), +N(OP_MailEventNotificationMsg), +N(OP_RestartZoneMsg), +N(OP_CharTransferStartRequestMsg), +N(OP_CharTransferStartReplyMsg), +N(OP_CharTransferRequestMsg), +N(OP_CharTransferReplyMsg), +N(OP_CharTransferRollbackRequestMsg), +N(OP_CharTransferCommitRequestMsg), +N(OP_CharTransferRollbackReplyMsg), +N(OP_CharTransferCommitReplyMsg), +N(OP_GetCharacterSerializedRequestM), +N(OP_GetCharacterSerializedReplyMsg), +N(OP_CreateCharFromCBBRequestMsg), +N(OP_CreateCharFromCBBReplyMsg), +N(OP_HousingDataChangedMsg), +N(OP_HousingRestoreMsg), +N(OP_AuctionItem), +N(OP_AuctionItemReply), +N(OP_AuctionCoin), +N(OP_AuctionCoinReply), +N(OP_AuctionCharacter), +N(OP_AuctionCharacterReply), +N(OP_AuctionCommitMsg), +N(OP_AuctionAbortMsg), +N(OP_CharTransferValidateRequestMsg), +N(OP_CharTransferValidateReplyMsg), +N(OP_CharacterLinkdeadMsg), +N(OP_RaceRestrictionMsg), +N(OP_SetInstanceDisplayNameMsg), +N(OP_EqHearChatCmd), +N(OP_EqDisplayTextCmd), +N(OP_EqCreateGhostCmd), +N(OP_EqCreateWidgetCmd), +N(OP_EqCreateSignWidgetCmd), +N(OP_EqDestroyGhostCmd), +N(OP_EqUpdateGhostCmd), +N(OP_EqSetControlGhostCmd), +N(OP_EqSetPOVGhostCmd), +N(OP_EqHearCombatCmd), +N(OP_EqHearSpellCastCmd), +N(OP_EqHearSpellInterruptCmd), +N(OP_EqHearSpellFizzleCmd), +N(OP_EqHearConsiderCmd), +N(OP_EqUpdateSubClassesCmd), +N(OP_EqCreateListBoxCmd), +N(OP_EqSetDebugPathPointsCmd), +N(OP_EqCannedEmoteCmd), +N(OP_EqStateCmd), +N(OP_EqPlaySoundCmd), +N(OP_EqPlaySound3DCmd), +N(OP_EqPlayVoiceCmd), +N(OP_EqHearDrowningCmd), +N(OP_EqHearDeathCmd), +N(OP_EqGroupMemberRemovedCmd), +N(OP_EqHearChainEffectCmd), +N(OP_EqReceiveOfferCmd), +N(OP_EqInspectPCResultsCmd), +N(OP_EqDrawablePathGraphCmd), +N(OP_EqDialogOpenCmd), +N(OP_EqDialogCloseCmd), +N(OP_EqCollectionUpdateCmd), +N(OP_EqCollectionFilterCmd), +N(OP_EqCollectionItemCmd), +N(OP_EqQuestJournalUpdateCmd), +N(OP_EqQuestJournalReplyCmd), +N(OP_EqQuestGroupCmd), +N(OP_EqUpdateMerchantCmd), +N(OP_EqUpdateStoreCmd), +N(OP_EqUpdatePlayerTradeCmd), +N(OP_EqHelpPathCmd), +N(OP_EqHelpPathClearCmd), +N(OP_EqUpdateBankCmd), +N(OP_EqExamineInfoCmd), +N(OP_EqCloseWindowCmd), +N(OP_EqUpdateLootCmd), +N(OP_EqJunctionListCmd), +N(OP_EqShowDeathWindowCmd), +N(OP_EqDisplaySpellFailCmd), +N(OP_EqSpellCastStartCmd), +N(OP_EqSpellCastEndCmd), +N(OP_EqResurrectedCmd), +N(OP_EqChoiceWinCmd), +N(OP_EqSetDefaultVerbCmd), +N(OP_EqInstructionWindowCmd), +N(OP_EqInstructionWindowCloseCmd), +N(OP_EqInstructionWindowGoalCmd), +N(OP_EqInstructionWindowTaskCmd), +N(OP_EqEnableGameEventCmd), +N(OP_EqShowWindowCmd), +N(OP_EqEnableWindowCmd), +N(OP_EqFlashWindowCmd), +N(OP_EqHearPlayFlavorCmd), +N(OP_EqUpdateSignWidgetCmd), +N(OP_EqDebugPVDCmd), +N(OP_EqShowBookCmd), +N(OP_EqQuestionnaireCmd), +N(OP_EqGetProbsCmd), +N(OP_EqHearHealCmd), +N(OP_EqChatChannelUpdateCmd), +N(OP_EqWhoChannelQueryReplyCmd), +N(OP_EqAvailWorldChannelsCmd), +N(OP_ArenaGameTypesMsg), +N(OP_EqUpdateTargetCmd), +N(OP_EqConsignmentItemsCmd), +N(OP_EqStartBrokerCmd), +N(OP_EqMapExplorationCmd), +N(OP_EqStoreLogCmd), +N(OP_EqSpellMoveToRangeAndRetryCmd), +N(OP_EqUpdatePlayerMailCmd), +N(OP_EqFactionUpdateCmd), +N(OP_UpdateTitleCmd), +N(OP_UpdatePositionMsg), +N(OP_AttackNotAllowed), +N(OP_AttackAllowed), +N(OP_CancelSpellCast), +N(OP_BadLanguageFilter), +N(OP_DressingRoom), +N(OP_TraitsList), +N(OP_PointOfInterest), +N(OP_AdventureList), +N(OP_CharacterAchievements), +N(OP_RecipeList), +N(OP_BagOptions), +N(OP_AchievementUpdateMsg), +N(OP_PetOptions), +N(OP_BrokerAddBag), +N(OP_CharacterPet), +N(OP_ClearForTakeOffMsg), +N(OP_CharacterCurrency), +N(OP_TradeskillList), +N(OP_RecipeBook), +N(OP_CharacterMerc), +N(OP_AfterInvSpellUpdate), +N(OP_CharacterCreatedDungeons), +N(OP_CharacterHousingList), +N(OP_HouseItemsList), +N(OP_CharacterMounts), +N(OP_LoadCalendarEvents), +N(OP_LoadWelcomeWindow), +N(OP_DungeonMakerItemRequest), +N(OP_SysClient), +N(OP_LFGGroupSearch), +N(OP_MarketPlacePrices), +N(OP_MarketFundsUpdate), +N(OP_MarketAddFundsRequest), +N(OP_ZoneBgInstanceList), +N(OP_UIEvent), +N(OP_Launchpad), +N(OP_EQHearThreatCmd), +N(OP_EqHearSpellNoLandCmd), +N(OP_Weakness), +N(OP_SavageBarInitMsg), +N(OP_PetOptionsResponse), +N(OP_CurrentPet), +N(OP_JournalQuestStoryline), +N(OP_DailyObjectives), +N(OP_RecipeListUnknown), +N(OP_EQHearDispellCmd), +N(OP_ClearForLandingMsg), +N(OP_LikeOption), +N(OP_HeritageMsg), +N(OP_OpenCharCust), +N(OP_PaperdollImage), +N(OP_ReadyForTakeOffMsg), +N(OP_EarlyLandingRequestMsg), +N(OP_SubmitCharCust), +N(OP_DietyAbilityWindow), +N(OP_EqTargetItemCmd), \ No newline at end of file diff --git a/old/linked_list.h b/old/linked_list.h new file mode 100644 index 0000000..023a9d2 --- /dev/null +++ b/old/linked_list.h @@ -0,0 +1,445 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#ifndef LINKEDLIST_H +#define LINKEDLIST_H + +#include "types.h" + +enum direction{FORWARD,BACKWARD}; + +template class LinkedListIterator; + +template +class ListElement +{ +private: + + TYPE data; + ListElement* next; + ListElement* prev; +public: + ListElement (); + ListElement (const TYPE&); + ListElement (const ListElement&); + + ~ListElement (); + + ListElement& operator= (const ListElement&); + + ListElement* GetLast () + { + ListElement* tmp = this; + while (tmp->GetNext()) { + tmp = tmp->GetNext(); + } + return tmp; + } + ListElement* GetNext () const { return next ; } + ListElement* GetPrev () const { return prev ; } + + inline TYPE& GetData () { return data ; } + inline const TYPE& GetData () const { return data ; } + + void SetData ( const TYPE& d ) { data = d ; } // Quagmire - this may look like a mem leak, but dont change it, this behavior is expected where it's called + void SetLastNext ( ListElement* p ) + { + GetLast()->SetNext(p); + } + void SetNext (ListElement* n) { next = n ; } + void SetPrev (ListElement* p) { prev = p ; } + + void ReplaceData(const TYPE&); +}; + +template +class LinkedList +{ +private: + int32 count; + ListElement* first; + bool list_destructor_invoked; + +public: + + LinkedList(); + ~LinkedList(); + bool dont_delete; + LinkedList& operator= (const LinkedList&); + + void Append (const TYPE&); + void Insert (const TYPE&); + TYPE Pop(); + TYPE PeekTop(); + void Clear(); + void LCount() { count--; } + void ResetCount() { count=0; } + int32 Count() { return count; } + friend class LinkedListIterator; +}; + +template +class LinkedListIterator +{ +private: + LinkedList& list; + ListElement* current_element; + direction dir; + +public: + LinkedListIterator(LinkedList& l,direction d = FORWARD) : list(l), dir(d) {}; + + void Advance(); + const TYPE& GetData(); + bool IsFirst() + { + if (current_element->GetPrev() == 0) + return true; + else + return false; + } + bool IsLast() + { + if (current_element->GetNext() == 0) + return true; + else + return false; + } + bool MoreElements(); + void MoveFirst(); + void MoveLast(); + void RemoveCurrent(bool DeleteData = true); + void Replace(const TYPE& new_data); + void Reset(); + void SetDir(direction); +}; + +template +void LinkedListIterator::Advance() +{ + if (current_element == 0) + { + return; + } + if (dir == FORWARD) + { + current_element = current_element->GetNext(); + } + else + { + current_element = current_element->GetPrev(); + } + + if (list.list_destructor_invoked) + { + while(current_element && current_element->GetData() == 0) + { +// if (current_element == 0) +// { +// return; +// } + if (dir == FORWARD) + { + current_element = current_element->GetNext(); + } + else + { + current_element = current_element->GetPrev(); + } + } + } +} + +template +bool LinkedListIterator::MoreElements() +{ + if (current_element == 0) + return false; + return true; +} + +template +const TYPE& LinkedListIterator::GetData() +{ + return current_element->GetData(); +} + +template +void LinkedListIterator::MoveFirst() +{ + ListElement* prev = current_element->GetPrev(); + ListElement* next = current_element->GetNext(); + + if (prev == 0) + { + return; + } + +// if (prev != 0) +// { + prev->SetNext(next); +// } + if (next != 0) + { + next->SetPrev(prev); + } + current_element->SetPrev(0); + current_element->SetNext(list.first); + list.first->SetPrev(current_element); + list.first = current_element; +} + + +template +void LinkedListIterator::MoveLast() +{ + ListElement* prev = current_element->GetPrev(); + ListElement* next = current_element->GetNext(); + + if (next == 0) + { + return; + } + + if (prev != 0) + { + prev->SetNext(next); + } + else + { + list.first = next; + } +// if (next != 0) +// { + next->SetPrev(prev); +// } + current_element->SetNext(0); + current_element->SetPrev(next->GetLast()); + next->GetLast()->SetNext(current_element); +} + +template +void LinkedListIterator::RemoveCurrent(bool DeleteData) +{ + ListElement* save; + + if (list.first == current_element) + { + list.first = current_element->GetNext(); + } + + if (current_element->GetPrev() != 0) + { + current_element->GetPrev()->SetNext(current_element->GetNext()); + } + if (current_element->GetNext() != 0) + { + current_element->GetNext()->SetPrev(current_element->GetPrev()); + } + if (dir == FORWARD) + { + save = current_element->GetNext(); + } + else + { + save = current_element->GetPrev(); + } + current_element->SetNext(0); + current_element->SetPrev(0); + if (!DeleteData) + current_element->SetData(0); + safe_delete(current_element); + current_element = save; + list.LCount(); +} + +template +void LinkedListIterator::Replace(const TYPE& new_data) +{ + current_element->ReplaceData(new_data); +} + +template +void LinkedListIterator::Reset() +{ + if (!(&list)) + { + current_element=0; + return; + } + + if (dir == FORWARD) + { + current_element = list.first; + } + else + { + if (list.first == 0) + { + current_element = 0; + } + else + { + current_element = list.first->GetLast(); + } + } + + if (list.list_destructor_invoked) + { + while(current_element && current_element->GetData() == 0) + { +// if (current_element == 0) +// { +// return; +// } + if (dir == FORWARD) + { + current_element = current_element->GetNext(); + } + else + { + current_element = current_element->GetPrev(); + } + } + } +} + +template +void LinkedListIterator::SetDir(direction d) +{ + dir = d; +} + +template +ListElement::ListElement(const TYPE& d) +{ + data = d; + next = 0; + prev = 0; +} + +template +ListElement::~ListElement() +{ +// cout << "ListElement::~ListElement()" << endl; + + if (data != 0) + safe_delete(data); + data = 0; + if (next != 0) + { + safe_delete(next); + next = 0; + } +} + +template +void ListElement::ReplaceData(const TYPE& new_data) +{ + if (data != 0) + safe_delete(data); + data = new_data; +} + +template +LinkedList::LinkedList() +{ + list_destructor_invoked = false; + first = 0; + count = 0; + dont_delete = false; +} + +template +LinkedList::~LinkedList() +{ + list_destructor_invoked = true; + if(!dont_delete) + Clear(); +} + +template +void LinkedList::Clear() { + while (first) { + ListElement* tmp = first; + first = tmp->GetNext(); + tmp->SetNext(0); + safe_delete(tmp); + } + ResetCount(); +} + +template +void LinkedList::Append(const TYPE& data) +{ + ListElement* new_element = new ListElement(data); + + if (first == 0) + { + first = new_element; + } + else + { + new_element->SetPrev(first->GetLast()); + first->SetLastNext(new_element); + } + count++; +} + +template +void LinkedList::Insert(const TYPE& data) +{ + ListElement* new_element = new ListElement(data); + + new_element->SetNext(first); + if (first != 0) + { + first->SetPrev(new_element); + } + first = new_element; + count++; +} + +template +TYPE LinkedList::Pop() { + TYPE ret = 0; + if (first) { + ListElement* tmpdel = first; + first = tmpdel->GetNext(); + if (first) + first->SetPrev(0); + ret = tmpdel->GetData(); + tmpdel->SetData(0); + tmpdel->SetNext(0); + safe_delete(tmpdel); + count--; + } + return ret; +} + +template +TYPE LinkedList::PeekTop() { + if (first) + return first->GetData(); + return 0; +} + +#endif + + diff --git a/old/login_oplist.h b/old/login_oplist.h new file mode 100644 index 0000000..c8bd536 --- /dev/null +++ b/old/login_oplist.h @@ -0,0 +1,61 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#if defined(LOGIN) || defined(MINILOGIN) +N(OP_LoginRequestMsg), +N(OP_LoginByNumRequestMsg), +N(OP_WSLoginRequestMsg), +N(OP_ESLoginRequestMsg), +N(OP_LoginReplyMsg), +N(OP_WorldListMsg), +N(OP_WorldStatusChangeMsg), +N(OP_AllWSDescRequestMsg), +N(OP_WSStatusReplyMsg), +N(OP_AllCharactersDescRequestMsg), +N(OP_AllCharactersDescReplyMsg), +N(OP_CreateCharacterRequestMsg), +N(OP_ReskinCharacterRequestMsg), +N(OP_CreateCharacterReplyMsg), +N(OP_WSCreateCharacterRequestMsg), +N(OP_WSCreateCharacterReplyMsg), +N(OP_DeleteCharacterRequestMsg), +N(OP_DeleteCharacterReplyMsg), +N(OP_PlayCharacterRequestMsg), +N(OP_PlayCharacterReplyMsg), +N(OP_ServerPlayCharacterRequestMsg), +N(OP_ServerPlayCharacterReplyMsg), +N(OP_KeymapLoadMsg), +N(OP_KeymapNoneMsg), +N(OP_KeymapDataMsg), +N(OP_KeymapSaveMsg), +//N(OP_LSRequestPlayerDescMsg), +N(OP_LSCheckAcctLockMsg), +N(OP_WSAcctLockStatusMsg), +N(OP_LsRequestClientCrashLogMsg), +N(OP_LsClientBaselogReplyMsg), +N(OP_LsClientCrashlogReplyMsg), +N(OP_LsClientAlertlogReplyMsg), +N(OP_LsClientVerifylogReplyMsg), +N(OP_BadLanguageFilter), +N(OP_WSServerLockMsg), +N(OP_WSServerHideMsg), +N(OP_LSServerLockMsg), +N(OP_UpdateCharacterSheetMsg), +N(OP_UpdateInventoryMsg), +#endif diff --git a/old/md5.cpp b/old/md5.cpp new file mode 100644 index 0000000..1244c8c --- /dev/null +++ b/old/md5.cpp @@ -0,0 +1,281 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#include /* for memcpy() */ +#include "../common/md5.h" +#include "../common/MiscFunctions.h" +#include "../common/seperator.h" + +MD5::MD5() { + memset(pMD5, 0, 16); +} + +MD5::MD5(const uchar* buf, uint32 len) { + Generate(buf, len, pMD5); +} + +MD5::MD5(const char* buf, uint32 len) { + Generate((const uchar*) buf, len, pMD5); +} + +MD5::MD5(const int8 buf[16]) { + Set(buf); +} + +MD5::MD5(const char* iMD5String) { + Set(iMD5String); +} + +void MD5::Generate(const char* iString) { + Generate((const uchar*) iString, strlen(iString)); +} + +void MD5::Generate(const int8* buf, uint32 len) { + Generate(buf, len, pMD5); +} + +bool MD5::Set(const int8 buf[16]) { + memcpy(pMD5, buf, 16); + return true; +} + +bool MD5::Set(const char* iMD5String) { + char tmp[5] = { '0', 'x', 0, 0, 0 }; + for (int i=0; i<16; i++) { + tmp[2] = iMD5String[i*2]; + tmp[3] = iMD5String[(i*2) + 1]; + if (!Seperator::IsHexNumber(tmp)) + return false; + pMD5[i] = hextoi(tmp); + } + return true; +} + +MD5::operator const char* () { + snprintf(pMD5String, sizeof(pMD5String), "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", pMD5[0], pMD5[1], pMD5[2], pMD5[3], pMD5[4], pMD5[5], pMD5[6], pMD5[7], pMD5[8], pMD5[9], pMD5[10], pMD5[11], pMD5[12], pMD5[13], pMD5[14], pMD5[15]); + return pMD5String; +} + +bool MD5::operator== (const MD5& iMD5) { + if (memcmp(pMD5, iMD5.pMD5, 16) == 0) + return true; + else + return false; +} + +bool MD5::operator== (const int8* iMD5) { + if (memcmp(pMD5, iMD5, 16) == 0) + return true; + else + return false; +} + +bool MD5::operator== (const char* iMD5String) { + char tmp[5] = { '0', 'x', 0, 0, 0 }; + for (int i=0; i<16; i++) { + tmp[2] = iMD5String[i*2]; + tmp[3] = iMD5String[(i*2) + 1]; + if (pMD5[i] != hextoi(tmp)) + return false; + } + return true; +} + +MD5& MD5::operator= (const MD5& iMD5) { + memcpy(pMD5, iMD5.pMD5, 16); + return *this; +} + +MD5* MD5::operator= (const MD5* iMD5) { + memcpy(pMD5, iMD5->pMD5, 16); + return this; +} + +/* Byte-swap an array of words to little-endian. (Byte-sex independent) */ +void MD5::byteSwap(uint32 *buf, uint32 words) { + int8 *p = (int8 *)buf; + do { + *buf++ = (uint32)((uint32)p[3]<<8 | p[2]) << 16 | + ((uint32)p[1]<<8 | p[0]); + p += 4; + } while (--words); +} + +void MD5::Generate(const int8* buf, uint32 len, int8 digest[16]) { + MD5Context ctx; + Init(&ctx); + Update(&ctx, buf, len); + Final(digest, &ctx); +} + +/* Start MD5 accumulation. */ +void MD5::Init(struct MD5Context *ctx) { + ctx->hash[0] = 0x67452301; + ctx->hash[1] = 0xefcdab89; + ctx->hash[2] = 0x98badcfe; + ctx->hash[3] = 0x10325476; + ctx->bytes[1] = ctx->bytes[0] = 0; +} + +/* Update ctx to reflect the addition of another buffer full of bytes. */ +void MD5::Update(struct MD5Context *ctx, int8 const *buf, uint32 len) { + uint32 t = ctx->bytes[0]; + if ((ctx->bytes[0] = t + len) < t) /* Update 64-bit byte count */ + ctx->bytes[1]++; /* Carry from low to high */ + + + + t = 64 - (t & 0x3f); /* Bytes available in ctx->input (>= 1) */ + if (t > len) { + memcpy((int8*)ctx->input+64-t, buf, len); + return; + } + /* First chunk is an odd size */ + memcpy((int8*)ctx->input+64-t, buf, t); + byteSwap(ctx->input, 16); + Transform(ctx->hash, ctx->input); + buf += t; + len -= t; + /* Process data in 64-byte chunks */ + while (len >= 64) { + memcpy(ctx->input, buf, 64); + byteSwap(ctx->input, 16); + Transform(ctx->hash, ctx->input); + buf += 64; + len -= 64; + } + /* Buffer any remaining bytes of data */ + memcpy(ctx->input, buf, len); +} + +/* Final wrapup - pad to 64-byte boundary with the bit pattern +* 1 0* (64-bit count of bits processed, LSB-first) */ +void MD5::Final(int8 digest[16], MD5Context *ctx) { + int count = ctx->bytes[0] & 0x3F; /* Bytes mod 64 */ + int8 *p = (int8*)ctx->input + count; + /* Set the first byte of padding to 0x80. There is always room. */ + *p++ = 0x80; + /* Bytes of zero padding needed to make 56 bytes (-8..55) */ + count = 56 - 1 - count; + if (count < 0) { /* Padding forces an extra block */ + memset(p, 0, count+8); + byteSwap(ctx->input, 16); + Transform(ctx->hash, ctx->input); + p = (int8*)ctx->input; + count = 56; + } + memset(p, 0, count); + byteSwap(ctx->input, 14); + /* Append 8 bytes of length in *bits* and transform */ + ctx->input[14] = ctx->bytes[0] << 3; + + ctx->input[15] = ctx->bytes[1] << 3 | ctx->bytes[0] >> 29; + Transform(ctx->hash, ctx->input); + byteSwap(ctx->hash, 4); + memcpy(digest, ctx->hash, 16); + memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */ +} + +/* The four core functions */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f,w,x,y,z,in,s) (w += f(x,y,z)+in, w = (w<>(32-s)) + x) + + + +/* The heart of the MD5 algorithm. */ +void MD5::Transform(uint32 hash[4], const uint32 input[16]) { + uint32 a = hash[0], b = hash[1], c = hash[2], d = hash[3]; + + MD5STEP(F1, a, b, c, d, input[ 0]+0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, input[ 1]+0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, input[ 2]+0x242070db, 17); + MD5STEP(F1, b, c, d, a, input[ 3]+0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, input[ 4]+0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, input[ 5]+0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, input[ 6]+0xa8304613, 17); + MD5STEP(F1, b, c, d, a, input[ 7]+0xfd469501, 22); + MD5STEP(F1, a, b, c, d, input[ 8]+0x698098d8, 7); + MD5STEP(F1, d, a, b, c, input[ 9]+0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, input[10]+0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, input[11]+0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, input[12]+0x6b901122, 7); + MD5STEP(F1, d, a, b, c, input[13]+0xfd987193, 12); + MD5STEP(F1, c, d, a, b, input[14]+0xa679438e, 17); + MD5STEP(F1, b, c, d, a, input[15]+0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, input[ 1]+0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, input[ 6]+0xc040b340, 9); + MD5STEP(F2, c, d, a, b, input[11]+0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, input[ 0]+0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, input[ 5]+0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, input[10]+0x02441453, 9); + MD5STEP(F2, c, d, a, b, input[15]+0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, input[ 4]+0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, input[ 9]+0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, input[14]+0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, input[ 3]+0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, input[ 8]+0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, input[13]+0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, input[ 2]+0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, input[ 7]+0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, input[12]+0x8d2a4c8a, 20); + + + + + MD5STEP(F3, a, b, c, d, input[ 5]+0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, input[ 8]+0x8771f681, 11); + MD5STEP(F3, c, d, a, b, input[11]+0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, input[14]+0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, input[ 1]+0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, input[ 4]+0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, input[ 7]+0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, input[10]+0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, input[13]+0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, input[ 0]+0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, input[ 3]+0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, input[ 6]+0x04881d05, 23); + MD5STEP(F3, a, b, c, d, input[ 9]+0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, input[12]+0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, input[15]+0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, input[ 2]+0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, input[ 0]+0xf4292244, 6); + MD5STEP(F4, d, a, b, c, input[ 7]+0x432aff97, 10); + MD5STEP(F4, c, d, a, b, input[14]+0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, input[ 5]+0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, input[12]+0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, input[ 3]+0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, input[10]+0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, input[ 1]+0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, input[ 8]+0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, input[15]+0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, input[ 6]+0xa3014314, 15); + MD5STEP(F4, b, c, d, a, input[13]+0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, input[ 4]+0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, input[11]+0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, input[ 2]+0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, input[ 9]+0xeb86d391, 21); + + hash[0] += a; hash[1] += b; hash[2] += c; hash[3] += d; +} diff --git a/old/md5.h b/old/md5.h new file mode 100644 index 0000000..6c54f94 --- /dev/null +++ b/old/md5.h @@ -0,0 +1,64 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#ifndef MD5_H +#define MD5_H +#include "../common/types.h" + + +class MD5 { +public: + struct MD5Context { + uint32 hash[4]; + uint32 bytes[2]; + uint32 input[16]; + }; + static void Generate(const int8* buf, uint32 len, int8 digest[16]); + + static void Init(struct MD5Context *context); + static void Update(struct MD5Context *context, const int8 *buf, uint32 len); + static void Final(int8 digest[16], struct MD5Context *context); + + MD5(); + MD5(const uchar* buf, uint32 len); + MD5(const char* buf, uint32 len); + MD5(const int8 buf[16]); + MD5(const char* iMD5String); + + void Generate(const char* iString); + void Generate(const int8* buf, uint32 len); + bool Set(const int8 buf[16]); + bool Set(const char* iMD5String); + + bool operator== (const MD5& iMD5); + bool operator== (const int8 iMD5[16]); + bool operator== (const char* iMD5String); + + MD5& operator= (const MD5& iMD5); + MD5* operator= (const MD5* iMD5); + MD5* operator= (const int8* iMD5); + operator const char* (); +protected: + int8 pMD5[16]; +private: + static void byteSwap(uint32 *buf, uint32 words); + static void Transform(uint32 hash[4], const int32 input[16]); + char pMD5String[33]; +}; +#endif diff --git a/old/misc.cpp b/old/misc.cpp new file mode 100644 index 0000000..6f5daa0 --- /dev/null +++ b/old/misc.cpp @@ -0,0 +1,305 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#ifdef WIN32 + // VS6 doesn't like the length of STL generated names: disabling + #pragma warning(disable:4786) +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include "misc.h" +#include "types.h" +using namespace std; + +#define ENC(c) (((c) & 0x3f) + ' ') +#define DEC(c) (((c) - ' ') & 0x3f) + +map DBFieldNames; + +#ifndef WIN32 +#ifdef FREEBSD +int print_stacktrace() +{ + printf("Insert stack trace here...\n"); + return(0); +} +#else //!WIN32 && !FREEBSD == linux +#include +int print_stacktrace() +{ + void *ba[20]; + int n = backtrace (ba, 20); + if (n != 0) + { + char **names = backtrace_symbols (ba, n); + if (names != NULL) + { + int i; + cerr << "called from " << (char*)names[0] << endl; + for (i = 1; i < n; ++i) + cerr << " " << (char*)names[i] << endl; + free (names); + } + } + return(0); +} +#endif //!FREEBSD +#endif //!WIN32 + +int Deflate(unsigned char* in_data, int in_length, unsigned char* out_data, int max_out_length) +{ +z_stream zstream; +int zerror; + + zstream.next_in = in_data; + zstream.avail_in = in_length; + zstream.zalloc = Z_NULL; + zstream.zfree = Z_NULL; + zstream.opaque = Z_NULL; + deflateInit(&zstream, Z_FINISH); + zstream.next_out = out_data; + zstream.avail_out = max_out_length; + zerror = deflate(&zstream, Z_FINISH); + + if (zerror == Z_STREAM_END) + { + deflateEnd(&zstream); + return zstream.total_out; + } + else + { + cout << "Error: Deflate: deflate() returned " << zerror << " '"; + if (zstream.msg) + cout << zstream.msg; + cout << "'" << endl; + zerror = deflateEnd(&zstream); + return 0; + } +} + +int Inflate(unsigned char* indata, int indatalen, unsigned char* outdata, int outdatalen, bool iQuiet) +{ +z_stream zstream; +int zerror = 0; +int i; + + zstream.next_in = indata; + zstream.avail_in = indatalen; + zstream.next_out = outdata; + zstream.avail_out = outdatalen; + zstream.zalloc = Z_NULL; + zstream.zfree = Z_NULL; + zstream.opaque = Z_NULL; + + i = inflateInit2( &zstream, 15 ); + if (i != Z_OK) { + return 0; + } + + zerror = inflate( &zstream, Z_FINISH ); + + if(zerror == Z_STREAM_END) { + inflateEnd( &zstream ); + return zstream.total_out; + } + else { + if (!iQuiet) { + cout << "Error: Inflate: inflate() returned " << zerror << " '"; + if (zstream.msg) + cout << zstream.msg; + cout << "'" << endl; + } + + if (zerror == Z_DATA_ERROR || zerror == Z_ERRNO) + return -1; + + if (zerror == Z_MEM_ERROR && zstream.msg == 0) + { + return 0; + } + + zerror = inflateEnd( &zstream ); + return 0; + } +} + +void dump_message_column(unsigned char *buffer, unsigned long length, string leader, FILE *to) +{ +unsigned long i,j; +unsigned long rows,offset=0; + rows=(length/16)+1; + for(i=0;i= 0x41 && val <=0x5A) || (val >= 0x61 && val <=0x7A)) + return true; + else + return false; +} + +unsigned int GetSpellNameCrc(const char* src) { + if (!src) + return 0; + uLong crc = crc32(0L, Z_NULL, 0); + return crc32(crc, (unsigned const char*)src, strlen(src)); +} + +int GetItemNameCrc(string item_name){ + const char *src = item_name.c_str(); + uLong crc = crc32(0L, Z_NULL, 0); + crc = crc32(crc, (unsigned const char *)src,strlen(src)) + 1; + return sint32(crc) * -1; +} + +unsigned int GetNameCrc(string name) { + const char* src = name.c_str(); + uLong crc = crc32(0L, Z_NULL, 0); + crc = crc32(crc, (unsigned const char*)src, strlen(src)) + 1; + return int32(crc)-1; +} diff --git a/old/misc.h b/old/misc.h new file mode 100644 index 0000000..4107eb9 --- /dev/null +++ b/old/misc.h @@ -0,0 +1,65 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#ifndef _MISC_H + +#define _MISC_H +#include +#include +#include + +using namespace std; + +#define ITEMFIELDCOUNT 116 + +void Unprotect(string &s, char what); + +void Protect(string &s, char what); + +bool ItemParse(const char *data, int length, map > &items, int id_pos, int name_pos, int max_field, int level=0); + +int Tokenize(string s, map & tokens, char delim='|'); + +void LoadItemDBFieldNames(); + +void encode_length(unsigned long length, char *out); +unsigned long decode_length(char *in); +unsigned long encode(char *in, unsigned long length, char *out); +void decode(char *in, char *out); +void encode_chunk(char *in, int len, char *out); +void decode_chunk(char *in, char *out); + +int Deflate(unsigned char* in_data, int in_length, unsigned char* out_data, int max_out_length); +int Inflate(unsigned char* indata, int indatalen, unsigned char* outdata, int outdatalen, bool iQuiet=true); +#ifndef WIN32 +int print_stacktrace(); +#endif + +bool alpha_check(unsigned char val); + +void dump_message_column(unsigned char *buffer, unsigned long length, string leader="", FILE *to = stdout); +string string_from_time(string pattern, time_t now=0); +string timestamp(time_t now=0); +string long2ip(unsigned long ip); +string pop_arg(string &s, string seps, bool obey_quotes); +int EQsprintf(char *buffer, const char *pattern, const char *arg1, const char *arg2, const char *arg3, const char *arg4, const char *arg5, const char *arg6, const char *arg7, const char *arg8, const char *arg9); +unsigned int GetSpellNameCrc(const char* src); +int GetItemNameCrc(string item_name); +unsigned int GetNameCrc(string name); +#endif diff --git a/old/op_codes.h b/old/op_codes.h new file mode 100644 index 0000000..c25e9a0 --- /dev/null +++ b/old/op_codes.h @@ -0,0 +1,44 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#ifndef _OP_CODES_H + +#define _OP_CODES_H + +static const char OP_SessionRequest = 0x01; +static const char OP_SessionResponse = 0x02; +static const char OP_Combined = 0x03; +static const char OP_SessionDisconnect = 0x05; +static const char OP_KeepAlive = 0x06; +static const char OP_ServerKeyRequest = 0x07; +static const char OP_SessionStatResponse= 0x08; +static const char OP_Packet = 0x09; +static const char OP_Fragment = 0x0d; +static const char OP_OutOfOrderAck = 0x11; +static const char OP_Ack = 0x15; +static const char OP_AppCombined = 0x19; +static const char OP_OutOfSession = 0x1d; + +#if defined(LOGIN) || defined(CHAT) + #define APP_OPCODE_SIZE 1 +#else + #define APP_OPCODE_SIZE 2 +#endif + +#endif diff --git a/old/opcodemgr.cpp b/old/opcodemgr.cpp new file mode 100644 index 0000000..6d57921 --- /dev/null +++ b/old/opcodemgr.cpp @@ -0,0 +1,350 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#include "debug.h" +#include +#include +#include "opcodemgr.h" +//#include "debug.h" +#include "emu_opcodes.h" +#include "../common/Log.h" + +#if defined(SHARED_OPCODES) && !defined(EQ2) + #include "EMuShareMem.h" + extern LoadEMuShareMemDLL EMuShareMemDLL; +#endif + +#include +#include +using namespace std; + + +//#define DEBUG_TRANSLATE + + +OpcodeManager::OpcodeManager() { + loaded = false; +} +bool OpcodeManager::LoadOpcodesMap(map* eq, OpcodeSetStrategy *s, std::string* missingOpcodes){ + //do the mapping and store them in the shared memory array + bool ret = true; + EmuOpcode emu_op; + map::iterator res; + //stupid enum wont let me ++ on it... + + + for(emu_op = (EmuOpcode)(0); emu_op < _maxEmuOpcode; emu_op=(EmuOpcode)(emu_op+1)) { + //get the name of this emu opcode + const char *op_name = OpcodeNames[emu_op]; + if(op_name[0] == '\0') { + break; + } + + //find the opcode in the file + res = eq->find(op_name); + if(res == eq->end()) { + if(missingOpcodes) { + if(missingOpcodes->size() < 1) { + missingOpcodes->append(op_name); + } + else { + missingOpcodes->append(", " + std::string(op_name)); + } + } + else { + LogWrite(OPCODE__WARNING, 1, "Opcode", "Opcode %s is missing from the opcodes table.", op_name); + } + s->Set(emu_op, 0xFFFF); + continue; //continue to give them a list of all missing opcodes + } + + //ship the mapping off to shared mem. + s->Set(emu_op, res->second); + } + return ret; +} +bool OpcodeManager::LoadOpcodesFile(const char *filename, OpcodeSetStrategy *s) { + FILE *opf = fopen(filename, "r"); + if(opf == NULL) { + LogWrite(OPCODE__ERROR, 0, "Opcode", "Unable to open opcodes file '%s'. Thats bad.", filename); + return(false); + } + + map eq; + + //load the opcode file into eq, could swap in a nice XML parser here + char line[2048]; + int lineno = 0; + uint16 curop; + while(!feof(opf)) { + lineno++; + line[0] = '\0'; //for blank line at end of file + if(fgets(line, sizeof(line), opf) == NULL) + break; + + //ignore any line that dosent start with OP_ + if(line[0] != 'O' || line[1] != 'P' || line[2] != '_') + continue; + + char *num = line+3; //skip OP_ + //look for the = sign + while(*num != '=' && *num != '\0') { + num++; + } + //make sure we found = + if(*num != '=') { + LogWrite(OPCODE__ERROR, 0, "Opcode", "Malformed opcode line at %s:%d\n", filename, lineno); + continue; + } + *num = '\0'; //null terminate the name + num++; //num should point to the opcode + + //read the opcode + if(sscanf(num, "0x%hx", &curop) != 1) { + LogWrite(OPCODE__ERROR, 0, "Opcode", "Malformed opcode at %s:%d\n", filename, lineno); + continue; + } + + //we have a name and our opcode... stick it in the map + eq[line] = curop; + } + fclose(opf); + return LoadOpcodesMap(&eq, s); +} + +//convenience routines +const char *OpcodeManager::EmuToName(const EmuOpcode emu_op) { + if(emu_op > _maxEmuOpcode) + return "OP_Unknown"; + + return(OpcodeNames[emu_op]); +} + +const char *OpcodeManager::EQToName(const uint16 eq_op) { + //first must resolve the eq op to an emu op + EmuOpcode emu_op = EQToEmu(eq_op); + if(emu_op > _maxEmuOpcode) + return "OP_Unknown"; + + return(OpcodeNames[emu_op]); +} + +EmuOpcode OpcodeManager::NameSearch(const char *name) { + EmuOpcode emu_op; + //stupid enum wont let me ++ on it... + for(emu_op = (EmuOpcode)(0); emu_op < _maxEmuOpcode; emu_op=(EmuOpcode)(emu_op+1)) { + //get the name of this emu opcode + const char *op_name = OpcodeNames[emu_op]; + if(!strcasecmp(op_name, name)) { + return(emu_op); + } + } + return(OP_Unknown); +} + +RegularOpcodeManager::RegularOpcodeManager() +: MutableOpcodeManager() +{ + emu_to_eq = NULL; + eq_to_emu = NULL; + EQOpcodeCount = 0; + EmuOpcodeCount = 0; +} + +RegularOpcodeManager::~RegularOpcodeManager() { + safe_delete_array(emu_to_eq); + safe_delete_array(eq_to_emu); +} + +bool RegularOpcodeManager::LoadOpcodes(map* eq, std::string* missingOpcodes) { + NormalMemStrategy s; + s.it = this; + MOpcodes.lock(); + + loaded = true; + eq_to_emu = new EmuOpcode[MAX_EQ_OPCODE]; + emu_to_eq = new uint16[_maxEmuOpcode]; + EQOpcodeCount = MAX_EQ_OPCODE; + EmuOpcodeCount = _maxEmuOpcode; + + //dont need to set eq_to_emu cause every element should get a value + memset(eq_to_emu, 0, sizeof(EmuOpcode)*MAX_EQ_OPCODE); + memset(emu_to_eq, 0xCD, sizeof(uint16)*_maxEmuOpcode); + + bool ret = LoadOpcodesMap(eq, &s, missingOpcodes); + MOpcodes.unlock(); + return ret; +} + +bool RegularOpcodeManager::LoadOpcodes(const char *filename) { + NormalMemStrategy s; + s.it = this; + MOpcodes.lock(); + + loaded = true; + eq_to_emu = new EmuOpcode[MAX_EQ_OPCODE]; + emu_to_eq = new uint16[_maxEmuOpcode]; + EQOpcodeCount = MAX_EQ_OPCODE; + EmuOpcodeCount = _maxEmuOpcode; + + //dont need to set eq_to_emu cause every element should get a value + memset(eq_to_emu, 0, sizeof(EmuOpcode)*MAX_EQ_OPCODE); + memset(emu_to_eq, 0xCD, sizeof(uint16)*_maxEmuOpcode); + + bool ret = LoadOpcodesFile(filename, &s); + MOpcodes.unlock(); + return ret; +} + +bool RegularOpcodeManager::ReloadOpcodes(const char *filename) { + if(!loaded) + return(LoadOpcodes(filename)); + + NormalMemStrategy s; + s.it = this; + MOpcodes.lock(); + + memset(eq_to_emu, 0, sizeof(EmuOpcode)*MAX_EQ_OPCODE); + + bool ret = LoadOpcodesFile(filename, &s); + + MOpcodes.unlock(); + return(ret); +} + + +uint16 RegularOpcodeManager::EmuToEQ(const EmuOpcode emu_op) { + //opcode is checked for validity in GetEQOpcode + uint16 res; + MOpcodes.lock(); + + if(emu_op > _maxEmuOpcode) + res = 0; + else + res = emu_to_eq[emu_op]; + + MOpcodes.unlock(); +#ifdef _DEBUG_TRANSLATE + fprintf(stderr, "M Translate Emu %s (%d) to EQ 0x%.4x\n", OpcodeNames[emu_op], emu_op, res); +#endif + return(res); +} + +EmuOpcode RegularOpcodeManager::EQToEmu(const uint16 eq_op) { + //opcode is checked for validity in GetEmuOpcode +//Disabled since current live EQ uses the entire uint16 bitspace for opcodes +// if(eq_op > MAX_EQ_OPCODE) +// return(OP_Unknown); + EmuOpcode res; + MOpcodes.lock(); + if(eq_op >= MAX_EQ_OPCODE) + res = OP_Unknown; + else + res = eq_to_emu[eq_op]; + MOpcodes.unlock(); +#ifdef _DEBUG_TRANSLATE + fprintf(stderr, "M Translate EQ 0x%.4x to Emu %s (%d)\n", eq_op, OpcodeNames[res], res); +#endif + return(res); +} + +void RegularOpcodeManager::SetOpcode(EmuOpcode emu_op, uint16 eq_op) { + + //clear out old mapping + uint16 oldop = 0; + + if(emu_op <= _maxEmuOpcode) + oldop = emu_to_eq[emu_op]; + + if(oldop != 0 && oldop < MAX_EQ_OPCODE) + eq_to_emu[oldop] = OP_Unknown; + + //use our strategy, since we have it + NormalMemStrategy s; + s.it = this; + s.Set(emu_op, eq_op); +} + + +void RegularOpcodeManager::NormalMemStrategy::Set(EmuOpcode emu_op, uint16 eq_op) { + if(uint32(emu_op) >= it->EmuOpcodeCount || eq_op >= it->EQOpcodeCount) + return; + it->emu_to_eq[emu_op] = eq_op; + it->eq_to_emu[eq_op] = emu_op; +} + +NullOpcodeManager::NullOpcodeManager() +: MutableOpcodeManager() { +} + +bool NullOpcodeManager::LoadOpcodes(map* eq, std::string* missingOpcodes) { + return(true); +} + +bool NullOpcodeManager::LoadOpcodes(const char *filename) { + return(true); +} + +bool NullOpcodeManager::ReloadOpcodes(const char *filename) { + return(true); +} + +uint16 NullOpcodeManager::EmuToEQ(const EmuOpcode emu_op) { + return(0); +} + +EmuOpcode NullOpcodeManager::EQToEmu(const uint16 eq_op) { + return(OP_Unknown); +} + +EmptyOpcodeManager::EmptyOpcodeManager() +: MutableOpcodeManager() { +} + + +bool EmptyOpcodeManager::LoadOpcodes(const char *filename) { + return(true); +} + +bool EmptyOpcodeManager::LoadOpcodes(map* eq, std::string* missingOpcodes) { + return(true); +} + +bool EmptyOpcodeManager::ReloadOpcodes(const char *filename) { + return(true); +} + +uint16 EmptyOpcodeManager::EmuToEQ(const EmuOpcode emu_op) { + map::iterator f; + f = emu_to_eq.find(emu_op); + return(f == emu_to_eq.end()? 0 : f->second); +} + +EmuOpcode EmptyOpcodeManager::EQToEmu(const uint16 eq_op) { + map::iterator f; + f = eq_to_emu.find(eq_op); + return(f == eq_to_emu.end()?OP_Unknown:f->second); +} + +void EmptyOpcodeManager::SetOpcode(EmuOpcode emu_op, uint16 eq_op) { + emu_to_eq[emu_op] = eq_op; + eq_to_emu[eq_op] = emu_op; +} + + diff --git a/old/opcodemgr.h b/old/opcodemgr.h new file mode 100644 index 0000000..fe487f0 --- /dev/null +++ b/old/opcodemgr.h @@ -0,0 +1,162 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#ifndef OPCODE_MANAGER_H +#define OPCODE_MANAGER_H + +#include "types.h" +#include "Mutex.h" +#include "emu_opcodes.h" + +#include +using namespace std; + +class OpcodeManager { +public: + OpcodeManager(); + virtual ~OpcodeManager() {} + + virtual bool Mutable() { return(false); } + virtual bool LoadOpcodes(const char *filename) = 0; + virtual bool LoadOpcodes(map* eq, std::string* missingOpcodes = nullptr) = 0; + virtual bool ReloadOpcodes(const char *filename) = 0; + + virtual uint16 EmuToEQ(const EmuOpcode emu_op) = 0; + virtual EmuOpcode EQToEmu(const uint16 eq_op) = 0; + + static const char *EmuToName(const EmuOpcode emu_op); + const char *EQToName(const uint16 emu_op); + EmuOpcode NameSearch(const char *name); + + //This has to be public for stupid visual studio + class OpcodeSetStrategy { + public: + virtual void Set(EmuOpcode emu_op, uint16 eq_op) = 0; + virtual ~OpcodeSetStrategy(){} + }; + +protected: + bool loaded; //true if all opcodes loaded + Mutex MOpcodes; //this only protects the local machine + //in a shared manager, this dosent protect others + + static bool LoadOpcodesFile(const char *filename, OpcodeSetStrategy *s); + static bool LoadOpcodesMap(map* eq, OpcodeSetStrategy *s, std::string* missingOpcodes = nullptr); +}; + +class MutableOpcodeManager : public OpcodeManager { +public: + MutableOpcodeManager() : OpcodeManager() {} + virtual bool Mutable() { return(true); } + virtual void SetOpcode(EmuOpcode emu_op, uint16 eq_op) = 0; +}; + +#ifdef SHARED_OPCODES //quick toggle since only world and zone should possibly use this +//keeps opcodes in shared memory +class SharedOpcodeManager : public OpcodeManager { +public: + virtual ~SharedOpcodeManager() {} + + virtual bool LoadOpcodes(const char *filename); + virtual bool LoadOpcodes(map* eq, std::string* missingOpcodes = nullptr); + virtual bool ReloadOpcodes(const char *filename); + + virtual uint16 EmuToEQ(const EmuOpcode emu_op); + virtual EmuOpcode EQToEmu(const uint16 eq_op); + +protected: + class SharedMemStrategy : public OpcodeManager::OpcodeSetStrategy { + public: + void Set(EmuOpcode emu_op, uint16 eq_op); + }; + static bool DLLLoadOpcodesCallback(const char *filename); +}; +#endif //SHARED_OPCODES + +//keeps opcodes in regular heap memory +class RegularOpcodeManager : public MutableOpcodeManager { +public: + RegularOpcodeManager(); + virtual ~RegularOpcodeManager(); + + virtual bool Editable() { return(true); } + virtual bool LoadOpcodes(const char *filename); + virtual bool LoadOpcodes(map* eq, std::string* missingOpcodes = nullptr); + virtual bool ReloadOpcodes(const char *filename); + + virtual uint16 EmuToEQ(const EmuOpcode emu_op); + virtual EmuOpcode EQToEmu(const uint16 eq_op); + + //implement our editing interface + virtual void SetOpcode(EmuOpcode emu_op, uint16 eq_op); + +protected: + class NormalMemStrategy : public OpcodeManager::OpcodeSetStrategy { + public: + RegularOpcodeManager *it; + void Set(EmuOpcode emu_op, uint16 eq_op); + }; + friend class NormalMemStrategy; + + uint16 *emu_to_eq; + EmuOpcode *eq_to_emu; + uint32 EQOpcodeCount; + uint32 EmuOpcodeCount; +}; + +//always resolves everything to 0 or OP_Unknown +class NullOpcodeManager : public MutableOpcodeManager { +public: + NullOpcodeManager(); + + virtual bool LoadOpcodes(const char *filename); + virtual bool LoadOpcodes(map* eq, std::string* missingOpcodes = nullptr); + virtual bool ReloadOpcodes(const char *filename); + + virtual uint16 EmuToEQ(const EmuOpcode emu_op); + virtual EmuOpcode EQToEmu(const uint16 eq_op); + + //fake it, just used for testing anyways + virtual void SetOpcode(EmuOpcode emu_op, uint16 eq_op) {} +}; + +//starts as NullOpcodeManager, but remembers any mappings set +//could prolly have been implemented with an extension to regular, +//by overriding its load methods to be empty. +class EmptyOpcodeManager : public MutableOpcodeManager { +public: + EmptyOpcodeManager(); + + virtual bool LoadOpcodes(const char *filename); + virtual bool LoadOpcodes(map* eq, std::string* missingOpcodes = nullptr); + virtual bool ReloadOpcodes(const char *filename); + + virtual uint16 EmuToEQ(const EmuOpcode emu_op); + virtual EmuOpcode EQToEmu(const uint16 eq_op); + + //fake it, just used for testing anyways + virtual void SetOpcode(EmuOpcode emu_op, uint16 eq_op); +protected: + map emu_to_eq; + map eq_to_emu; +}; + +#endif + + diff --git a/old/packet_dump.cpp b/old/packet_dump.cpp new file mode 100644 index 0000000..27cb0a9 --- /dev/null +++ b/old/packet_dump.cpp @@ -0,0 +1,195 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#include "../common/debug.h" +#include +#include +#include + +using namespace std; + +#include "packet_dump.h" +#include "EQStream.h" +#include "../common/servertalk.h" + +void DumpPacketAscii(const uchar* buf, int32 size, int32 cols, int32 skip) { + // Output as ASCII + for(int32 i=skip; i 32 && buf[i] < 127) + { + cout << buf[i]; + } + else + { + cout << '.'; + } + } + cout << endl << endl; +} + +void DumpPacketHex(const uchar* buf, int32 size, int32 cols, int32 skip) { + if (size == 0 || size > 39565) + return; + // Output as HEX + char output[4]; + int j = 0; char* ascii = new char[cols+1]; memset(ascii, 0, cols+1); + int32 i; + for(i=skip; i= 32 && buf[i] < 127) { + ascii[j++] = buf[i]; + } + else { + ascii[j++] = '.'; + } +// cout << setfill(0) << setw(2) << hex << (int)buf[i] << " "; + } + int32 k = ((i-skip)-1)%cols; + if (k < 8) + cout << " "; + for (int32 h = k+1; h < cols; h++) { + cout << " "; + } + cout << " | " << ascii << endl; + safe_delete_array(ascii); +} + +void DumpPacket(const uchar* buf, int32 size) +{ + DumpPacketHex(buf, size); +// DumpPacketAscii(buf,size); +} + +void DumpPacket(const ServerPacket* pack, bool iShowInfo) { + if (iShowInfo) { + cout << "Dumping ServerPacket: 0x" << hex << setfill('0') << setw(4) << pack->opcode << dec; + cout << " size:" << pack->size << endl; + } + DumpPacketHex(pack->pBuffer, pack->size); +} + +void DumpPacketBin(const ServerPacket* pack) { + DumpPacketBin(pack->pBuffer, pack->size); +} + +void DumpPacketBin(int32 data) { + DumpPacketBin((uchar*)&data, sizeof(int32)); +} + +void DumpPacketBin(int16 data) { + DumpPacketBin((uchar*)&data, sizeof(int16)); +} + +void DumpPacketBin(int8 data) { + DumpPacketBin((uchar*)&data, sizeof(int8)); +} + + +void DumpPacketBin(const void* iData, int32 len) { + if (!len) + return; + const int8* data = (const int8*) iData; + int32 k=0; + for (k=0; k 1) + cout << " " << hex << setw(2) << setfill('0') << (int) data[k-3] << dec; + if (tmp > 2) + cout << " " << hex << setw(2) << setfill('0') << (int) data[k-2] << dec; + if (tmp > 3) + cout << " " << hex << setw(2) << setfill('0') << (int) data[k-1] << dec; + cout << endl; +} diff --git a/old/packet_dump.h b/old/packet_dump.h new file mode 100644 index 0000000..42fe2ef --- /dev/null +++ b/old/packet_dump.h @@ -0,0 +1,41 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#ifndef PACKET_DUMP_H +#define PACKET_DUMP_H + +#include +using namespace std; + +#include "../common/types.h" +#include "EQPacket.h" + +class ServerPacket; + +void DumpPacketAscii(const uchar* buf, int32 size, int32 cols=16, int32 skip=0); +void DumpPacketHex(const uchar* buf, int32 size, int32 cols=16, int32 skip=0); +void DumpPacketBin(const void* data, int32 len); +void DumpPacket(const uchar* buf, int32 size); +void DumpPacket(const ServerPacket* pack, bool iShowInfo = false); +void DumpPacketBin(const ServerPacket* pack); +void DumpPacketBin(int32 data); +void DumpPacketBin(int16 data); +void DumpPacketBin(int8 data); + +#endif diff --git a/old/packet_functions.cpp b/old/packet_functions.cpp new file mode 100644 index 0000000..a7e6a6c --- /dev/null +++ b/old/packet_functions.cpp @@ -0,0 +1,537 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#include "../common/debug.h" +#include +#include +#include +#include +#include "packet_dump.h" +#include "EQStream.h" +#include "packet_functions.h" + +#ifndef WIN32 + #include +#endif + +using namespace std; + +#define eqemu_alloc_func Z_NULL +#define eqemu_free_func Z_NULL + + +int DeflatePacket(unsigned char* in_data, int in_length, unsigned char* out_data, int max_out_length) { +#ifdef REUSE_ZLIB + static bool inited = false; + static z_stream zstream; + int zerror; + + if(in_data == NULL && out_data == NULL && in_length == 0 && max_out_length == 0) { + //special delete state + deflateEnd(&zstream); + return(0); + } + if(!inited) { + zstream.zalloc = eqemu_alloc_func; + zstream.zfree = eqemu_free_func; + zstream.opaque = Z_NULL; + deflateInit(&zstream, Z_FINISH); + } + + zstream.next_in = in_data; + zstream.avail_in = in_length; +/* zstream.zalloc = Z_NULL; + zstream.zfree = Z_NULL; + zstream.opaque = Z_NULL; + deflateInit(&zstream, Z_FINISH);*/ + zstream.next_out = out_data; + zstream.avail_out = max_out_length; + zerror = deflate(&zstream, Z_FINISH); + + deflateReset(&zstream); + + if (zerror == Z_STREAM_END) + { +// deflateEnd(&zstream); + return zstream.total_out; + } + else + { +// zerror = deflateEnd(&zstream); + return 0; + } +#else + if(in_data == NULL) { + return(0); + } + + z_stream zstream; + int zerror; + + zstream.next_in = in_data; + zstream.avail_in = in_length; + zstream.zalloc = eqemu_alloc_func; + zstream.zfree = eqemu_free_func; + zstream.opaque = Z_NULL; + deflateInit(&zstream, Z_FINISH); + zstream.next_out = out_data; + zstream.avail_out = max_out_length; + zerror = deflate(&zstream, Z_FINISH); + + if (zerror == Z_STREAM_END) + { + deflateEnd(&zstream); + return zstream.total_out; + } + else + { + zerror = deflateEnd(&zstream); + return 0; + } +#endif +} + +uint32 InflatePacket(uchar* indata, uint32 indatalen, uchar* outdata, uint32 outdatalen, bool iQuiet) { +#ifdef REUSE_ZLIB + static bool inited = false; + static z_stream zstream; + int zerror; + + if(indata == NULL && outdata == NULL && indatalen == 0 && outdatalen == 0) { + //special delete state + inflateEnd(&zstream); + return(0); + } + if(!inited) { + zstream.zalloc = eqemu_alloc_func; + zstream.zfree = eqemu_free_func; + zstream.opaque = Z_NULL; + inflateInit2(&zstream, 15); + } + + zstream.next_in = indata; + zstream.avail_in = indatalen; + zstream.next_out = outdata; + zstream.avail_out = outdatalen; + zstream.zalloc = eqemu_alloc_func; + zstream.zfree = eqemu_free_func; + zstream.opaque = Z_NULL; + + i = inflateInit2( &zstream, 15 ); + if (i != Z_OK) { + return 0; + } + + zerror = inflate( &zstream, Z_FINISH ); + + inflateReset(&zstream); + + if(zerror == Z_STREAM_END) { + return zstream.total_out; + } + else { + if (!iQuiet) { + cout << "Error: InflatePacket: inflate() returned " << zerror << " '"; + if (zstream.msg) + cout << zstream.msg; + cout << "'" << endl; + //DumpPacket(indata-16, indatalen+16); + } + + if (zerror == -4 && zstream.msg == 0) + { + return 0; + } + + return 0; + } +#else + if(indata == NULL) + return(0); + + z_stream zstream; + int zerror = 0; + int i; + + zstream.next_in = indata; + zstream.avail_in = indatalen; + zstream.next_out = outdata; + zstream.avail_out = outdatalen; + zstream.zalloc = eqemu_alloc_func; + zstream.zfree = eqemu_free_func; + zstream.opaque = Z_NULL; + + i = inflateInit2( &zstream, 15 ); + if (i != Z_OK) { + return 0; + } + + zerror = inflate( &zstream, Z_FINISH ); + + if(zerror == Z_STREAM_END) { + inflateEnd( &zstream ); + return zstream.total_out; + } + else { + if (!iQuiet) { + cout << "Error: InflatePacket: inflate() returned " << zerror << " '"; + if (zstream.msg) + cout << zstream.msg; + cout << "'" << endl; + //DumpPacket(indata-16, indatalen+16); + } + + if (zerror == -4 && zstream.msg == 0) + { + return 0; + } + + zerror = inflateEnd( &zstream ); + return 0; + } +#endif +} + +int32 roll(int32 in, int8 bits) { + return ((in << bits) | (in >> (32-bits))); +} + +int64 roll(int64 in, int8 bits) { + return ((in << bits) | (in >> (64-bits))); +} + +int32 rorl(int32 in, int8 bits) { + return ((in >> bits) | (in << (32-bits))); +} + +int64 rorl(int64 in, int8 bits) { + return ((in >> bits) | (in << (64-bits))); +} + +int32 CRCLookup(uchar idx) { + if (idx == 0) + return 0x00000000; + + if (idx == 1) + return 0x77073096; + + if (idx == 2) + return roll(CRCLookup(1), 1); + + if (idx == 4) + return 0x076DC419; + + for (uchar b=7; b>0; b--) { + uchar bv = 1 << b; + + if (!(idx ^ bv)) { + // bit is only one set + return ( roll(CRCLookup (4), b - 2) ); + } + + if (idx&bv) { + // bit is set + return( CRCLookup(bv) ^ CRCLookup(idx&(bv - 1)) ); + } + } + + //Failure + return false; +} + +uint32 GenerateCRC(int32 b, int32 bufsize, uchar *buf) { + int32 CRC = (b ^ 0xFFFFFFFF); + int32 bufremain = bufsize; + uchar* bufptr = buf; + + while (bufremain--) { + CRC = CRCLookup((uchar)(*(bufptr++)^ (CRC&0xFF))) ^ (CRC >> 8); + } + + return (htonl (CRC ^ 0xFFFFFFFF)); +} + +long int CRCArray[] = { + 0, +1996959894, +3993919788, +2567524794, +124634137, +1886057615, +3915621685, +2657392035, +249268274, +2044508324, +3772115230, +2547177864, +162941995, +2125561021, +3887607047, +2428444049, +498536548, +1789927666, +4089016648, +2227061214, +450548861, +1843258603, +4107580753, +2211677639, +325883990, +1684777152, +4251122042, +2321926636, +335633487, +1661365465, +4195302755, +2366115317, +997073096, +1281953886, +3579855332, +2724688242, +1006888145, +1258607687, +3524101629, +2768942443, +901097722, +1119000684, +3686517206, +2898065728, +853044451, +1172266101, +3705015759, +2882616665, +651767980, +1373503546, +3369554304, +3218104598, +565507253, +1454621731, +3485111705, +3099436303, +671266974, +1594198024, +3322730930, +2970347812, +795835527, +1483230225, +3244367275, +3060149565, +1994146192, +31158534, +2563907772, +4023717930, +1907459465, +112637215, +2680153253, +3904427059, +2013776290, +251722036, +2517215374, +3775830040, +2137656763, +141376813, +2439277719, +3865271297, +1802195444, +476864866, +2238001368, +4066508878, +1812370925, +453092731, +2181625025, +4111451223, +1706088902, +314042704, +2344532202, +4240017532, +1658658271, +366619977, +2362670323, +4224994405, +1303535960, +984961486, +2747007092, +3569037538, +1256170817, +1037604311, +2765210733, +3554079995, +1131014506, +879679996, +2909243462, +3663771856, +1141124467, +855842277, +2852801631, +3708648649, +1342533948, +654459306, +3188396048, +3373015174, +1466479909, +544179635, +3110523913, +3462522015, +1591671054, +702138776, +2966460450, +3352799412, +1504918807, +783551873, +3082640443, +3233442989, +3988292384, +2596254646, +62317068, +1957810842, +3939845945, +2647816111, +81470997, +1943803523, +3814918930, +2489596804, +225274430, +2053790376, +3826175755, +2466906013, +167816743, +2097651377, +4027552580, +2265490386, +503444072, +1762050814, +4150417245, +2154129355, +426522225, +1852507879, +4275313526, +2312317920, +282753626, +1742555852, +4189708143, +2394877945, +397917763, +1622183637, +3604390888, +2714866558, +953729732, +1340076626, +3518719985, +2797360999, +1068828381, +1219638859, +3624741850, +2936675148, +906185462, +1090812512, +3747672003, +2825379669, +829329135, +1181335161, +3412177804, +3160834842, +628085408, +1382605366, +3423369109, +3138078467, +570562233, +1426400815, +3317316542, +2998733608, +733239954, +1555261956, +3268935591, +3050360625, +752459403, +1541320221, +2607071920, +3965973030, +1969922972, +40735498, +2617837225, +3943577151, +1913087877, +83908371, +2512341634, +3803740692, +2075208622, +213261112, +2463272603, +3855990285, +2094854071, +198958881, +2262029012, +4057260610, +1759359992, +534414190, +2176718541, +4139329115, +1873836001, +414664567, +2282248934, +4279200368, +1711684554, +285281116, +2405801727, +4167216745, +1634467795, +376229701, +2685067896, +3608007406, +1308918612, +956543938, +2808555105, +3495958263, +1231636301, +1047427035, +2932959818, +3654703836, +1088359270, +936918000, +2847714899, +3736837829, +1202900863, +817233897, +3183342108, +3401237130, +1404277552, +615818150, +3134207493, +3453421203, +1423857449, +601450431, +3009837614, +3294710456, +1567103746, +711928724, +3020668471, +3272380065, +1510334235, +755167117}; + +uint32 GenerateCRCRecipe(uint32 initial, void* buf, uint32 len) +{ + uint32 c = 0xFFFFFFFF; + sint8* u = static_cast(buf); + for (size_t i = 0; i < len; ++i) + { + c = CRCArray[(c ^ u[i]) & 0xFF] ^ (c >> 8); + } + return c; +} \ No newline at end of file diff --git a/old/packet_functions.h b/old/packet_functions.h new file mode 100644 index 0000000..318ff2d --- /dev/null +++ b/old/packet_functions.h @@ -0,0 +1,45 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#ifndef PACKET_FUNCTIONS_H +#define PACKET_FUNCTIONS_H +#include "types.h" +#include "EQPacket.h" + +int32 roll(int32 in, int8 bits); +int64 roll(int64 in, int8 bits); +int32 rorl(int32 in, int8 bits); +int64 rorl(int64 in, int8 bits); + +void EncryptProfilePacket(EQApplicationPacket* app); +void EncryptProfilePacket(uchar* pBuffer, int32 size); + +#define EncryptSpawnPacket EncryptZoneSpawnPacket +//void EncryptSpawnPacket(EQApplicationPacket* app); +//void EncryptSpawnPacket(uchar* pBuffer, int32 size); + +void EncryptZoneSpawnPacket(EQApplicationPacket* app); +void EncryptZoneSpawnPacket(uchar* pBuffer, int32 size); + +int DeflatePacket(unsigned char* in_data, int in_length, unsigned char* out_data, int max_out_length); +uint32 InflatePacket(uchar* indata, uint32 indatalen, uchar* outdata, uint32 outdatalen, bool iQuiet = false); +uint32 GenerateCRC(int32 b, int32 bufsize, uchar *buf); +uint32 GenerateCRCRecipe(uint32 b, void* buf, uint32 bufsize); + +#endif diff --git a/old/queue.h b/old/queue.h new file mode 100644 index 0000000..e29ea05 --- /dev/null +++ b/old/queue.h @@ -0,0 +1,128 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#ifndef QUEUE_H +#define QUEUE_H + +template +class MyQueue; + +template +class MyQueueNode +{ +public: + MyQueueNode(T* data) + { + next = 0; + this->data = data; + } + + friend class MyQueue; + +private: + T* data; + MyQueueNode* next; +}; + +template +class MyQueue +{ +public: + MyQueue() + { + head = tail = 0; + } + ~MyQueue() { + clear(); + } + + void push(T* data) + { + if (head == 0) + { + tail = head = new MyQueueNode(data); + } + else + { + tail->next = new MyQueueNode(data); + tail = tail->next; + } + } + + T* pop() + { + if (head == 0) + { + return 0; + } + + T* data = head->data; + MyQueueNode* next_node = head->next; + delete head; + head = next_node; + + return data; + } + + T* top() + { + if (head == 0) + { + return 0; + } + + return head->data; + } + + bool empty() + { + if (head == 0) + { + return true; + } + + return false; + } + + void clear() + { + T* d = 0; + while((d = pop())) { + delete d; + } + return; + } + + int count() + { + int count = 0; + MyQueueNode* d = head; + while(d != 0) { + count++; + d = d->next; + } + return(count); + } + +private: + MyQueueNode* head; + MyQueueNode* tail; +}; + +#endif diff --git a/old/seperator.h b/old/seperator.h new file mode 100644 index 0000000..b033aa8 --- /dev/null +++ b/old/seperator.h @@ -0,0 +1,165 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +// This class will split up a string smartly at the div character (default is space and tab) +// Seperator.arg[i] is a copy of the string chopped at the divs +// Seperator.argplus[i] is a pointer to the original string so it doesnt end at the div + +// Written by Quagmire +#ifndef SEPERATOR_H +#define SEPERATOR_H + +#include +#include + +class Seperator +{ +public: + Seperator(const char* message, char div = ' ', int16 in_maxargnum = 10, int16 arglen = 100, bool iObeyQuotes = false, char div2 = '\t', char div3 = 0, bool iSkipEmpty = true) { + int i; + argnum = 0; + msg = strdup(message); + this->maxargnum = in_maxargnum; + argplus = new const char *[maxargnum+1]; + arg = new char *[maxargnum+1]; + for (i=0; i<=maxargnum; i++) { + argplus[i]=arg[i] = new char[arglen+1]; + memset(arg[i], 0, arglen+1); + } + + int len = strlen(message); + int s = 0, l = 0; + bool inarg = (!iSkipEmpty || !(message[0] == div || message[0] == div2 || message[0] == div3)); + bool inquote = (iObeyQuotes && (message[0] == '\"' || message[0] == '\'')); + argplus[0] = message; + if (len == 0) + return; + + for (i=0; i= arglen) + l = arglen; + if (l){ + if(l > 1 && (argplus[argnum][0] == '\'' || argplus[argnum][0] == '\"')){ + l--; + memcpy(arg[argnum], argplus[argnum]+1, l); + } + else + memcpy(arg[argnum], argplus[argnum], l); + } + arg[argnum][l] = 0; + argnum++; + if (iSkipEmpty) + inarg = false; + else { + s=i+1; + argplus[argnum] = &message[s]; + } + } + } + else if (iObeyQuotes && (message[i] == '\"' || message[i] == '\'')) { + inquote = true; + } + else { + s = i; + argplus[argnum] = &message[s]; + if (!(message[i] == div || message[i] == div2 || message[i] == div3)) { + inarg = true; + } + } + if (argnum > maxargnum) + break; + } + if (inarg && argnum <= maxargnum) { + l = i-s; + if (l >= arglen) + l = arglen; + if (l) + memcpy(arg[argnum], argplus[argnum], l); + } + } + ~Seperator() { + for (int i=0; i<=maxargnum; i++) + safe_delete_array(arg[i]); + safe_delete_array(arg); + safe_delete_array(argplus); + if (msg) + free(msg); + } + int16 argnum; + char** arg; + const char** argplus; + char * msg; + bool IsSet(int num) const { + return IsSet(arg[num]); + } + bool IsNumber(int num) const { + return IsNumber(arg[num]); + } + bool IsHexNumber(int num) const { + return IsHexNumber(arg[num]); + } + static bool IsSet(const char *check) { + return check[0] != '\0'; + } + static bool IsNumber(const char* check) { + bool SeenDec = false; + int len = strlen(check); + if (len == 0) { + return false; + } + int i; + for (i = 0; i < len; i++) { + if (check[i] < '0' || check[i] > '9') { + if (check[i] == '.' && !SeenDec) { + SeenDec = true; + } + else if (i == 0 && (check[i] == '-' || check[i] == '+') && !check[i+1] == 0) { + // this is ok, do nothin + } + else { + return false; + } + } + } + return true; + } + static bool IsHexNumber(char* check) { + int len = strlen(check); + if (len < 3) + return false; + if (check[0] != '0' || (check[1] != 'x' && check[1] != 'X')) + return false; + for (int i=2; i '9') && (check[i] < 'A' || check[i] > 'F') && (check[i] < 'a' || check[i] > 'f')) + return false; + } + return true; + } + inline int16 GetMaxArgNum() const { return maxargnum; } + inline int16 GetArgNumber() const { return argnum; } +private: + int16 maxargnum; +}; + +#endif diff --git a/old/servertalk.h b/old/servertalk.h new file mode 100644 index 0000000..fa2c1c1 --- /dev/null +++ b/old/servertalk.h @@ -0,0 +1,754 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#ifndef EQ_SOPCODES_H +#define EQ_SOPCODES_H + +#define EQEMU_PROTOCOL_VERSION "0.5.0" + +#include "types.h" +#include "packet_functions.h" +#include + +#define SERVER_TIMEOUT 45000 // how often keepalive gets sent +#define INTERSERVER_TIMER 10000 +#define LoginServer_StatusUpdateInterval 15000 +#define LoginServer_AuthStale 60000 +#define AUTHCHANGE_TIMEOUT 900 // in seconds + +#define ServerOP_KeepAlive 0x0001 // packet to test if port is still open +#define ServerOP_ChannelMessage 0x0002 // broadcast/guildsay +#define ServerOP_SetZone 0x0003 // client -> server zoneinfo +#define ServerOP_ShutdownAll 0x0004 // exit(0); +#define ServerOP_ZoneShutdown 0x0005 // unload all data, goto sleep mode +#define ServerOP_ZoneBootup 0x0006 // come out of sleep mode and load zone specified +#define ServerOP_ZoneStatus 0x0007 // Shows status of all zones +#define ServerOP_SetConnectInfo 0x0008 // Tells server address and port # +#define ServerOP_EmoteMessage 0x0009 // Worldfarts +#define ServerOP_ClientList 0x000A // Update worldserver's client list, for #whos +#define ServerOP_Who 0x000B // #who +#define ServerOP_ZonePlayer 0x000C // #zone, or #summon +#define ServerOP_KickPlayer 0x000D // #kick +#define ServerOP_RefreshGuild 0x000E // Notice to all zoneservers to refresh their guild cache for ID# in packet +#define ServerOP_GuildKickAll 0x000F // Remove all clients from this guild +#define ServerOP_GuildInvite 0x0010 +#define ServerOP_GuildRemove 0x0011 +#define ServerOP_GuildPromote 0x0012 +#define ServerOP_GuildDemote 0x0013 +#define ServerOP_GuildLeader 0x0014 +#define ServerOP_GuildGMSet 0x0015 +#define ServerOP_GuildGMSetRank 0x0016 +#define ServerOP_FlagUpdate 0x0018 // GM Flag updated for character, refresh the memory cache +#define ServerOP_GMGoto 0x0019 +#define ServerOP_MultiLineMsg 0x001A +#define ServerOP_Lock 0x001B // For #lock/#unlock inside server +#define ServerOP_Motd 0x001C // For changing MoTD inside server. +#define ServerOP_Uptime 0x001D +#define ServerOP_Petition 0x001E +#define ServerOP_KillPlayer 0x001F +#define ServerOP_UpdateGM 0x0020 +#define ServerOP_RezzPlayer 0x0021 +#define ServerOP_ZoneReboot 0x0022 +#define ServerOP_ZoneToZoneRequest 0x0023 +#define ServerOP_AcceptWorldEntrance 0x0024 +#define ServerOP_ZAAuth 0x0025 +#define ServerOP_ZAAuthFailed 0x0026 +#define ServerOP_ZoneIncClient 0x0027 // Incomming client +#define ServerOP_ClientListKA 0x0028 +#define ServerOP_ChangeWID 0x0029 +#define ServerOP_IPLookup 0x002A +#define ServerOP_LockZone 0x002B +#define ServerOP_ItemStatus 0x002C +#define ServerOP_OOCMute 0x002D +#define ServerOP_Revoke 0x002E +#define ServerOP_GuildJoin 0x002F +#define ServerOP_GroupIDReq 0x0030 +#define ServerOP_GroupIDReply 0x0031 +#define ServerOP_GroupLeave 0x0032 // for disbanding out of zone folks +#define ServerOP_RezzPlayerAccept 0x0033 +#define ServerOP_SpawnCondition 0x0034 +#define ServerOP_SpawnEvent 0x0035 + +#define UpdateServerOP_Verified 0x5090 +#define UpdateServerOP_DisplayMsg 0x5091 +#define UpdateServerOP_Completed 0x5092 + +#define ServerOP_LSInfo 0x1000 +#define ServerOP_LSStatus 0x1001 +#define ServerOP_LSClientAuth 0x1002 +#define ServerOP_LSFatalError 0x1003 +#define ServerOP_SystemwideMessage 0x1005 +#define ServerOP_ListWorlds 0x1006 +#define ServerOP_PeerConnect 0x1007 + +#define ServerOP_LSZoneInfo 0x3001 +#define ServerOP_LSZoneStart 0x3002 +#define ServerOP_LSZoneBoot 0x3003 +#define ServerOP_LSZoneShutdown 0x3004 +#define ServerOP_LSZoneSleep 0x3005 +#define ServerOP_LSPlayerLeftWorld 0x3006 +#define ServerOP_LSPlayerJoinWorld 0x3007 +#define ServerOP_LSPlayerZoneChange 0x3008 + +#define ServerOP_UsertoWorldReq 0xAB00 +#define ServerOP_UsertoWorldResp 0xAB01 + +#define ServerOP_EncapPacket 0x2007 // Packet within a packet +#define ServerOP_WorldListUpdate 0x2008 +#define ServerOP_WorldListRemove 0x2009 +#define ServerOP_TriggerWorldListRefresh 0x200A + +#define ServerOP_WhoAll 0x0210 + +#define ServerOP_SetWorldTime 0x200B +#define ServerOP_GetWorldTime 0x200C +#define ServerOP_SyncWorldTime 0x200E + +//EQ2 Opcodes +#define ServerOP_CharTimeStamp 0x200F + +#define ServerOP_NameFilterCheck 0x2011 +#define ServerOP_BasicCharUpdate 0x2012 +#define ServerOP_CharacterCreate 0x2013 +#define ServerOP_NameCharUpdate 0x2014 +#define ServerOP_GetLatestTables 0x2015 +#define ServerOP_GetTableQuery 0x2016 +#define ServerOP_GetTableData 0x2017 +#define ServerOP_RaceUpdate 0x2018 +#define ServerOP_ZoneUpdate 0x2019 +#define ServerOP_BugReport 0x201A +#define ServerOP_ResetDatabase 0x201B +#define ServerOP_ZoneUpdates 0x201C +#define ServerOP_LoginEquipment 0x201D // updates charater select screen item appearances (gear appear) +#define ServerOP_CharacterPicture 0x201E + + +/************ PACKET RELATED STRUCT ************/ +class ServerPacket +{ +public: + ~ServerPacket() { safe_delete_array(pBuffer); } + ServerPacket(int16 in_opcode = 0, int32 in_size = 0) { + this->compressed = false; + size = in_size; + opcode = in_opcode; + if (size == 0) { + pBuffer = 0; + } + else { + pBuffer = new uchar[size]; + memset(pBuffer, 0, size); + } + destination = 0; + InflatedSize = 0; + } + ServerPacket* Copy() { + if (this == 0) { + return 0; + } + ServerPacket* ret = new ServerPacket(this->opcode, this->size); + if (this->size) + memcpy(ret->pBuffer, this->pBuffer, this->size); + ret->compressed = this->compressed; + ret->InflatedSize = this->InflatedSize; + return ret; + } + bool Deflate() { + if (compressed) + return false; + if ((!this->pBuffer) || (!this->size)) + return false; + uchar* tmp = new uchar[this->size + 128]; + int32 tmpsize = DeflatePacket(this->pBuffer, this->size, tmp, this->size + 128); + if (!tmpsize) { + safe_delete_array(tmp); + return false; + } + this->compressed = true; + this->InflatedSize = this->size; + this->size = tmpsize; + uchar* new_buffer = new uchar[this->size]; + memcpy(new_buffer, tmp, this->size); + safe_delete_array(tmp); + uchar* tmpdel = this->pBuffer; + this->pBuffer = new_buffer; + safe_delete_array(tmpdel); + return true; + } + bool Inflate() { + if (!compressed) + return false; + if ((!this->pBuffer) || (!this->size)) + return false; + uchar* tmp = new uchar[InflatedSize]; + int32 tmpsize = InflatePacket(this->pBuffer, this->size, tmp, InflatedSize); + if (!tmpsize) { + safe_delete_array(tmp); + return false; + } + compressed = false; + this->size = tmpsize; + uchar* tmpdel = this->pBuffer; + this->pBuffer = tmp; + safe_delete_array(tmpdel); + return true; + } + int32 size; + int16 opcode; + uchar* pBuffer; + bool compressed; + int32 InflatedSize; + int32 destination; +}; + +#pragma pack(1) + +struct GetLatestTables_Struct{ + float table_version; + float data_version; +}; + +struct ServerLSInfo_Struct { + char name[201]; // name the worldserver wants + char address[250]; // DNS address of the server + char account[31]; // account name for the worldserver + char password[256]; // password for the name + char protocolversion[25]; // Major protocol version number + char serverversion[64]; // minor server software version number + int8 servertype; // 0=world, 1=chat, 2=login, 3=MeshLogin, 4=World Debug + int32 dbversion; // database major+minor version from version.h (for PatchServer) +}; + +struct ServerLSStatus_Struct { + sint32 status; + sint32 num_players; + sint32 num_zones; + int8 world_max_level; +}; + +struct ServerSystemwideMessage { + int32 lsaccount_id; + char key[30]; // sessionID key for verification + int32 type; + char message[0]; +}; + +struct ServerSyncWorldList_Struct { + int32 RemoteID; + int32 ip; + sint32 status; + char name[201]; + char address[250]; + char account[31]; + int32 accountid; + int8 authlevel; + int8 servertype; // 0=world, 1=chat, 2=login + int32 adminid; + int8 showdown; + sint32 num_players; + sint32 num_zones; + bool placeholder; +}; + +struct UsertoWorldRequest_Struct { + int32 lsaccountid; + int32 char_id; + int32 worldid; + int32 FromID; + int32 ToID; + char ip_address[21]; +}; + +struct UsertoWorldResponse_Struct { + int32 lsaccountid; + int32 char_id; + int32 worldid; + int32 access_key; + int8 response; + char ip_address[80]; + int32 port; + int32 FromID; + int32 ToID; +}; + +struct ServerEncapPacket_Struct { + int32 ToID; // ID number of the LWorld on the other server + int16 opcode; + int16 size; + uchar data[0]; +}; + +struct ServerEmoteMessage_Struct { + char to[64]; + int32 guilddbid; + sint16 minstatus; + int32 type; + char message[0]; +}; + +/*struct TableVersion{ + char name[64]; + int32 version; + int32 max_table_version; + int32 max_data_version; + sint32 data_version; + int8 last; + char column_names[1000]; +};*/ + +typedef struct { + char name[256]; + unsigned int name_len; + unsigned int version; + unsigned int data_version; +} TableVersion; + +template void AddPtrData(string* buffer, Type& data){ + buffer->append((char*)&data, sizeof(Type)); +} +template void AddPtrData(string* buffer, Type* data, int16 size){ + buffer->append(data, size); +} +class LatestTableVersions { +public: + LatestTableVersions(){ + tables = 0; + current_index = 0; + total_tables = 0; + data_version = 0; + } + ~LatestTableVersions(){ + safe_delete_array(tables); + } + void SetTableSize(int16 size){ + total_tables = size; + tables = new TableVersion[total_tables]; + } + void AddTable(char* name, int32 version, int32 data_version){ + strcpy(tables[current_index].name, name); + tables[current_index].version = version; + tables[current_index].data_version = data_version; + current_index++; + } + int16 GetTotalSize(){ + return total_tables * sizeof(TableVersion) + sizeof(int16); + } + int16 GetTotalTables(){ + return total_tables; + } + TableVersion* GetTables(){ + return tables; + } + TableVersion GetTable(int16 index){ + return tables[index]; + } + string Serialize(){ + AddPtrData(&buffer, total_tables); + for(int16 i=0;i tmp_queries; +}; +class TableDataQuery{ +public: + TableDataQuery(char* table_name){ + if( strlen(table_name) >= sizeof(tablename) ) + return; + strcpy(tablename, table_name); + num_queries = 0; + columns_size = 0; + columns = 0; + version = 0; + table_size = 0; + } + TableDataQuery(){ + num_queries = 0; + columns_size = 0; + columns = 0; + version = 0; + table_size = 0; + } + ~TableDataQuery(){ + safe_delete_array(columns); + for(int32 i=0;iquery); + safe_delete(queries[i]); + } + } + int32 GetTotalQueries(){ + return num_queries; + } + string* Serialize(){ + buffer = ""; + num_queries = queries.size(); + if(GetTotalQueries() == 0) + return 0; + table_size = strlen(tablename); + AddPtrData(&buffer, table_size); + AddPtrData(&buffer, tablename, table_size + 1); + AddPtrData(&buffer, version); + if(num_queries > 200){ + int32 max_queries = 200; + AddPtrData(&buffer, max_queries); + } + else + AddPtrData(&buffer, num_queries); + AddPtrData(&buffer, columns_size); + AddPtrData(&buffer, columns, columns_size); + vector::iterator query_iterator; + int16 count = 0; + for(int i=GetTotalQueries() - 1;i >=0 && count < 200;i--){ + AddPtrData(&buffer, queries[i]->size); + AddPtrData(&buffer, queries[i]->query, queries[i]->size); + safe_delete_array(queries[i]->query); + safe_delete(queries[i]); + queries.pop_back(); + count++; + } + return &buffer; + } + void DeSerialize(uchar* data){ + uchar* ptr = data; + + memcpy(&table_size, ptr, sizeof(table_size)); + ptr+= sizeof(table_size); + memcpy(&tablename, ptr, table_size + 1); + ptr+= table_size + 1; + + memcpy(&version, ptr, sizeof(version)); + ptr+= sizeof(version); + + memcpy(&num_queries, ptr, sizeof(num_queries)); + ptr+= sizeof(num_queries); + + memcpy(&columns_size, ptr, sizeof(columns_size)); + ptr+= sizeof(columns_size); + columns = new char[columns_size + 1]; + memcpy(columns, ptr, columns_size + 1); + ptr+= columns_size; + + for(int32 i=0;isize, ptr, sizeof(new_query->size)); + ptr+= sizeof(new_query->size); + new_query->query = new char[new_query->size + 1]; + memcpy(new_query->query, ptr, new_query->size); + ptr+= new_query->size; + queries.push_back(new_query); + } + catch( bad_alloc &ba ) + { + cout << ba.what() << endl; + if( NULL != new_query ) + delete new_query; + } + } + } + string buffer; + int32 num_queries; + int32 version; + int16 table_size; + char tablename[64]; + int16 columns_size; + char* columns; + vector queries; +}; + +// Max number of equipment updates to send at once +struct EquipmentUpdateRequest_Struct +{ + int16 max_per_batch; +}; + +// Login's structure of equipment data +struct LoginEquipmentUpdate +{ + int32 world_char_id; + int16 equip_type; + int8 red; + int8 green; + int8 blue; + int8 highlight_red; + int8 highlight_green; + int8 highlight_blue; + int32 slot; +}; + +// World's structure of equipment data +struct EquipmentUpdate_Struct +{ + int32 id; // unique record identifier per world + int32 world_char_id; + int16 equip_type; + int8 red; + int8 green; + int8 blue; + int8 highlight_red; + int8 highlight_green; + int8 highlight_blue; + int32 slot; +}; + +// How many equipmment updates are there to send? +struct EquipmentUpdateList_Struct +{ + sint16 total_updates; +}; + +struct ZoneUpdateRequest_Struct{ + int16 max_per_batch; +}; + +struct LoginZoneUpdate{ + string name; + string description; +}; + +struct ZoneUpdate_Struct{ + int32 zone_id; + int8 zone_name_length; + int8 zone_desc_length; + char data[0]; +}; + +struct ZoneUpdateList_Struct{ + uint16 total_updates; + char data[0]; +}; + +//EQ2 Specific Structures Login -> World (Image) +struct CharacterTimeStamp_Struct { +int32 char_id; +int32 account_id; +int32 unix_timestamp; +}; + +//EQ2 Specific Structures World -> Login (Image) + +/**UPDATE_FIELD TYPES** +These will be stored beside the timestamp on the world server to determine what has changed on between the timestamp, when the update is sent, it will remove the flag. + +8 bits in a byte: +Example: 01001100 +0 Level Flag +1 Race Flag +0 Class Flag +0 Gender Flag +1 Zone Flag +1 Armor Flag +0 Name Flag +0 Delete Flag +**/ +#define LEVEL_UPDATE_FLAG 1 +#define RACE_UPDATE_FLAG 2 +#define CLASS_UPDATE_FLAG 4 +#define GENDER_UPDATE_FLAG 8 +#define ZONE_UPDATE_FLAG 16 +#define ARMOR_UPDATE_FLAG 32 +#define NAME_UPDATE_FLAG 64 +#define DELETE_UPDATE_FLAG 128 +//This structure used for basic changes such as level,class,gender, and deletes that are not able to be backed up +struct CharDataUpdate_Struct { +int32 account_id; +int32 char_id; +int8 update_field; +int32 update_data; +}; +struct BugReport{ +char category[64]; +char subcategory[64]; +char causes_crash[64]; +char reproducible[64]; +char summary[128]; +char description[2000]; +char version[32]; +char player[64]; +int32 account_id; +char spawn_name[64]; +int32 spawn_id; +int32 zone_id; +}; + +struct RaceUpdate_Struct { +int32 account_id; +int32 char_id; +int16 model_type; +int8 race; +}; + +//If this structure comes in with more than 74 bytes, should probably discard (leaves 65 bytes for new_name) +#define CHARNAMEUPDATESTRUCT_MAXSIZE 74 +struct CharNameUpdate_Struct { +int32 account_id; +int32 char_id; +int8 name_length; // If its longer than 64, something is wrong :-/ +char new_name[0]; +}; + +//If this structure comes in with more than 78 bytes, should probably discard (leaves 65 bytes for new_zone) +#define CHARZONESTRUCT_MAXSIZE 78 +struct CharZoneUpdate_Struct { +int32 account_id; +int32 char_id; +int32 zone_id; +int8 zone_length; // If its longer than 64, something is wrong :-/ +char new_zone[0]; +}; + +struct WorldCharCreate_Struct { +int32 account_id; +int32 char_id; +int16 model_type; +int16 char_size; +uchar character[0]; +}; + +struct WorldCharNameFilter_Struct { +int32 account_id; +int16 name_length; +uchar name[0]; +}; + +struct WorldCharNameFilterResponse_Struct { +int32 account_id; +int32 char_id; +int8 response; +}; + +#define CHARPICSTRUCT_MINSIZE 10 +// Should only be used for the headshot picture +struct CharPictureUpdate_Struct { + int32 account_id; + int32 char_id; + int16 pic_size; + char pic[0]; +}; + +#pragma pack() + +#endif diff --git a/old/sha512.cpp b/old/sha512.cpp new file mode 100644 index 0000000..10b9592 --- /dev/null +++ b/old/sha512.cpp @@ -0,0 +1,155 @@ +#include +#include +#include "sha512.h" + +const unsigned long long SHA512::sha512_k[80] = //ULL = uint64 + {0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, + 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL, + 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, + 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, + 0xd807aa98a3030242ULL, 0x12835b0145706fbeULL, + 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, + 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, + 0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL, + 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, + 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, + 0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL, + 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL, + 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, + 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL, + 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, + 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, + 0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL, + 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL, + 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, + 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL, + 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, + 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, + 0xd192e819d6ef5218ULL, 0xd69906245565a910ULL, + 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL, + 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, + 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL, + 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, + 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL, + 0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL, + 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL, + 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, + 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL, + 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, + 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, + 0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL, + 0x113f9804bef90daeULL, 0x1b710b35131c471bULL, + 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, + 0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL, + 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, + 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL}; + +void SHA512::transform(const unsigned char *message, unsigned int block_nb) +{ + uint64 w[80]; + uint64 wv[8]; + uint64 t1, t2; + const unsigned char *sub_block; + int i, j; + for (i = 0; i < (int) block_nb; i++) { + sub_block = message + (i << 7); + for (j = 0; j < 16; j++) { + SHA2_PACK64(&sub_block[j << 3], &w[j]); + } + for (j = 16; j < 80; j++) { + w[j] = SHA512_F4(w[j - 2]) + w[j - 7] + SHA512_F3(w[j - 15]) + w[j - 16]; + } + for (j = 0; j < 8; j++) { + wv[j] = m_h[j]; + } + for (j = 0; j < 80; j++) { + t1 = wv[7] + SHA512_F2(wv[4]) + SHA2_CH(wv[4], wv[5], wv[6]) + + sha512_k[j] + w[j]; + t2 = SHA512_F1(wv[0]) + SHA2_MAJ(wv[0], wv[1], wv[2]); + wv[7] = wv[6]; + wv[6] = wv[5]; + wv[5] = wv[4]; + wv[4] = wv[3] + t1; + wv[3] = wv[2]; + wv[2] = wv[1]; + wv[1] = wv[0]; + wv[0] = t1 + t2; + } + for (j = 0; j < 8; j++) { + m_h[j] += wv[j]; + } + + } +} + +void SHA512::init() +{ + m_h[0] = 0x6a09e667f3bcc908ULL; + m_h[1] = 0xbb67ae8584caa73bULL; + m_h[2] = 0x3c6ef372fe94f82bULL; + m_h[3] = 0xa54ff53a5f1d36f1ULL; + m_h[4] = 0x510e527fade682d1ULL; + m_h[5] = 0x9b05688c2b3e6c1fULL; + m_h[6] = 0x1f83d9abfb41bd6bULL; + m_h[7] = 0x5be0cd19137e2179ULL; + m_len = 0; + m_tot_len = 0; +} + +void SHA512::update(const unsigned char *message, unsigned int len) +{ + unsigned int block_nb; + unsigned int new_len, rem_len, tmp_len; + const unsigned char *shifted_message; + tmp_len = SHA384_512_BLOCK_SIZE - m_len; + rem_len = len < tmp_len ? len : tmp_len; + memcpy(&m_block[m_len], message, rem_len); + if (m_len + len < SHA384_512_BLOCK_SIZE) { + m_len += len; + return; + } + new_len = len - rem_len; + block_nb = new_len / SHA384_512_BLOCK_SIZE; + shifted_message = message + rem_len; + transform(m_block, 1); + transform(shifted_message, block_nb); + rem_len = new_len % SHA384_512_BLOCK_SIZE; + memcpy(m_block, &shifted_message[block_nb << 7], rem_len); + m_len = rem_len; + m_tot_len += (block_nb + 1) << 7; +} + +void SHA512::final(unsigned char *digest) +{ + unsigned int block_nb; + unsigned int pm_len; + unsigned int len_b; + int i; + block_nb = 1 + ((SHA384_512_BLOCK_SIZE - 17) + < (m_len % SHA384_512_BLOCK_SIZE)); + len_b = (m_tot_len + m_len) << 3; + pm_len = block_nb << 7; + memset(m_block + m_len, 0, pm_len - m_len); + m_block[m_len] = 0x80; + SHA2_UNPACK32(len_b, m_block + pm_len - 4); + transform(m_block, block_nb); + for (i = 0 ; i < 8; i++) { + SHA2_UNPACK64(m_h[i], &digest[i << 3]); + } +} + +std::string sha512(std::string input) +{ + unsigned char digest[SHA512::DIGEST_SIZE]; + memset(digest,0,SHA512::DIGEST_SIZE); + SHA512 ctx = SHA512(); + ctx.init(); + ctx.update((unsigned char*)input.c_str(), input.length()); + ctx.final(digest); + + char buf[2*SHA512::DIGEST_SIZE+1]; + buf[2*SHA512::DIGEST_SIZE] = 0; + for (int i = 0; i < SHA512::DIGEST_SIZE; i++) + sprintf(buf+i*2, "%02x", digest[i]); + return std::string(buf); +} \ No newline at end of file diff --git a/old/sha512.h b/old/sha512.h new file mode 100644 index 0000000..72ce5a8 --- /dev/null +++ b/old/sha512.h @@ -0,0 +1,71 @@ +#ifndef SHA512_H +#define SHA512_H +#include + +class SHA512 +{ +protected: + typedef unsigned char uint8; + typedef unsigned int uint32; + typedef unsigned long long uint64; + + const static uint64 sha512_k[]; + static const unsigned int SHA384_512_BLOCK_SIZE = (1024/8); + +public: + void init(); + void update(const unsigned char *message, unsigned int len); + void final(unsigned char *digest); + static const unsigned int DIGEST_SIZE = ( 512 / 8); + +protected: + void transform(const unsigned char *message, unsigned int block_nb); + unsigned int m_tot_len; + unsigned int m_len; + unsigned char m_block[2 * SHA384_512_BLOCK_SIZE]; + uint64 m_h[8]; +}; + + +std::string sha512(std::string input); + +#define SHA2_SHFR(x, n) (x >> n) +#define SHA2_ROTR(x, n) ((x >> n) | (x << ((sizeof(x) << 3) - n))) +#define SHA2_ROTL(x, n) ((x << n) | (x >> ((sizeof(x) << 3) - n))) +#define SHA2_CH(x, y, z) ((x & y) ^ (~x & z)) +#define SHA2_MAJ(x, y, z) ((x & y) ^ (x & z) ^ (y & z)) +#define SHA512_F1(x) (SHA2_ROTR(x, 28) ^ SHA2_ROTR(x, 34) ^ SHA2_ROTR(x, 39)) +#define SHA512_F2(x) (SHA2_ROTR(x, 14) ^ SHA2_ROTR(x, 18) ^ SHA2_ROTR(x, 41)) +#define SHA512_F3(x) (SHA2_ROTR(x, 1) ^ SHA2_ROTR(x, 8) ^ SHA2_SHFR(x, 7)) +#define SHA512_F4(x) (SHA2_ROTR(x, 19) ^ SHA2_ROTR(x, 61) ^ SHA2_SHFR(x, 6)) +#define SHA2_UNPACK32(x, str) \ +{ \ + *((str) + 3) = (uint8) ((x) ); \ + *((str) + 2) = (uint8) ((x) >> 8); \ + *((str) + 1) = (uint8) ((x) >> 16); \ + *((str) + 0) = (uint8) ((x) >> 24); \ +} +#define SHA2_UNPACK64(x, str) \ +{ \ + *((str) + 7) = (uint8) ((x) ); \ + *((str) + 6) = (uint8) ((x) >> 8); \ + *((str) + 5) = (uint8) ((x) >> 16); \ + *((str) + 4) = (uint8) ((x) >> 24); \ + *((str) + 3) = (uint8) ((x) >> 32); \ + *((str) + 2) = (uint8) ((x) >> 40); \ + *((str) + 1) = (uint8) ((x) >> 48); \ + *((str) + 0) = (uint8) ((x) >> 56); \ +} +#define SHA2_PACK64(str, x) \ +{ \ + *(x) = ((uint64) *((str) + 7) ) \ + | ((uint64) *((str) + 6) << 8) \ + | ((uint64) *((str) + 5) << 16) \ + | ((uint64) *((str) + 4) << 24) \ + | ((uint64) *((str) + 3) << 32) \ + | ((uint64) *((str) + 2) << 40) \ + | ((uint64) *((str) + 1) << 48) \ + | ((uint64) *((str) + 0) << 56); \ +} + +#endif \ No newline at end of file diff --git a/old/string_util.cpp b/old/string_util.cpp new file mode 100644 index 0000000..df3790d --- /dev/null +++ b/old/string_util.cpp @@ -0,0 +1,529 @@ +/* + * Copyright 2013 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "string_util.h" +#include + +#ifdef _WINDOWS + #include + + #define snprintf _snprintf + #define strncasecmp _strnicmp + #define strcasecmp _stricmp + +#else + #include + #include +#include + +#endif + +#ifndef va_copy + #define va_copy(d,s) ((d) = (s)) +#endif + +// original source: +// https://github.com/facebook/folly/blob/master/folly/String.cpp +// +const std::string vStringFormat(const char* format, va_list args) +{ + std::string output; + va_list tmpargs; + + va_copy(tmpargs,args); + int characters_used = vsnprintf(nullptr, 0, format, tmpargs); + va_end(tmpargs); + + // Looks like we have a valid format string. + if (characters_used > 0) { + output.resize(characters_used + 1); + + va_copy(tmpargs,args); + characters_used = vsnprintf(&output[0], output.capacity(), format, tmpargs); + va_end(tmpargs); + + output.resize(characters_used); + + // We shouldn't have a format error by this point, but I can't imagine what error we + // could have by this point. Still, return empty string; + if (characters_used < 0) + output.clear(); + } + return output; +} + +const std::string str_tolower(std::string s) +{ + std::transform( + s.begin(), s.end(), s.begin(), + [](unsigned char c) { return ::tolower(c); } + ); + return s; +} + +std::vector split(std::string str_to_split, char delimiter) +{ + std::stringstream ss(str_to_split); + std::string item; + std::vector exploded_values; + while (std::getline(ss, item, delimiter)) { + exploded_values.push_back(item); + } + + return exploded_values; +} + +const std::string str_toupper(std::string s) +{ + std::transform( + s.begin(), s.end(), s.begin(), + [](unsigned char c) { return ::toupper(c); } + ); + return s; +} + +const std::string ucfirst(std::string s) +{ + std::string output = s; + if (!s.empty()) + output[0] = static_cast(::toupper(s[0])); + + return output; +} + +const std::string StringFormat(const char *format, ...) +{ + va_list args; + va_start(args, format); + std::string output = vStringFormat(format, args); + va_end(args); + return output; +} + +std::vector SplitString(const std::string &str, char delim) { + std::vector ret; + std::stringstream ss(str); + std::string item; + + while(std::getline(ss, item, delim)) { + ret.push_back(item); + } + + return ret; +} + +std::string implode(std::string glue, std::vector src) +{ + if (src.empty()) { + return {}; + } + + std::ostringstream output; + std::vector::iterator src_iter; + + for (src_iter = src.begin(); src_iter != src.end(); src_iter++) { + output << *src_iter << glue; + } + + std::string final_output = output.str(); + final_output.resize (output.str().size () - glue.size()); + + return final_output; +} + +std::string EscapeString(const std::string &s) { + std::string ret; + + size_t sz = s.length(); + for(size_t i = 0; i < sz; ++i) { + char c = s[i]; + switch(c) { + case '\x00': + ret += "\\x00"; + break; + case '\n': + ret += "\\n"; + break; + case '\r': + ret += "\\r"; + break; + case '\\': + ret += "\\\\"; + break; + case '\'': + ret += "\\'"; + break; + case '\"': + ret += "\\\""; + break; + case '\x1a': + ret += "\\x1a"; + break; + default: + ret.push_back(c); + break; + } + } + + return ret; +} + +std::string EscapeString(const char *src, size_t sz) { + std::string ret; + + for(size_t i = 0; i < sz; ++i) { + char c = src[i]; + switch(c) { + case '\x00': + ret += "\\x00"; + break; + case '\n': + ret += "\\n"; + break; + case '\r': + ret += "\\r"; + break; + case '\\': + ret += "\\\\"; + break; + case '\'': + ret += "\\'"; + break; + case '\"': + ret += "\\\""; + break; + case '\x1a': + ret += "\\x1a"; + break; + default: + ret.push_back(c); + break; + } + } + + return ret; +} + +bool StringIsNumber(const std::string &s) { + try { + auto r = stod(s); + return true; + } + catch (std::exception &) { + return false; + } +} + +void ToLowerString(std::string &s) { + std::transform(s.begin(), s.end(), s.begin(), ::tolower); +} + +void ToUpperString(std::string &s) { + std::transform(s.begin(), s.end(), s.begin(), ::toupper); +} + +std::string JoinString(const std::vector& ar, const std::string &delim) { + std::string ret; + for (size_t i = 0; i < ar.size(); ++i) { + if (i != 0) { + ret += delim; + } + + ret += ar[i]; + } + + return ret; +} + +void find_replace(std::string &string_subject, const std::string &search_string, const std::string &replace_string) +{ + if (string_subject.find(search_string) == std::string::npos) { + return; + } + + size_t start_pos = 0; + while((start_pos = string_subject.find(search_string, start_pos)) != std::string::npos) { + string_subject.replace(start_pos, search_string.length(), replace_string); + start_pos += replace_string.length(); + } + +} + +void ParseAccountString(const std::string &s, std::string &account, std::string &loginserver) +{ + auto split = SplitString(s, ':'); + if (split.size() == 2) { + loginserver = split[0]; + account = split[1]; + } + else if(split.size() == 1) { + account = split[0]; + } +} + +//Const char based + +// normal strncpy doesnt put a null term on copied strings, this one does +// ref: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wcecrt/htm/_wcecrt_strncpy_wcsncpy.asp +char* strn0cpy(char* dest, const char* source, uint32 size) { + if (!dest) + return 0; + if (size == 0 || source == 0) { + dest[0] = 0; + return dest; + } + strncpy(dest, source, size); + dest[size - 1] = 0; + return dest; +} + +// String N w/null Copy Truncated? +// return value =true if entire string(source) fit, false if it was truncated +bool strn0cpyt(char* dest, const char* source, uint32 size) { + if (!dest) + return 0; + if (size == 0 || source == 0) { + dest[0] = 0; + return false; + } + strncpy(dest, source, size); + dest[size - 1] = 0; + return (bool)(source[strlen(dest)] == 0); +} + +const char *MakeLowerString(const char *source) { + static char str[128]; + if (!source) + return nullptr; + MakeLowerString(source, str); + return str; +} + +void MakeLowerString(const char *source, char *target) { + if (!source || !target) { + *target = 0; + return; + } + while (*source) + { + *target = tolower(*source); + target++; source++; + } + *target = 0; +} + +int MakeAnyLenString(char** ret, const char* format, ...) { + int buf_len = 128; + int chars = -1; + va_list argptr, tmpargptr; + va_start(argptr, format); + while (chars == -1 || chars >= buf_len) { + safe_delete_array(*ret); + if (chars == -1) + buf_len *= 2; + else + buf_len = chars + 1; + *ret = new char[buf_len]; + va_copy(tmpargptr, argptr); + chars = vsnprintf(*ret, buf_len, format, tmpargptr); + } + va_end(argptr); + return chars; +} + +uint32 AppendAnyLenString(char** ret, uint32* bufsize, uint32* strlen, const char* format, ...) { + if (*bufsize == 0) + *bufsize = 256; + if (*ret == 0) + *strlen = 0; + int chars = -1; + char* oldret = 0; + va_list argptr, tmpargptr; + va_start(argptr, format); + while (chars == -1 || chars >= (int32)(*bufsize - *strlen)) { + if (chars == -1) + *bufsize += 256; + else + *bufsize += chars + 25; + oldret = *ret; + *ret = new char[*bufsize]; + if (oldret) { + if (*strlen) + memcpy(*ret, oldret, *strlen); + safe_delete_array(oldret); + } + va_copy(tmpargptr, argptr); + chars = vsnprintf(&(*ret)[*strlen], (*bufsize - *strlen), format, tmpargptr); + } + va_end(argptr); + *strlen += chars; + return *strlen; +} + +uint32 hextoi(const char* num) { + if (num == nullptr) + return 0; + + int len = strlen(num); + if (len < 3) + return 0; + + if (num[0] != '0' || (num[1] != 'x' && num[1] != 'X')) + return 0; + + uint32 ret = 0; + int mul = 1; + for (int i = len - 1; i >= 2; i--) { + if (num[i] >= 'A' && num[i] <= 'F') + ret += ((num[i] - 'A') + 10) * mul; + else if (num[i] >= 'a' && num[i] <= 'f') + ret += ((num[i] - 'a') + 10) * mul; + else if (num[i] >= '0' && num[i] <= '9') + ret += (num[i] - '0') * mul; + else + return 0; + mul *= 16; + } + return ret; +} + +uint64 hextoi64(const char* num) { + if (num == nullptr) + return 0; + + int len = strlen(num); + if (len < 3) + return 0; + + if (num[0] != '0' || (num[1] != 'x' && num[1] != 'X')) + return 0; + + uint64 ret = 0; + int mul = 1; + for (int i = len - 1; i >= 2; i--) { + if (num[i] >= 'A' && num[i] <= 'F') + ret += ((num[i] - 'A') + 10) * mul; + else if (num[i] >= 'a' && num[i] <= 'f') + ret += ((num[i] - 'a') + 10) * mul; + else if (num[i] >= '0' && num[i] <= '9') + ret += (num[i] - '0') * mul; + else + return 0; + mul *= 16; + } + return ret; +} + +bool atobool(const char* iBool) { + + if (iBool == nullptr) + return false; + if (!strcasecmp(iBool, "true")) + return true; + if (!strcasecmp(iBool, "false")) + return false; + if (!strcasecmp(iBool, "yes")) + return true; + if (!strcasecmp(iBool, "no")) + return false; + if (!strcasecmp(iBool, "on")) + return true; + if (!strcasecmp(iBool, "off")) + return false; + if (!strcasecmp(iBool, "enable")) + return true; + if (!strcasecmp(iBool, "disable")) + return false; + if (!strcasecmp(iBool, "enabled")) + return true; + if (!strcasecmp(iBool, "disabled")) + return false; + if (!strcasecmp(iBool, "y")) + return true; + if (!strcasecmp(iBool, "n")) + return false; + if (atoi(iBool)) + return true; + return false; +} + +// removes the crap and turns the underscores into spaces. +char *CleanMobName(const char *in, char *out) +{ + unsigned i, j; + + for (i = j = 0; i < strlen(in); i++) + { + // convert _ to space.. any other conversions like this? I *think* this + // is the only non alpha char that's not stripped but converted. + if (in[i] == '_') + { + out[j++] = ' '; + } + else + { + if (isalpha(in[i]) || (in[i] == '`')) // numbers, #, or any other crap just gets skipped + out[j++] = in[i]; + } + } + out[j] = 0; // terimnate the string before returning it + return out; +} + + +void RemoveApostrophes(std::string &s) +{ + for (unsigned int i = 0; i < s.length(); ++i) + if (s[i] == '\'') + s[i] = '_'; +} + +char *RemoveApostrophes(const char *s) +{ + auto NewString = new char[strlen(s) + 1]; + + strcpy(NewString, s); + + for (unsigned int i = 0; i < strlen(NewString); ++i) + if (NewString[i] == '\'') + NewString[i] = '_'; + + return NewString; +} + +const char *ConvertArray(int input, char *returnchar) +{ + sprintf(returnchar, "%i", input); + return returnchar; +} + +const char *ConvertArrayF(float input, char *returnchar) +{ + sprintf(returnchar, "%0.2f", input); + return returnchar; +} + +bool isAlphaNumeric(const char *text) +{ + for (unsigned int charIndex = 0; charIndex 'z') && + (text[charIndex] < 'A' || text[charIndex] > 'Z') && + (text[charIndex] < '0' || text[charIndex] > '9')) + return false; + } + + return true; +} diff --git a/old/string_util.h b/old/string_util.h new file mode 100644 index 0000000..037d6a2 --- /dev/null +++ b/old/string_util.h @@ -0,0 +1,193 @@ +/* + * Copyright 2013 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef _STRINGUTIL_H_ +#define _STRINGUTIL_H_ + +#include +#include +#include +#include +#include + +#ifndef _WIN32 +// this doesn't appear to affect linux-based systems..need feedback for _WIN64 +#include +#endif + +#ifdef _WINDOWS +#include +#include +#include +#endif + +#include "types.h" + +//std::string based +const std::string str_tolower(std::string s); +const std::string str_toupper(std::string s); +const std::string ucfirst(std::string s); +std::vector split(std::string str_to_split, char delimiter); +const std::string StringFormat(const char* format, ...); +const std::string vStringFormat(const char* format, va_list args); +std::string implode(std::string glue, std::vector src); + +/** + * @param str + * @param chars + * @return + */ +inline std::string <rim(std::string &str, const std::string &chars = "\t\n\v\f\r ") +{ + str.erase(0, str.find_first_not_of(chars)); + return str; +} + +/** + * @param str + * @param chars + * @return + */ +inline std::string &rtrim(std::string &str, const std::string &chars = "\t\n\v\f\r ") +{ + str.erase(str.find_last_not_of(chars) + 1); + return str; +} + +/** + * @param str + * @param chars + * @return + */ +inline std::string &trim(std::string &str, const std::string &chars = "\t\n\v\f\r ") +{ + return ltrim(rtrim(str, chars), chars); +} + +template +std::string implode(const std::string &glue, const std::pair &encapsulation, const std::vector &src) +{ + if (src.empty()) { + return {}; + } + + std::ostringstream oss; + + for (const T &src_iter : src) { + oss << encapsulation.first << src_iter << encapsulation.second << glue; + } + + std::string output(oss.str()); + output.resize(output.size() - glue.size()); + + return output; +} + +// _WIN32 builds require that #include be included in whatever code file the invocation is made from (no header files) +template +std::vector join_pair(const std::string &glue, const std::pair &encapsulation, const std::vector> &src) +{ + if (src.empty()) { + return {}; + } + + std::vector output; + + for (const std::pair &src_iter : src) { + output.push_back( + + fmt::format( + "{}{}{}{}{}{}{}", + encapsulation.first, + src_iter.first, + encapsulation.second, + glue, + encapsulation.first, + src_iter.second, + encapsulation.second + ) + ); + } + + return output; +} + +// _WIN32 builds require that #include be included in whatever code file the invocation is made from (no header files) +template +std::vector join_tuple(const std::string &glue, const std::pair &encapsulation, const std::vector> &src) +{ + if (src.empty()) { + return {}; + } + + std::vector output; + + for (const std::tuple &src_iter : src) { + + output.push_back( + + fmt::format( + "{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", + encapsulation.first, + std::get<0>(src_iter), + encapsulation.second, + glue, + encapsulation.first, + std::get<1>(src_iter), + encapsulation.second, + glue, + encapsulation.first, + std::get<2>(src_iter), + encapsulation.second, + glue, + encapsulation.first, + std::get<3>(src_iter), + encapsulation.second + ) + ); + } + + return output; +} + +std::vector SplitString(const std::string &s, char delim); +std::string EscapeString(const char *src, size_t sz); +std::string EscapeString(const std::string &s); +bool StringIsNumber(const std::string &s); +void ToLowerString(std::string &s); +void ToUpperString(std::string &s); +std::string JoinString(const std::vector& ar, const std::string &delim); +void find_replace(std::string& string_subject, const std::string& search_string, const std::string& replace_string); +void ParseAccountString(const std::string &s, std::string &account, std::string &loginserver); + +//const char based + +bool atobool(const char* iBool); +bool isAlphaNumeric(const char *text); +bool strn0cpyt(char* dest, const char* source, uint32 size); +char *CleanMobName(const char *in, char *out); +char *RemoveApostrophes(const char *s); +char* strn0cpy(char* dest, const char* source, uint32 size); +const char *ConvertArray(int input, char *returnchar); +const char *ConvertArrayF(float input, char *returnchar); +const char *MakeLowerString(const char *source); +int MakeAnyLenString(char** ret, const char* format, ...); +uint32 AppendAnyLenString(char** ret, uint32* bufsize, uint32* strlen, const char* format, ...); +uint32 hextoi(const char* num); +uint64 hextoi64(const char* num); +void MakeLowerString(const char *source, char *target); +void RemoveApostrophes(std::string &s); + +#endif diff --git a/old/timer.cpp b/old/timer.cpp new file mode 100644 index 0000000..e9c08ef --- /dev/null +++ b/old/timer.cpp @@ -0,0 +1,207 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#include "../common/debug.h" +// Disgrace: for windows compile +#ifndef WIN32 + #include +#else + #include +#endif + +#include +using namespace std; + +#include "timer.h" + +int32 started_unix_timestamp = 0; +int32 current_time = 0; +int32 last_time = 0; + +Timer::Timer(){ + timer_time = 30000; //default to 30 seconds + start_time = current_time; + set_at_trigger = timer_time; + pUseAcurateTiming = false; + enabled = false; +} +Timer::Timer(int32 in_timer_time, bool iUseAcurateTiming) { + timer_time = in_timer_time; + start_time = current_time; + set_at_trigger = timer_time; + pUseAcurateTiming = iUseAcurateTiming; + if (timer_time == 0) { + enabled = false; + } + else { + enabled = true; + } +} + +Timer::Timer(int32 start, int32 timer, bool iUseAcurateTiming = false) { + timer_time = timer; + start_time = start; + set_at_trigger = timer_time; + pUseAcurateTiming = iUseAcurateTiming; + if (timer_time == 0) { + enabled = false; + } + else { + enabled = true; + } +} + +/* Reimplemented for MSVC - Bounce */ +#ifdef WIN32 +int gettimeofday (timeval *tp, ...) +{ + timeb tb; + + ftime (&tb); + + tp->tv_sec = tb.time; + tp->tv_usec = tb.millitm * 1000; + + return 0; +} +#endif + +/* This function checks if the timer triggered */ +bool Timer::Check(bool iReset) +{ + if (enabled && current_time-start_time > timer_time) { + if (iReset) { + if (pUseAcurateTiming) + start_time += timer_time; + else + start_time = current_time; // Reset timer + timer_time = set_at_trigger; + } + return true; + } + + return false; +} + +/* This function disables the timer */ +void Timer::Disable() { + enabled = false; +} + +void Timer::Enable() { + enabled = true; +} + +/* This function set the timer and restart it */ +void Timer::Start(int32 set_timer_time, bool ChangeResetTimer) { + start_time = current_time; + enabled = true; + if (set_timer_time != 0) + { + timer_time = set_timer_time; + if (ChangeResetTimer) + set_at_trigger = set_timer_time; + } +} + +/* This timer updates the timer without restarting it */ +void Timer::SetTimer(int32 set_timer_time) { + /* If we were disabled before => restart the timer */ + if (!enabled) { + start_time = current_time; + enabled = true; + } + if (set_timer_time != 0) { + timer_time = set_timer_time; + set_at_trigger = set_timer_time; + } +} + +int32 Timer::GetElapsedTime(){ + if (enabled) { + return current_time - start_time; + } + else { + return 0xFFFFFFFF; + } +} + +int32 Timer::GetRemainingTime() { + if (enabled) { + if (current_time-start_time > timer_time) + return 0; + else + return (start_time + timer_time) - current_time; + } + else { + return 0xFFFFFFFF; + } +} + +void Timer::SetAtTrigger(int32 in_set_at_trigger, bool iEnableIfDisabled) { + set_at_trigger = in_set_at_trigger; + if (!Enabled() && iEnableIfDisabled) { + Enable(); + } +} + +void Timer::Trigger() +{ + enabled = true; + + timer_time = set_at_trigger; + start_time = current_time-timer_time-1; +} + +const int32& Timer::GetCurrentTime2() +{ + return current_time; +} + +const int32& Timer::SetCurrentTime() +{ + struct timeval read_time; + int32 this_time; + + gettimeofday(&read_time,0); + if(started_unix_timestamp == 0) + started_unix_timestamp = read_time.tv_sec; + + this_time = (read_time.tv_sec - started_unix_timestamp) * 1000 + read_time.tv_usec / 1000; + + if (last_time == 0) + { + current_time = 0; + } + else + { + current_time += this_time - last_time; + } + + last_time = this_time; + +// cerr << "Current time:" << current_time << endl; + return current_time; +} + +int32 Timer::GetUnixTimeStamp(){ + struct timeval read_time; + gettimeofday(&read_time,0); + return read_time.tv_sec; +} diff --git a/old/timer.h b/old/timer.h new file mode 100644 index 0000000..a70a2d2 --- /dev/null +++ b/old/timer.h @@ -0,0 +1,88 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#ifndef TIMER_H +#define TIMER_H + +#include "types.h" +#include + +// Disgrace: for windows compile +#ifdef WIN32 + #include + #include + int gettimeofday (timeval *tp, ...); +#endif + +class Timer +{ +public: + Timer(); + Timer(int32 timer_time, bool iUseAcurateTiming = false); + Timer(int32 start, int32 timer, bool iUseAcurateTiming); + ~Timer() { } + + bool Check(bool iReset = true); + void Enable(); + void Disable(); + void Start(int32 set_timer_time=0, bool ChangeResetTimer = true); + void SetTimer(int32 set_timer_time=0); + int32 GetRemainingTime(); + int32 GetElapsedTime(); + inline const int32& GetTimerTime() { return timer_time; } + inline const int32& GetSetAtTrigger() { return set_at_trigger; } + void Trigger(); + void SetAtTrigger(int32 set_at_trigger, bool iEnableIfDisabled = false); + + inline bool Enabled() { return enabled; } + inline int32 GetStartTime() { return(start_time); } + inline int32 GetDuration() { return(timer_time); } + + static const int32& SetCurrentTime(); + static const int32& GetCurrentTime2(); + static int32 GetUnixTimeStamp(); + +private: + int32 start_time; + int32 timer_time; + bool enabled; + int32 set_at_trigger; + + // Tells the timer to be more acurate about happening every X ms. + // Instead of Check() setting the start_time = now, + // it it sets it to start_time += timer_time + bool pUseAcurateTiming; + +// static int32 current_time; +// static int32 last_time; +}; + +struct BenchTimer +{ + typedef std::chrono::high_resolution_clock clock; + + BenchTimer() : start_time(clock::now()) {} + void reset() { start_time = clock::now(); } + // this is seconds + double elapsed() { return std::chrono::duration(clock::now() - start_time).count(); } +private: + std::chrono::time_point start_time; +}; + +#endif diff --git a/old/types.h b/old/types.h new file mode 100644 index 0000000..c65ccf1 --- /dev/null +++ b/old/types.h @@ -0,0 +1,191 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#ifndef TYPES_H +#define TYPES_H + +#include + +using namespace std; + +//atoi is not int32 or uint32 safe!!!! +#define atoul(str) strtoul(str, NULL, 10) +#ifdef WIN32 + #define atoi64(str) _atoi64(str) +#else + #define atoi64(str) strtoll(str, NULL, 10) +#endif +typedef unsigned char int8; +typedef unsigned short int16; +typedef unsigned int int32; + +typedef unsigned char uint8; +typedef signed char sint8; +typedef unsigned short uint16; +typedef signed short sint16; +typedef unsigned int uint32; +typedef signed int sint32; + +#ifdef WIN32 + #if defined(_INTEGRAL_MAX_BITS) && _INTEGRAL_MAX_BITS >= 64 + typedef unsigned __int64 int64; + typedef unsigned __int64 uint64; + typedef signed __int64 sint64; + #else + #error __int64 not supported + #endif +#else +typedef unsigned long long int64; +typedef unsigned long long uint64; +typedef signed long long sint64; +//typedef __u64 int64; +//typedef __u64 uint64; +//typedef __s64 sint64; +#endif + +typedef unsigned long ulong; +typedef unsigned short ushort; +typedef unsigned char uchar; + +#ifdef WIN32 + #define snprintf _snprintf + #define vsnprintf _vsnprintf + #define strncasecmp _strnicmp + #define strcasecmp _stricmp + typedef void ThreadReturnType; +// #define THREAD_RETURN(x) return; + #define THREAD_RETURN(x) _endthread(); return; +#else + typedef void* ThreadReturnType; + typedef int SOCKET; + #define THREAD_RETURN(x) return(x); +#endif + +#define safe_delete(d) if(d) { delete d; d=0; } +#define safe_delete_array(d) if(d) { delete[] d; d=0; } +#define L32(i) ((int32) i) +#define H32(i) ((int32) (i >> 32)) +#define L16(i) ((int16) i) + +#ifndef WIN32 +// More WIN32 compatability + typedef unsigned long DWORD; + typedef unsigned char BYTE; + typedef char CHAR; + typedef unsigned short WORD; + typedef float FLOAT; + typedef FLOAT *PFLOAT; + typedef BYTE *PBYTE,*LPBYTE; + typedef int *PINT,*LPINT; + typedef WORD *PWORD,*LPWORD; + typedef long *LPLONG, LONG; + typedef DWORD *PDWORD,*LPDWORD; + typedef int INT; + typedef unsigned int UINT,*PUINT,*LPUINT; +#endif + + +#ifdef WIN32 +#define DLLFUNC extern "C" __declspec(dllexport) +#else +#define DLLFUNC extern "C" +#endif + + +#pragma pack(1) +struct uint16_breakdown { + union { + uint16 all; + struct { + uint8 b1; + uint8 b2; + } bytes; + }; + inline uint16& operator=(const uint16& val) { return (all=val); } + inline uint16* operator&() { return &all; } + inline operator uint16&() { return all; } + inline uint8& b1() { return bytes.b1; } + inline uint8& b2() { return bytes.b2; } +}; + +struct uint32_breakdown { + union { + uint32 all; + struct { + uint16 w1; + uint16 w2; + } words; + struct { + uint8 b1; + union { + struct { + uint8 b2; + uint8 b3; + } middle; + uint16 w2_3; // word bytes 2 to 3 + }; + uint8 b4; + } bytes; + }; + inline uint32& operator=(const uint32& val) { return (all=val); } + inline uint32* operator&() { return &all; } + inline operator uint32&() { return all; } + + inline uint16& w1() { return words.w1; } + inline uint16& w2() { return words.w2; } + inline uint16& w2_3() { return bytes.w2_3; } + inline uint8& b1() { return bytes.b1; } + inline uint8& b2() { return bytes.middle.b2; } + inline uint8& b3() { return bytes.middle.b3; } + inline uint8& b4() { return bytes.b4; } +}; + +struct EQ2_32BitString{ + int32 size; + string data; +}; +struct EQ2_16BitString{ + int16 size; + string data; +}; +struct EQ2_8BitString{ + int8 size; + string data; +}; + +struct EQ2_Color{ + int8 red; + int8 green; + int8 blue; +}; + +struct WorldTime{ + int16 year; + int month; + int day; + int hour; + int minute; +}; + +typedef enum QUERY_TYPE{ Q_SELECT, Q_UPDATE, Q_REPLACE, Q_INSERT, Q_DELETE, Q_DBMS} QUERY_TYPE; + +#pragma pack() + + +#endif diff --git a/old/unix.cpp b/old/unix.cpp new file mode 100644 index 0000000..6476c3a --- /dev/null +++ b/old/unix.cpp @@ -0,0 +1,45 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#include "unix.h" +#include +#include + +void Sleep(unsigned int x) { + if (x > 0) + usleep(x*1000); +} + +char* strupr(char* tmp) { + int l = strlen(tmp); + for (int x = 0; x < l; x++) { + tmp[x] = toupper(tmp[x]); + } + return tmp; +} + +char* strlwr(char* tmp) { + int l = strlen(tmp); + for (int x = 0; x < l; x++) { + tmp[x] = tolower(tmp[x]); + } + return tmp; +} + + diff --git a/old/unix.h b/old/unix.h new file mode 100644 index 0000000..82560a1 --- /dev/null +++ b/old/unix.h @@ -0,0 +1,34 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ +#ifndef WIN32 +#ifndef __UNIX_H__ +#define __UNIX_H__ + #ifndef PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP + #define PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP {0, 0, 0, PTHREAD_MUTEX_RECURSIVE_NP, __LOCK_INITIALIZER} + #endif +#include + +typedef int SOCKET; + +void Sleep(unsigned int x); +char* strupr(char* tmp); +char* strlwr(char* tmp); +#endif +#endif diff --git a/old/version.h b/old/version.h new file mode 100644 index 0000000..0d6beb4 --- /dev/null +++ b/old/version.h @@ -0,0 +1,56 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + EQ2Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . +*/ + +#include "LogTypes.h" + +#ifndef VERSION_H +#define VERSION_H + +#define CURRENT_DATABASE_MINORVERSION 43 +#define CURRENT_DATABASE_MAJORVERSION 730 +#if defined(LOGIN) + #define EQ2EMU_MODULE "EQ2EMu LoginServer" +#elif defined(PATCHER) + #define EQ2EMU_MODULE "EQ2EMu PatchServer" +#elif defined(CHAT) + #define EQ2EMU_MODULE "EQ2EMu ChatServer" +#elif defined(ZONE) + #define EQ2EMU_MODULE "EQ2EMu ZoneServer" +#else + #define EQ2EMU_MODULE "EQ2EMu WorldServer" +#endif + +#if defined(LOGIN) +#define CURRENT_VERSION "0.9.9-Nebula" +#elif defined(WORLD) +#define CURRENT_VERSION "0.9.9-Nebula" +#else +#define CURRENT_VERSION "0.9.9-Nebula" +#endif + +#define COMPILE_DATE __DATE__ +#define COMPILE_TIME __TIME__ +#ifndef WIN32 + #define LAST_MODIFIED __TIME__ +#else + #define LAST_MODIFIED __TIMESTAMP__ +#endif + +#endif \ No newline at end of file diff --git a/old/xmlParser.cpp b/old/xmlParser.cpp new file mode 100644 index 0000000..e0f7f33 --- /dev/null +++ b/old/xmlParser.cpp @@ -0,0 +1,2974 @@ +/** + **************************************************************************** + *

XML.c - implementation file for basic XML parser written in ANSI C++ + * for portability. It works by using recursion and a node tree for breaking + * down the elements of an XML document.

+ * + * @version V2.44 + * @author Frank Vanden Berghen + * + * NOTE: + * + * If you add "#define STRICT_PARSING", on the first line of this file + * the parser will see the following XML-stream: + * some textother text + * as an error. Otherwise, this tring will be equivalent to: + * some textother text + * + * NOTE: + * + * If you add "#define APPROXIMATE_PARSING" on the first line of this file + * the parser will see the following XML-stream: + * + * + * + * as equivalent to the following XML-stream: + * + * + * + * This can be useful for badly-formed XML-streams but prevent the use + * of the following XML-stream (problem is: tags at contiguous levels + * have the same names): + * + * + * + * + * + * + * NOTE: + * + * If you add "#define _XMLPARSER_NO_MESSAGEBOX_" on the first line of this file + * the "openFileHelper" function will always display error messages inside the + * console instead of inside a message-box-window. Message-box-windows are + * available on windows 9x/NT/2000/XP/Vista only. + * + * Copyright (c) 2002, Frank Vanden Berghen - All rights reserved. + * Commercialized by Business-Insight + * See the file "AFPL-license.txt about the licensing terms + * + **************************************************************************** + */ +#ifndef _CRT_SECURE_NO_DEPRECATE +#define _CRT_SECURE_NO_DEPRECATE +#endif +#include "xmlParser.h" +#ifdef _XMLWINDOWS +//#ifdef _DEBUG +//#define _CRTDBG_MAP_ALLOC +//#include +//#endif +#define WIN32_LEAN_AND_MEAN +#include // to have IsTextUnicode, MultiByteToWideChar, WideCharToMultiByte to handle unicode files + // to have "MessageBoxA" to display error messages for openFilHelper +#endif + +#include +#include +#include +#include +#include + +XMLCSTR XMLNode::getVersion() { return _CXML("v2.44"); } +void freeXMLString(XMLSTR t){if(t)free(t);} + +static XMLNode::XMLCharEncoding characterEncoding=XMLNode::char_encoding_UTF8; +static char guessWideCharChars=1, dropWhiteSpace=1, removeCommentsInMiddleOfText=1; + +inline int mmin( const int t1, const int t2 ) { return t1 < t2 ? t1 : t2; } + +// You can modify the initialization of the variable "XMLClearTags" below +// to change the clearTags that are currently recognized by the library. +// The number on the second columns is the length of the string inside the +// first column. +// The "") }, + { _CXML("") }, + { _CXML("") }, + { _CXML("
")    ,5,  _CXML("
") }, +// { _CXML("")}, + { NULL ,0, NULL } +}; + +// You can modify the initialization of the variable "XMLEntities" below +// to change the character entities that are currently recognized by the library. +// The number on the second columns is the length of the string inside the +// first column. Additionally, the syntaxes " " and " " are recognized. +typedef struct { XMLCSTR s; int l; XMLCHAR c;} XMLCharacterEntity; +static XMLCharacterEntity XMLEntities[] = +{ + { _CXML("&" ), 5, _CXML('&' )}, + { _CXML("<" ), 4, _CXML('<' )}, + { _CXML(">" ), 4, _CXML('>' )}, + { _CXML("""), 6, _CXML('\"')}, + { _CXML("'"), 6, _CXML('\'')}, + { NULL , 0, '\0' } +}; + +// When rendering the XMLNode to a string (using the "createXMLString" function), +// you can ask for a beautiful formatting. This formatting is using the +// following indentation character: +#define INDENTCHAR _CXML('\t') + +// The following function parses the XML errors into a user friendly string. +// You can edit this to change the output language of the library to something else. +XMLCSTR XMLNode::getError(XMLError xerror) +{ + switch (xerror) + { + case eXMLErrorNone: return _CXML("No error"); + case eXMLErrorMissingEndTag: return _CXML("Warning: Unmatched end tag"); + case eXMLErrorNoXMLTagFound: return _CXML("Warning: No XML tag found"); + case eXMLErrorEmpty: return _CXML("Error: No XML data"); + case eXMLErrorMissingTagName: return _CXML("Error: Missing start tag name"); + case eXMLErrorMissingEndTagName: return _CXML("Error: Missing end tag name"); + case eXMLErrorUnmatchedEndTag: return _CXML("Error: Unmatched end tag"); + case eXMLErrorUnmatchedEndClearTag: return _CXML("Error: Unmatched clear tag end"); + case eXMLErrorUnexpectedToken: return _CXML("Error: Unexpected token found"); + case eXMLErrorNoElements: return _CXML("Error: No elements found"); + case eXMLErrorFileNotFound: return _CXML("Error: File not found"); + case eXMLErrorFirstTagNotFound: return _CXML("Error: First Tag not found"); + case eXMLErrorUnknownCharacterEntity:return _CXML("Error: Unknown character entity"); + case eXMLErrorCharacterCodeAbove255: return _CXML("Error: Character code above 255 is forbidden in MultiByte char mode."); + case eXMLErrorCharConversionError: return _CXML("Error: unable to convert between WideChar and MultiByte chars"); + case eXMLErrorCannotOpenWriteFile: return _CXML("Error: unable to open file for writing"); + case eXMLErrorCannotWriteFile: return _CXML("Error: cannot write into file"); + + case eXMLErrorBase64DataSizeIsNotMultipleOf4: return _CXML("Warning: Base64-string length is not a multiple of 4"); + case eXMLErrorBase64DecodeTruncatedData: return _CXML("Warning: Base64-string is truncated"); + case eXMLErrorBase64DecodeIllegalCharacter: return _CXML("Error: Base64-string contains an illegal character"); + case eXMLErrorBase64DecodeBufferTooSmall: return _CXML("Error: Base64 decode output buffer is too small"); + }; + return _CXML("Unknown"); +} + +///////////////////////////////////////////////////////////////////////// +// Here start the abstraction layer to be OS-independent // +///////////////////////////////////////////////////////////////////////// + +// Here is an abstraction layer to access some common string manipulation functions. +// The abstraction layer is currently working for gcc, Microsoft Visual Studio 6.0, +// Microsoft Visual Studio .NET, CC (sun compiler) and Borland C++. +// If you plan to "port" the library to a new system/compiler, all you have to do is +// to edit the following lines. +#ifdef XML_NO_WIDE_CHAR +char myIsTextWideChar(const void *b, int len) { return FALSE; } +#else + #if defined (UNDER_CE) || !defined(_XMLWINDOWS) + char myIsTextWideChar(const void *b, int len) // inspired by the Wine API: RtlIsTextUnicode + { +#ifdef sun + // for SPARC processors: wchar_t* buffers must always be alligned, otherwise it's a char* buffer. + if ((((unsigned long)b)%sizeof(wchar_t))!=0) return FALSE; +#endif + const wchar_t *s=(const wchar_t*)b; + + // buffer too small: + if (len<(int)sizeof(wchar_t)) return FALSE; + + // odd length test + if (len&1) return FALSE; + + /* only checks the first 256 characters */ + len=mmin(256,len/sizeof(wchar_t)); + + // Check for the special byte order: + if (*((unsigned short*)s) == 0xFFFE) return TRUE; // IS_TEXT_UNICODE_REVERSE_SIGNATURE; + if (*((unsigned short*)s) == 0xFEFF) return TRUE; // IS_TEXT_UNICODE_SIGNATURE + + // checks for ASCII characters in the UNICODE stream + int i,stats=0; + for (i=0; ilen/2) return TRUE; + + // Check for UNICODE NULL chars + for (i=0; i + static inline int xstrnicmp(XMLCSTR c1, XMLCSTR c2, int l) { return wsncasecmp(c1,c2,l);} + static inline int xstrncmp(XMLCSTR c1, XMLCSTR c2, int l) { return wsncmp(c1,c2,l);} + static inline int xstricmp(XMLCSTR c1, XMLCSTR c2) { return wscasecmp(c1,c2); } + #else + static inline int xstrncmp(XMLCSTR c1, XMLCSTR c2, int l) { return wcsncmp(c1,c2,l);} + #ifdef __linux__ + // for gcc/linux + static inline int xstrnicmp(XMLCSTR c1, XMLCSTR c2, int l) { return wcsncasecmp(c1,c2,l);} + static inline int xstricmp(XMLCSTR c1, XMLCSTR c2) { return wcscasecmp(c1,c2); } + #else + #include + // for gcc/non-linux (MacOS X 10.3, FreeBSD 6.0, NetBSD 3.0, OpenBSD 3.8, AIX 4.3.2, HP-UX 11, IRIX 6.5, OSF/1 5.1, Cygwin, mingw) + static inline int xstricmp(XMLCSTR c1, XMLCSTR c2) + { + wchar_t left,right; + do + { + left=towlower(*c1++); right=towlower(*c2++); + } while (left&&(left==right)); + return (int)left-(int)right; + } + static inline int xstrnicmp(XMLCSTR c1, XMLCSTR c2, int l) + { + wchar_t left,right; + while(l--) + { + left=towlower(*c1++); right=towlower(*c2++); + if ((!left)||(left!=right)) return (int)left-(int)right; + } + return 0; + } + #endif + #endif + static inline XMLSTR xstrstr(XMLCSTR c1, XMLCSTR c2) { return (XMLSTR)wcsstr(c1,c2); } + static inline XMLSTR xstrcpy(XMLSTR c1, XMLCSTR c2) { return (XMLSTR)wcscpy(c1,c2); } + static inline FILE *xfopen(XMLCSTR filename,XMLCSTR mode) + { + char *filenameAscii=myWideCharToMultiByte(filename); + FILE *f; + if (mode[0]==_CXML('r')) f=fopen(filenameAscii,"rb"); + else f=fopen(filenameAscii,"wb"); + free(filenameAscii); + return f; + } + #else + static inline FILE *xfopen(XMLCSTR filename,XMLCSTR mode) { return fopen(filename,mode); } + static inline int xstrlen(XMLCSTR c) { return strlen(c); } + static inline int xstrnicmp(XMLCSTR c1, XMLCSTR c2, int l) { return strncasecmp(c1,c2,l);} + static inline int xstrncmp(XMLCSTR c1, XMLCSTR c2, int l) { return strncmp(c1,c2,l);} + static inline int xstricmp(XMLCSTR c1, XMLCSTR c2) { return strcasecmp(c1,c2); } + static inline XMLSTR xstrstr(XMLCSTR c1, XMLCSTR c2) { return (XMLSTR)strstr(c1,c2); } + static inline XMLSTR xstrcpy(XMLSTR c1, XMLCSTR c2) { return (XMLSTR)strcpy(c1,c2); } + #endif + static inline int _strnicmp(const char *c1,const char *c2, int l) { return strncasecmp(c1,c2,l);} +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// the "xmltoc,xmltob,xmltoi,xmltol,xmltof,xmltoa" functions // +/////////////////////////////////////////////////////////////////////////////// +// These 6 functions are not used inside the XMLparser. +// There are only here as "convenience" functions for the user. +// If you don't need them, you can delete them without any trouble. +#ifdef _XMLWIDECHAR + #ifdef _XMLWINDOWS + // for Microsoft Visual Studio 6.0 and Microsoft Visual Studio .NET and Borland C++ Builder 6.0 + char xmltob(XMLCSTR t,char v){ if (t&&(*t)) return (char)_wtoi(t); return v; } + int xmltoi(XMLCSTR t,int v){ if (t&&(*t)) return _wtoi(t); return v; } + long long xmltol(XMLCSTR t,long long v){ if (t&&(*t)) return _wtoi64(t); return v; } + double xmltof(XMLCSTR t,double v){ if (t&&(*t)) swscanf(t, L"%lf", &v); /*v=_wtof(t);*/ return v; } + #else + #ifdef sun + // for CC + #include + char xmltob(XMLCSTR t,char v){ if (t) return (char)wstol(t,NULL,10); return v; } + int xmltoi(XMLCSTR t,int v){ if (t) return (int)wstol(t,NULL,10); return v; } + long long xmltol(XMLCSTR t,long long v){ if (t) return wstol(t,NULL,10); return v; } + #else + // for gcc + char xmltob(XMLCSTR t,char v){ if (t) return (char)wcstol(t,NULL,10); return v; } + int xmltoi(XMLCSTR t,int v){ if (t) return (int)wcstol(t,NULL,10); return v; } + long long xmltol(XMLCSTR t,long long v){ if (t) return wcstol(t,NULL,10); return v; } + #endif + double xmltof(XMLCSTR t,double v){ if (t&&(*t)) swscanf(t, L"%lf", &v); /*v=_wtof(t);*/ return v; } + #endif +#else + #ifdef _XMLWINDOWS + long long xmltol(XMLCSTR t,long long v){ if (t&&(*t)) return _atoi64(t); return v; } + #else + long long xmltol(XMLCSTR t,long long v){ if (t&&(*t)) return atoll(t); return v; } + #endif + char xmltob(XMLCSTR t,char v){ if (t&&(*t)) return (char)atoi(t); return v; } + int xmltoi(XMLCSTR t,int v){ if (t&&(*t)) return atoi(t); return v; } + double xmltof(XMLCSTR t,double v){ if (t&&(*t)) return atof(t); return v; } +#endif +XMLCSTR xmltoa(XMLCSTR t, XMLCSTR v){ if (t) return t; return v; } +XMLCHAR xmltoc(XMLCSTR t,const XMLCHAR v){ if (t&&(*t)) return *t; return v; } + +///////////////////////////////////////////////////////////////////////// +// the "openFileHelper" function // +///////////////////////////////////////////////////////////////////////// + +// Since each application has its own way to report and deal with errors, you should modify & rewrite +// the following "openFileHelper" function to get an "error reporting mechanism" tailored to your needs. +XMLNode XMLNode::openFileHelper(XMLCSTR filename, XMLCSTR tag) +{ + // guess the value of the global parameter "characterEncoding" + // (the guess is based on the first 200 bytes of the file). + FILE *f=xfopen(filename,_CXML("rb")); + if (f) + { + char bb[205]; + int l=(int)fread(bb,1,200,f); + setGlobalOptions(guessCharEncoding(bb,l),guessWideCharChars,dropWhiteSpace,removeCommentsInMiddleOfText); + fclose(f); + } + + // parse the file + XMLResults pResults; + XMLNode xnode=XMLNode::parseFile(filename,tag,&pResults); + + // display error message (if any) + if (pResults.error != eXMLErrorNone) + { + // create message + char message[2000],*s1=(char*)"",*s3=(char*)""; XMLCSTR s2=_CXML(""); + if (pResults.error==eXMLErrorFirstTagNotFound) { s1=(char*)"First Tag should be '"; s2=tag; s3=(char*)"'.\n"; } +#ifdef _XMLWINDOWS + _snprintf(message,2000, +#else + snprintf(message,2000, +#endif +#ifdef _XMLWIDECHAR + "XML Parsing error inside file '%S'.\n%S\nAt line %i, column %i.\n%s%S%s" +#else + "XML Parsing error inside file '%s'.\n%s\nAt line %i, column %i.\n%s%s%s" +#endif + ,filename,XMLNode::getError(pResults.error),pResults.nLine,pResults.nColumn,s1,s2,s3); + + // display message +#if defined(_XMLWINDOWS) && !defined(UNDER_CE) && !defined(_XMLPARSER_NO_MESSAGEBOX_) + MessageBoxA(NULL,message,"XML Parsing error",MB_OK|MB_ICONERROR|MB_TOPMOST); +#else + printf("%s",message); +#endif + exit(255); + } + return xnode; +} + +///////////////////////////////////////////////////////////////////////// +// Here start the core implementation of the XMLParser library // +///////////////////////////////////////////////////////////////////////// + +// You should normally not change anything below this point. + +#ifndef _XMLWIDECHAR +// If "characterEncoding=ascii" then we assume that all characters have the same length of 1 byte. +// If "characterEncoding=UTF8" then the characters have different lengths (from 1 byte to 4 bytes). +// If "characterEncoding=ShiftJIS" then the characters have different lengths (from 1 byte to 2 bytes). +// This table is used as lookup-table to know the length of a character (in byte) based on the +// content of the first byte of the character. +// (note: if you modify this, you must always have XML_utf8ByteTable[0]=0 ). +static const char XML_utf8ByteTable[256] = +{ + // 0 1 2 3 4 5 6 7 8 9 a b c d e f + 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x00 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x10 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x20 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x30 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x40 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x50 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x60 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x70 End of ASCII range + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x80 0x80 to 0xc1 invalid + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x90 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0xa0 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0xb0 + 1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xc0 0xc2 to 0xdf 2 byte + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xd0 + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,// 0xe0 0xe0 to 0xef 3 byte + 4,4,4,4,4,1,1,1,1,1,1,1,1,1,1,1 // 0xf0 0xf0 to 0xf4 4 byte, 0xf5 and higher invalid +}; +static const char XML_legacyByteTable[256] = +{ + 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +}; +static const char XML_sjisByteTable[256] = +{ + // 0 1 2 3 4 5 6 7 8 9 a b c d e f + 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x00 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x10 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x20 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x30 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x40 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x50 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x60 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x70 + 1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0x80 0x81 to 0x9F 2 bytes + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0x90 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0xa0 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0xb0 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0xc0 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0xd0 + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xe0 0xe0 to 0xef 2 bytes + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 // 0xf0 +}; +static const char XML_gb2312ByteTable[256] = +{ +// 0 1 2 3 4 5 6 7 8 9 a b c d e f + 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x00 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x10 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x20 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x30 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x40 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x50 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x60 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x70 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x80 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x90 + 1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xa0 0xa1 to 0xf7 2 bytes + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xb0 + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xc0 + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xd0 + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xe0 + 2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1 // 0xf0 +}; +static const char XML_gbk_big5_ByteTable[256] = +{ + // 0 1 2 3 4 5 6 7 8 9 a b c d e f + 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x00 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x10 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x20 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x30 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x40 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x50 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x60 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x70 + 1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0x80 0x81 to 0xfe 2 bytes + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0x90 + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xa0 + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xb0 + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xc0 + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xd0 + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xe0 + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1 // 0xf0 +}; +static const char *XML_ByteTable=(const char *)XML_utf8ByteTable; // the default is "characterEncoding=XMLNode::encoding_UTF8" +#endif + + +XMLNode XMLNode::emptyXMLNode; +XMLClear XMLNode::emptyXMLClear={ NULL, NULL, NULL}; +XMLAttribute XMLNode::emptyXMLAttribute={ NULL, NULL}; + +// Enumeration used to decipher what type a token is +typedef enum XMLTokenTypeTag +{ + eTokenText = 0, + eTokenQuotedText, + eTokenTagStart, /* "<" */ + eTokenTagEnd, /* "" */ + eTokenEquals, /* "=" */ + eTokenDeclaration, /* "" */ + eTokenClear, + eTokenError +} XMLTokenType; + +// Main structure used for parsing XML +typedef struct XML +{ + XMLCSTR lpXML; + XMLCSTR lpszText; + int nIndex,nIndexMissigEndTag; + enum XMLError error; + XMLCSTR lpEndTag; + int cbEndTag; + XMLCSTR lpNewElement; + int cbNewElement; + int nFirst; +} XML; + +typedef struct +{ + ALLXMLClearTag *pClr; + XMLCSTR pStr; +} NextToken; + +// Enumeration used when parsing attributes +typedef enum Attrib +{ + eAttribName = 0, + eAttribEquals, + eAttribValue +} Attrib; + +// Enumeration used when parsing elements to dictate whether we are currently +// inside a tag +typedef enum XMLStatus +{ + eInsideTag = 0, + eOutsideTag +} XMLStatus; + +XMLError XMLNode::writeToFile(XMLCSTR filename, const char *encoding, char nFormat) const +{ + if (!d) return eXMLErrorNone; + FILE *f=xfopen(filename,_CXML("wb")); + if (!f) return eXMLErrorCannotOpenWriteFile; +#ifdef _XMLWIDECHAR + unsigned char h[2]={ 0xFF, 0xFE }; + if (!fwrite(h,2,1,f)) + { + fclose(f); + return eXMLErrorCannotWriteFile; + } + if ((!isDeclaration())&&((d->lpszName)||(!getChildNode().isDeclaration()))) + { + if (!fwrite(L"\n",sizeof(wchar_t)*40,1,f)) + { + fclose(f); + return eXMLErrorCannotWriteFile; + } + } +#else + if ((!isDeclaration())&&((d->lpszName)||(!getChildNode().isDeclaration()))) + { + if (characterEncoding==char_encoding_UTF8) + { + // header so that windows recognize the file as UTF-8: + unsigned char h[3]={0xEF,0xBB,0xBF}; + if (!fwrite(h,3,1,f)) + { + fclose(f); + return eXMLErrorCannotWriteFile; + } + encoding="utf-8"; + } else if (characterEncoding==char_encoding_ShiftJIS) encoding="SHIFT-JIS"; + + if (!encoding) encoding="ISO-8859-1"; + if (fprintf(f,"\n",encoding)<0) + { + fclose(f); + return eXMLErrorCannotWriteFile; + } + } else + { + if (characterEncoding==char_encoding_UTF8) + { + unsigned char h[3]={0xEF,0xBB,0xBF}; + if (!fwrite(h,3,1,f)) + { + fclose(f); + return eXMLErrorCannotWriteFile; + } + } + } +#endif + int i; + XMLSTR t=createXMLString(nFormat,&i); + if (!fwrite(t,sizeof(XMLCHAR)*i,1,f)) + { + free(t); + fclose(f); + return eXMLErrorCannotWriteFile; + } + if (fclose(f)!=0) + { + free(t); + return eXMLErrorCannotWriteFile; + } + free(t); + return eXMLErrorNone; +} + +// Duplicate a given string. +XMLSTR stringDup(XMLCSTR lpszData, int cbData) +{ + if (lpszData==NULL) return NULL; + + XMLSTR lpszNew; + if (cbData==-1) cbData=(int)xstrlen(lpszData); + lpszNew = (XMLSTR)malloc((cbData+1) * sizeof(XMLCHAR)); + if (lpszNew) + { + memcpy(lpszNew, lpszData, (cbData) * sizeof(XMLCHAR)); + lpszNew[cbData] = (XMLCHAR)NULL; + } + return lpszNew; +} + +XMLSTR ToXMLStringTool::toXMLUnSafe(XMLSTR dest,XMLCSTR source) +{ + XMLSTR dd=dest; + XMLCHAR ch; + XMLCharacterEntity *entity; + while ((ch=*source)) + { + entity=XMLEntities; + do + { + if (ch==entity->c) {xstrcpy(dest,entity->s); dest+=entity->l; source++; goto out_of_loop1; } + entity++; + } while(entity->s); +#ifdef _XMLWIDECHAR + *(dest++)=*(source++); +#else + switch(XML_ByteTable[(unsigned char)ch]) + { + case 4: + if ((!(source[1]))||(!(source[2]))||(!(source[3]))) { *(dest++)='_'; source++; } + else + { + *dest=*source; + dest[1]=source[1]; + dest[2]=source[2]; + dest[3]=source[3]; + dest+=4; source+=4; + } + break; + case 3: + if ((!(source[1]))||(!(source[2]))) { *(dest++)='_'; source++; } + else + { + *dest=*source; + dest[1]=source[1]; + dest[2]=source[2]; + dest+=3; source+=3; + } + break; + case 2: + if (!(source[1])) { *(dest++)='_'; source++; } + else + { + *dest=*source; + dest[1]=source[1]; + dest+=2; source+=2; + } + break; + case 1: *(dest++)=*(source++); + } +#endif +out_of_loop1: + ; + } + *dest=0; + return dd; +} + +// private (used while rendering): +int ToXMLStringTool::lengthXMLString(XMLCSTR source) +{ + int r=0; + XMLCharacterEntity *entity; + XMLCHAR ch; + while ((ch=*source)) + { + entity=XMLEntities; + do + { + if (ch==entity->c) { r+=entity->l; source++; goto out_of_loop1; } + entity++; + } while(entity->s); +#ifdef _XMLWIDECHAR + r++; source++; +#else + ch=XML_ByteTable[(unsigned char)ch]; r+=ch; source+=ch; +#endif +out_of_loop1: + ; + } + return r; +} + +ToXMLStringTool::~ToXMLStringTool(){ freeBuffer(); } +void ToXMLStringTool::freeBuffer(){ if (buf) free(buf); buf=NULL; buflen=0; } +XMLSTR ToXMLStringTool::toXML(XMLCSTR source) +{ + if (!source) + { + if (buflen<1) { buflen=1; buf=(XMLSTR)malloc(sizeof(XMLCHAR)); } + *buf=0; + return buf; + } + int l=lengthXMLString(source)+1; + if (l>buflen) { freeBuffer(); buflen=l; buf=(XMLSTR)malloc(l*sizeof(XMLCHAR)); } + return toXMLUnSafe(buf,source); +} + +// private: +XMLSTR fromXMLString(XMLCSTR s, int lo, XML *pXML) +{ + // This function is the opposite of the function "toXMLString". It decodes the escape + // sequences &, ", ', <, > and replace them by the characters + // &,",',<,>. This function is used internally by the XML Parser. All the calls to + // the XML library will always gives you back "decoded" strings. + // + // in: string (s) and length (lo) of string + // out: new allocated string converted from xml + if (!s) return NULL; + + int ll=0,j; + XMLSTR d; + XMLCSTR ss=s; + XMLCharacterEntity *entity; + while ((lo>0)&&(*s)) + { + if (*s==_CXML('&')) + { + if ((lo>2)&&(s[1]==_CXML('#'))) + { + s+=2; lo-=2; + if ((*s==_CXML('X'))||(*s==_CXML('x'))) { s++; lo--; } + while ((*s)&&(*s!=_CXML(';'))&&((lo--)>0)) s++; + if (*s!=_CXML(';')) + { + pXML->error=eXMLErrorUnknownCharacterEntity; + return NULL; + } + s++; lo--; + } else + { + entity=XMLEntities; + do + { + if ((lo>=entity->l)&&(xstrnicmp(s,entity->s,entity->l)==0)) { s+=entity->l; lo-=entity->l; break; } + entity++; + } while(entity->s); + if (!entity->s) + { + pXML->error=eXMLErrorUnknownCharacterEntity; + return NULL; + } + } + } else + { +#ifdef _XMLWIDECHAR + s++; lo--; +#else + j=XML_ByteTable[(unsigned char)*s]; s+=j; lo-=j; ll+=j-1; +#endif + } + ll++; + } + + d=(XMLSTR)malloc((ll+1)*sizeof(XMLCHAR)); + s=d; + while (ll-->0) + { + if (*ss==_CXML('&')) + { + if (ss[1]==_CXML('#')) + { + ss+=2; j=0; + if ((*ss==_CXML('X'))||(*ss==_CXML('x'))) + { + ss++; + while (*ss!=_CXML(';')) + { + if ((*ss>=_CXML('0'))&&(*ss<=_CXML('9'))) j=(j<<4)+*ss-_CXML('0'); + else if ((*ss>=_CXML('A'))&&(*ss<=_CXML('F'))) j=(j<<4)+*ss-_CXML('A')+10; + else if ((*ss>=_CXML('a'))&&(*ss<=_CXML('f'))) j=(j<<4)+*ss-_CXML('a')+10; + else { free((void*)s); pXML->error=eXMLErrorUnknownCharacterEntity;return NULL;} + ss++; + } + } else + { + while (*ss!=_CXML(';')) + { + if ((*ss>=_CXML('0'))&&(*ss<=_CXML('9'))) j=(j*10)+*ss-_CXML('0'); + else { free((void*)s); pXML->error=eXMLErrorUnknownCharacterEntity;return NULL;} + ss++; + } + } +#ifndef _XMLWIDECHAR + if (j>255) { free((void*)s); pXML->error=eXMLErrorCharacterCodeAbove255;return NULL;} +#endif + (*d++)=(XMLCHAR)j; ss++; + } else + { + entity=XMLEntities; + do + { + if (xstrnicmp(ss,entity->s,entity->l)==0) { *(d++)=entity->c; ss+=entity->l; break; } + entity++; + } while(entity->s); + } + } else + { +#ifdef _XMLWIDECHAR + *(d++)=*(ss++); +#else + switch(XML_ByteTable[(unsigned char)*ss]) + { + case 4: *(d++)=*(ss++); ll--; + case 3: *(d++)=*(ss++); ll--; + case 2: *(d++)=*(ss++); ll--; + case 1: *(d++)=*(ss++); + } +#endif + } + } + *d=0; + return (XMLSTR)s; +} + +#define XML_isSPACECHAR(ch) ((ch==_CXML('\n'))||(ch==_CXML(' '))||(ch== _CXML('\t'))||(ch==_CXML('\r'))) + +// private: +char myTagCompare(XMLCSTR cclose, XMLCSTR copen) +// !!!! WARNING strange convention&: +// return 0 if equals +// return 1 if different +{ + if (!cclose) return 1; + int l=(int)xstrlen(cclose); + if (xstrnicmp(cclose, copen, l)!=0) return 1; + const XMLCHAR c=copen[l]; + if (XML_isSPACECHAR(c)|| + (c==_CXML('/' ))|| + (c==_CXML('<' ))|| + (c==_CXML('>' ))|| + (c==_CXML('=' ))) return 0; + return 1; +} + +// Obtain the next character from the string. +static inline XMLCHAR getNextChar(XML *pXML) +{ + XMLCHAR ch = pXML->lpXML[pXML->nIndex]; +#ifdef _XMLWIDECHAR + if (ch!=0) pXML->nIndex++; +#else + pXML->nIndex+=XML_ByteTable[(unsigned char)ch]; +#endif + return ch; +} + +// Find the next token in a string. +// pcbToken contains the number of characters that have been read. +static NextToken GetNextToken(XML *pXML, int *pcbToken, enum XMLTokenTypeTag *pType) +{ + NextToken result; + XMLCHAR ch; + XMLCHAR chTemp; + int indexStart,nFoundMatch,nIsText=FALSE; + result.pClr=NULL; // prevent warning + + // Find next non-white space character + do { indexStart=pXML->nIndex; ch=getNextChar(pXML); } while XML_isSPACECHAR(ch); + + if (ch) + { + // Cache the current string pointer + result.pStr = &pXML->lpXML[indexStart]; + + // check for standard tokens + switch(ch) + { + // Check for quotes + case _CXML('\''): + case _CXML('\"'): + // Type of token + *pType = eTokenQuotedText; + chTemp = ch; + + // Set the size + nFoundMatch = FALSE; + + // Search through the string to find a matching quote + while((ch = getNextChar(pXML))) + { + if (ch==chTemp) { nFoundMatch = TRUE; break; } + if (ch==_CXML('<')) break; + } + + // If we failed to find a matching quote + if (nFoundMatch == FALSE) + { + pXML->nIndex=indexStart+1; + nIsText=TRUE; + break; + } + +// 4.02.2002 +// if (FindNonWhiteSpace(pXML)) pXML->nIndex--; + + break; + + // Equals (used with attribute values) + case _CXML('='): + *pType = eTokenEquals; + break; + + // Close tag + case _CXML('>'): + *pType = eTokenCloseTag; + break; + + // Check for tag start and tag end + case _CXML('<'): + + { + // First check whether the token is in the clear tag list (meaning it + // does not need formatting). + ALLXMLClearTag *ctag=XMLClearTags; + do + { + if (!xstrncmp(ctag->lpszOpen, result.pStr, ctag->openTagLen)) + { + result.pClr=ctag; + pXML->nIndex+=ctag->openTagLen-1; + *pType=eTokenClear; + return result; + } + ctag++; + } while(ctag->lpszOpen); + + // Peek at the next character to see if we have an end tag 'lpXML[pXML->nIndex]; + + // If we have a tag end... + if (chTemp == _CXML('/')) + { + // Set the type and ensure we point at the next character + getNextChar(pXML); + *pType = eTokenTagEnd; + } + + // If we have an XML declaration tag + else if (chTemp == _CXML('?')) + { + + // Set the type and ensure we point at the next character + getNextChar(pXML); + *pType = eTokenDeclaration; + } + + // Otherwise we must have a start tag + else + { + *pType = eTokenTagStart; + } + break; + } + + // Check to see if we have a short hand type end tag ('/>'). + case _CXML('/'): + + // Peek at the next character to see if we have a short end tag '/>' + chTemp = pXML->lpXML[pXML->nIndex]; + + // If we have a short hand end tag... + if (chTemp == _CXML('>')) + { + // Set the type and ensure we point at the next character + getNextChar(pXML); + *pType = eTokenShortHandClose; + break; + } + + // If we haven't found a short hand closing tag then drop into the + // text process + + // Other characters + default: + nIsText = TRUE; + } + + // If this is a TEXT node + if (nIsText) + { + // Indicate we are dealing with text + *pType = eTokenText; + while((ch = getNextChar(pXML))) + { + if XML_isSPACECHAR(ch) + { + indexStart++; break; + + } else if (ch==_CXML('/')) + { + // If we find a slash then this maybe text or a short hand end tag + // Peek at the next character to see it we have short hand end tag + ch=pXML->lpXML[pXML->nIndex]; + // If we found a short hand end tag then we need to exit the loop + if (ch==_CXML('>')) { pXML->nIndex--; break; } + + } else if ((ch==_CXML('<'))||(ch==_CXML('>'))||(ch==_CXML('='))) + { + pXML->nIndex--; break; + } + } + } + *pcbToken = pXML->nIndex-indexStart; + } else + { + // If we failed to obtain a valid character + *pcbToken = 0; + *pType = eTokenError; + result.pStr=NULL; + } + + return result; +} + +XMLCSTR XMLNode::updateName_WOSD(XMLSTR lpszName) +{ + if (!d) { free(lpszName); return NULL; } + if (d->lpszName&&(lpszName!=d->lpszName)) free((void*)d->lpszName); + d->lpszName=lpszName; + return lpszName; +} + +// private: +XMLNode::XMLNode(struct XMLNodeDataTag *p){ d=p; (p->ref_count)++; } +XMLNode::XMLNode(XMLNodeData *pParent, XMLSTR lpszName, char isDeclaration) +{ + d=(XMLNodeData*)malloc(sizeof(XMLNodeData)); + d->ref_count=1; + + d->lpszName=NULL; + d->nChild= 0; + d->nText = 0; + d->nClear = 0; + d->nAttribute = 0; + + d->isDeclaration = isDeclaration; + + d->pParent = pParent; + d->pChild= NULL; + d->pText= NULL; + d->pClear= NULL; + d->pAttribute= NULL; + d->pOrder= NULL; + + updateName_WOSD(lpszName); +} + +XMLNode XMLNode::createXMLTopNode_WOSD(XMLSTR lpszName, char isDeclaration) { return XMLNode(NULL,lpszName,isDeclaration); } +XMLNode XMLNode::createXMLTopNode(XMLCSTR lpszName, char isDeclaration) { return XMLNode(NULL,stringDup(lpszName),isDeclaration); } + +#define MEMORYINCREASE 50 + +static inline void myFree(void *p) { if (p) free(p); } +static inline void *myRealloc(void *p, int newsize, int memInc, int sizeofElem) +{ + if (p==NULL) { if (memInc) return malloc(memInc*sizeofElem); return malloc(sizeofElem); } + if ((memInc==0)||((newsize%memInc)==0)) p=realloc(p,(newsize+memInc)*sizeofElem); +// if (!p) +// { +// printf("XMLParser Error: Not enough memory! Aborting...\n"); exit(220); +// } + return p; +} + +// private: +XMLElementPosition XMLNode::findPosition(XMLNodeData *d, int index, XMLElementType xxtype) +{ + if (index<0) return -1; + int i=0,j=(int)((index<<2)+xxtype),*o=d->pOrder; while (o[i]!=j) i++; return i; +} + +// private: +// update "order" information when deleting a content of a XMLNode +int XMLNode::removeOrderElement(XMLNodeData *d, XMLElementType t, int index) +{ + int n=d->nChild+d->nText+d->nClear, *o=d->pOrder,i=findPosition(d,index,t); + memmove(o+i, o+i+1, (n-i)*sizeof(int)); + for (;ipOrder=(int)realloc(d->pOrder,n*sizeof(int)); + // but we skip reallocation because it's too time consuming. + // Anyway, at the end, it will be free'd completely at once. + return i; +} + +void *XMLNode::addToOrder(int memoryIncrease,int *_pos, int nc, void *p, int size, XMLElementType xtype) +{ + // in: *_pos is the position inside d->pOrder ("-1" means "EndOf") + // out: *_pos is the index inside p + p=myRealloc(p,(nc+1),memoryIncrease,size); + int n=d->nChild+d->nText+d->nClear; + d->pOrder=(int*)myRealloc(d->pOrder,n+1,memoryIncrease*3,sizeof(int)); + int pos=*_pos,*o=d->pOrder; + + if ((pos<0)||(pos>=n)) { *_pos=nc; o[n]=(int)((nc<<2)+xtype); return p; } + + int i=pos; + memmove(o+i+1, o+i, (n-i)*sizeof(int)); + + while ((pos>2; + memmove(((char*)p)+(pos+1)*size,((char*)p)+pos*size,(nc-pos)*size); + + return p; +} + +// Add a child node to the given element. +XMLNode XMLNode::addChild_priv(int memoryIncrease, XMLSTR lpszName, char isDeclaration, int pos) +{ + if (!lpszName) return emptyXMLNode; + d->pChild=(XMLNode*)addToOrder(memoryIncrease,&pos,d->nChild,d->pChild,sizeof(XMLNode),eNodeChild); + d->pChild[pos].d=NULL; + d->pChild[pos]=XMLNode(d,lpszName,isDeclaration); + d->nChild++; + return d->pChild[pos]; +} + +// Add an attribute to an element. +XMLAttribute *XMLNode::addAttribute_priv(int memoryIncrease,XMLSTR lpszName, XMLSTR lpszValuev) +{ + if (!lpszName) return &emptyXMLAttribute; + if (!d) { myFree(lpszName); myFree(lpszValuev); return &emptyXMLAttribute; } + int nc=d->nAttribute; + d->pAttribute=(XMLAttribute*)myRealloc(d->pAttribute,(nc+1),memoryIncrease,sizeof(XMLAttribute)); + XMLAttribute *pAttr=d->pAttribute+nc; + pAttr->lpszName = lpszName; + pAttr->lpszValue = lpszValuev; + d->nAttribute++; + return pAttr; +} + +// Add text to the element. +XMLCSTR XMLNode::addText_priv(int memoryIncrease, XMLSTR lpszValue, int pos) +{ + if (!lpszValue) return NULL; + if (!d) { myFree(lpszValue); return NULL; } + d->pText=(XMLCSTR*)addToOrder(memoryIncrease,&pos,d->nText,d->pText,sizeof(XMLSTR),eNodeText); + d->pText[pos]=lpszValue; + d->nText++; + return lpszValue; +} + +// Add clear (unformatted) text to the element. +XMLClear *XMLNode::addClear_priv(int memoryIncrease, XMLSTR lpszValue, XMLCSTR lpszOpen, XMLCSTR lpszClose, int pos) +{ + if (!lpszValue) return &emptyXMLClear; + if (!d) { myFree(lpszValue); return &emptyXMLClear; } + d->pClear=(XMLClear *)addToOrder(memoryIncrease,&pos,d->nClear,d->pClear,sizeof(XMLClear),eNodeClear); + XMLClear *pNewClear=d->pClear+pos; + pNewClear->lpszValue = lpszValue; + if (!lpszOpen) lpszOpen=XMLClearTags->lpszOpen; + if (!lpszClose) lpszClose=XMLClearTags->lpszClose; + pNewClear->lpszOpenTag = lpszOpen; + pNewClear->lpszCloseTag = lpszClose; + d->nClear++; + return pNewClear; +} + +// private: +// Parse a clear (unformatted) type node. +char XMLNode::parseClearTag(void *px, void *_pClear) +{ + XML *pXML=(XML *)px; + ALLXMLClearTag pClear=*((ALLXMLClearTag*)_pClear); + int cbTemp=0; + XMLCSTR lpszTemp=NULL; + XMLCSTR lpXML=&pXML->lpXML[pXML->nIndex]; + static XMLCSTR docTypeEnd=_CXML("]>"); + + // Find the closing tag + // Seems the ')) { lpszTemp=pCh; break; } +#ifdef _XMLWIDECHAR + pCh++; +#else + pCh+=XML_ByteTable[(unsigned char)(*pCh)]; +#endif + } + } else lpszTemp=xstrstr(lpXML, pClear.lpszClose); + + if (lpszTemp) + { + // Cache the size and increment the index + cbTemp = (int)(lpszTemp - lpXML); + + pXML->nIndex += cbTemp+(int)xstrlen(pClear.lpszClose); + + // Add the clear node to the current element + addClear_priv(MEMORYINCREASE,cbTemp?stringDup(lpXML,cbTemp):NULL, pClear.lpszOpen, pClear.lpszClose,-1); + return 0; + } + + // If we failed to find the end tag + pXML->error = eXMLErrorUnmatchedEndClearTag; + return 1; +} + +void XMLNode::exactMemory(XMLNodeData *d) +{ + if (d->pOrder) d->pOrder=(int*)realloc(d->pOrder,(d->nChild+d->nText+d->nClear)*sizeof(int)); + if (d->pChild) d->pChild=(XMLNode*)realloc(d->pChild,d->nChild*sizeof(XMLNode)); + if (d->pAttribute) d->pAttribute=(XMLAttribute*)realloc(d->pAttribute,d->nAttribute*sizeof(XMLAttribute)); + if (d->pText) d->pText=(XMLCSTR*)realloc(d->pText,d->nText*sizeof(XMLSTR)); + if (d->pClear) d->pClear=(XMLClear *)realloc(d->pClear,d->nClear*sizeof(XMLClear)); +} + +char XMLNode::maybeAddTxT(void *pa, XMLCSTR tokenPStr) +{ + XML *pXML=(XML *)pa; + XMLCSTR lpszText=pXML->lpszText; + if (!lpszText) return 0; + if (dropWhiteSpace) while (XML_isSPACECHAR(*lpszText)&&(lpszText!=tokenPStr)) lpszText++; + int cbText = (int)(tokenPStr - lpszText); + if (!cbText) { pXML->lpszText=NULL; return 0; } + if (dropWhiteSpace) { cbText--; while ((cbText)&&XML_isSPACECHAR(lpszText[cbText])) cbText--; cbText++; } + if (!cbText) { pXML->lpszText=NULL; return 0; } + XMLSTR lpt=fromXMLString(lpszText,cbText,pXML); + if (!lpt) return 1; + pXML->lpszText=NULL; + if (removeCommentsInMiddleOfText && d->nText && d->nClear) + { + // if the previous insertion was a comment () AND + // if the previous previous insertion was a text then, delete the comment and append the text + int n=d->nChild+d->nText+d->nClear-1,*o=d->pOrder; + if (((o[n]&3)==eNodeClear)&&((o[n-1]&3)==eNodeText)) + { + int i=o[n]>>2; + if (d->pClear[i].lpszOpenTag==XMLClearTags[2].lpszOpen) + { + deleteClear(i); + i=o[n-1]>>2; + n=xstrlen(d->pText[i]); + int n2=xstrlen(lpt)+1; + d->pText[i]=(XMLSTR)realloc((void*)d->pText[i],(n+n2)*sizeof(XMLCHAR)); + if (!d->pText[i]) return 1; + memcpy((void*)(d->pText[i]+n),lpt,n2*sizeof(XMLCHAR)); + free(lpt); + return 0; + } + } + } + addText_priv(MEMORYINCREASE,lpt,-1); + return 0; +} +// private: +// Recursively parse an XML element. +int XMLNode::ParseXMLElement(void *pa) +{ + XML *pXML=(XML *)pa; + int cbToken; + enum XMLTokenTypeTag xtype; + NextToken token; + XMLCSTR lpszTemp=NULL; + int cbTemp=0; + char nDeclaration; + XMLNode pNew; + enum XMLStatus status; // inside or outside a tag + enum Attrib attrib = eAttribName; + + assert(pXML); + + // If this is the first call to the function + if (pXML->nFirst) + { + // Assume we are outside of a tag definition + pXML->nFirst = FALSE; + status = eOutsideTag; + } else + { + // If this is not the first call then we should only be called when inside a tag. + status = eInsideTag; + } + + // Iterate through the tokens in the document + for(;;) + { + // Obtain the next token + token = GetNextToken(pXML, &cbToken, &xtype); + + if (xtype != eTokenError) + { + // Check the current status + switch(status) + { + + // If we are outside of a tag definition + case eOutsideTag: + + // Check what type of token we obtained + switch(xtype) + { + // If we have found text or quoted text + case eTokenText: + case eTokenCloseTag: /* '>' */ + case eTokenShortHandClose: /* '/>' */ + case eTokenQuotedText: + case eTokenEquals: + break; + + // If we found a start tag '<' and declarations 'error = eXMLErrorMissingTagName; + return FALSE; + } + + // If we found a new element which is the same as this + // element then we need to pass this back to the caller.. + +#ifdef APPROXIMATE_PARSING + if (d->lpszName && + myTagCompare(d->lpszName, token.pStr) == 0) + { + // Indicate to the caller that it needs to create a + // new element. + pXML->lpNewElement = token.pStr; + pXML->cbNewElement = cbToken; + return TRUE; + } else +#endif + { + // If the name of the new element differs from the name of + // the current element we need to add the new element to + // the current one and recurse + pNew = addChild_priv(MEMORYINCREASE,stringDup(token.pStr,cbToken), nDeclaration,-1); + + while (!pNew.isEmpty()) + { + // Callself to process the new node. If we return + // FALSE this means we dont have any more + // processing to do... + + if (!pNew.ParseXMLElement(pXML)) return FALSE; + else + { + // If the call to recurse this function + // evented in a end tag specified in XML then + // we need to unwind the calls to this + // function until we find the appropriate node + // (the element name and end tag name must + // match) + if (pXML->cbEndTag) + { + // If we are back at the root node then we + // have an unmatched end tag + if (!d->lpszName) + { + pXML->error=eXMLErrorUnmatchedEndTag; + return FALSE; + } + + // If the end tag matches the name of this + // element then we only need to unwind + // once more... + + if (myTagCompare(d->lpszName, pXML->lpEndTag)==0) + { + pXML->cbEndTag = 0; + } + + return TRUE; + } else + if (pXML->cbNewElement) + { + // If the call indicated a new element is to + // be created on THIS element. + + // If the name of this element matches the + // name of the element we need to create + // then we need to return to the caller + // and let it process the element. + + if (myTagCompare(d->lpszName, pXML->lpNewElement)==0) + { + return TRUE; + } + + // Add the new element and recurse + pNew = addChild_priv(MEMORYINCREASE,stringDup(pXML->lpNewElement,pXML->cbNewElement),0,-1); + pXML->cbNewElement = 0; + } + else + { + // If we didn't have a new element to create + pNew = emptyXMLNode; + + } + } + } + } + break; + + // If we found an end tag + case eTokenTagEnd: + + // If we have node text then add this to the element + if (maybeAddTxT(pXML,token.pStr)) return FALSE; + + // Find the name of the end tag + token = GetNextToken(pXML, &cbTemp, &xtype); + + // The end tag should be text + if (xtype != eTokenText) + { + pXML->error = eXMLErrorMissingEndTagName; + return FALSE; + } + lpszTemp = token.pStr; + + // After the end tag we should find a closing tag + token = GetNextToken(pXML, &cbToken, &xtype); + if (xtype != eTokenCloseTag) + { + pXML->error = eXMLErrorMissingEndTagName; + return FALSE; + } + pXML->lpszText=pXML->lpXML+pXML->nIndex; + + // We need to return to the previous caller. If the name + // of the tag cannot be found we need to keep returning to + // caller until we find a match + if (myTagCompare(d->lpszName, lpszTemp) != 0) +#ifdef STRICT_PARSING + { + pXML->error=eXMLErrorUnmatchedEndTag; + pXML->nIndexMissigEndTag=pXML->nIndex; + return FALSE; + } +#else + { + pXML->error=eXMLErrorMissingEndTag; + pXML->nIndexMissigEndTag=pXML->nIndex; + pXML->lpEndTag = lpszTemp; + pXML->cbEndTag = cbTemp; + } +#endif + + // Return to the caller + exactMemory(d); + return TRUE; + + // If we found a clear (unformatted) token + case eTokenClear: + // If we have node text then add this to the element + if (maybeAddTxT(pXML,token.pStr)) return FALSE; + if (parseClearTag(pXML, token.pClr)) return FALSE; + pXML->lpszText=pXML->lpXML+pXML->nIndex; + break; + + default: + break; + } + break; + + // If we are inside a tag definition we need to search for attributes + case eInsideTag: + + // Check what part of the attribute (name, equals, value) we + // are looking for. + switch(attrib) + { + // If we are looking for a new attribute + case eAttribName: + + // Check what the current token type is + switch(xtype) + { + // If the current type is text... + // Eg. 'attribute' + case eTokenText: + // Cache the token then indicate that we are next to + // look for the equals + lpszTemp = token.pStr; + cbTemp = cbToken; + attrib = eAttribEquals; + break; + + // If we found a closing tag... + // Eg. '>' + case eTokenCloseTag: + // We are now outside the tag + status = eOutsideTag; + pXML->lpszText=pXML->lpXML+pXML->nIndex; + break; + + // If we found a short hand '/>' closing tag then we can + // return to the caller + case eTokenShortHandClose: + exactMemory(d); + pXML->lpszText=pXML->lpXML+pXML->nIndex; + return TRUE; + + // Errors... + case eTokenQuotedText: /* '"SomeText"' */ + case eTokenTagStart: /* '<' */ + case eTokenTagEnd: /* 'error = eXMLErrorUnexpectedToken; + return FALSE; + default: break; + } + break; + + // If we are looking for an equals + case eAttribEquals: + // Check what the current token type is + switch(xtype) + { + // If the current type is text... + // Eg. 'Attribute AnotherAttribute' + case eTokenText: + // Add the unvalued attribute to the list + addAttribute_priv(MEMORYINCREASE,stringDup(lpszTemp,cbTemp), NULL); + // Cache the token then indicate. We are next to + // look for the equals attribute + lpszTemp = token.pStr; + cbTemp = cbToken; + break; + + // If we found a closing tag 'Attribute >' or a short hand + // closing tag 'Attribute />' + case eTokenShortHandClose: + case eTokenCloseTag: + // If we are a declaration element 'lpszText=pXML->lpXML+pXML->nIndex; + + if (d->isDeclaration && + (lpszTemp[cbTemp-1]) == _CXML('?')) + { + cbTemp--; + if (d->pParent && d->pParent->pParent) xtype = eTokenShortHandClose; + } + + if (cbTemp) + { + // Add the unvalued attribute to the list + addAttribute_priv(MEMORYINCREASE,stringDup(lpszTemp,cbTemp), NULL); + } + + // If this is the end of the tag then return to the caller + if (xtype == eTokenShortHandClose) + { + exactMemory(d); + return TRUE; + } + + // We are now outside the tag + status = eOutsideTag; + break; + + // If we found the equals token... + // Eg. 'Attribute =' + case eTokenEquals: + // Indicate that we next need to search for the value + // for the attribute + attrib = eAttribValue; + break; + + // Errors... + case eTokenQuotedText: /* 'Attribute "InvalidAttr"'*/ + case eTokenTagStart: /* 'Attribute <' */ + case eTokenTagEnd: /* 'Attribute error = eXMLErrorUnexpectedToken; + return FALSE; + default: break; + } + break; + + // If we are looking for an attribute value + case eAttribValue: + // Check what the current token type is + switch(xtype) + { + // If the current type is text or quoted text... + // Eg. 'Attribute = "Value"' or 'Attribute = Value' or + // 'Attribute = 'Value''. + case eTokenText: + case eTokenQuotedText: + // If we are a declaration element 'isDeclaration && + (token.pStr[cbToken-1]) == _CXML('?')) + { + cbToken--; + } + + if (cbTemp) + { + // Add the valued attribute to the list + if (xtype==eTokenQuotedText) { token.pStr++; cbToken-=2; } + XMLSTR attrVal=(XMLSTR)token.pStr; + if (attrVal) + { + attrVal=fromXMLString(attrVal,cbToken,pXML); + if (!attrVal) return FALSE; + } + addAttribute_priv(MEMORYINCREASE,stringDup(lpszTemp,cbTemp),attrVal); + } + + // Indicate we are searching for a new attribute + attrib = eAttribName; + break; + + // Errors... + case eTokenTagStart: /* 'Attr = <' */ + case eTokenTagEnd: /* 'Attr = ' */ + case eTokenShortHandClose: /* "Attr = />" */ + case eTokenEquals: /* 'Attr = =' */ + case eTokenDeclaration: /* 'Attr = error = eXMLErrorUnexpectedToken; + return FALSE; + break; + default: break; + } + } + } + } + // If we failed to obtain the next token + else + { + if ((!d->isDeclaration)&&(d->pParent)) + { +#ifdef STRICT_PARSING + pXML->error=eXMLErrorUnmatchedEndTag; +#else + pXML->error=eXMLErrorMissingEndTag; +#endif + pXML->nIndexMissigEndTag=pXML->nIndex; + } + maybeAddTxT(pXML,pXML->lpXML+pXML->nIndex); + return FALSE; + } + } +} + +// Count the number of lines and columns in an XML string. +static void CountLinesAndColumns(XMLCSTR lpXML, int nUpto, XMLResults *pResults) +{ + XMLCHAR ch; + assert(lpXML); + assert(pResults); + + struct XML xml={ lpXML,lpXML, 0, 0, eXMLErrorNone, NULL, 0, NULL, 0, TRUE }; + + pResults->nLine = 1; + pResults->nColumn = 1; + while (xml.nIndexnColumn++; + else + { + pResults->nLine++; + pResults->nColumn=1; + } + } +} + +// Parse XML and return the root element. +XMLNode XMLNode::parseString(XMLCSTR lpszXML, XMLCSTR tag, XMLResults *pResults) +{ + if (!lpszXML) + { + if (pResults) + { + pResults->error=eXMLErrorNoElements; + pResults->nLine=0; + pResults->nColumn=0; + } + return emptyXMLNode; + } + + XMLNode xnode(NULL,NULL,FALSE); + struct XML xml={ lpszXML, lpszXML, 0, 0, eXMLErrorNone, NULL, 0, NULL, 0, TRUE }; + + // Create header element + xnode.ParseXMLElement(&xml); + enum XMLError error = xml.error; + if (!xnode.nChildNode()) error=eXMLErrorNoXMLTagFound; + if ((xnode.nChildNode()==1)&&(xnode.nElement()==1)) xnode=xnode.getChildNode(); // skip the empty node + + // If no error occurred + if ((error==eXMLErrorNone)||(error==eXMLErrorMissingEndTag)||(error==eXMLErrorNoXMLTagFound)) + { + XMLCSTR name=xnode.getName(); + if (tag&&(*tag)&&((!name)||(xstricmp(name,tag)))) + { + xnode=xnode.getChildNode(tag); + if (xnode.isEmpty()) + { + if (pResults) + { + pResults->error=eXMLErrorFirstTagNotFound; + pResults->nLine=0; + pResults->nColumn=0; + } + return emptyXMLNode; + } + } + } else + { + // Cleanup: this will destroy all the nodes + xnode = emptyXMLNode; + } + + + // If we have been given somewhere to place results + if (pResults) + { + pResults->error = error; + + // If we have an error + if (error!=eXMLErrorNone) + { + if (error==eXMLErrorMissingEndTag) xml.nIndex=xml.nIndexMissigEndTag; + // Find which line and column it starts on. + CountLinesAndColumns(xml.lpXML, xml.nIndex, pResults); + } + } + return xnode; +} + +XMLNode XMLNode::parseFile(XMLCSTR filename, XMLCSTR tag, XMLResults *pResults) +{ + if (pResults) { pResults->nLine=0; pResults->nColumn=0; } + FILE *f=xfopen(filename,_CXML("rb")); + if (f==NULL) { if (pResults) pResults->error=eXMLErrorFileNotFound; return emptyXMLNode; } + fseek(f,0,SEEK_END); + int l=(int)ftell(f),headerSz=0; + if (!l) { if (pResults) pResults->error=eXMLErrorEmpty; fclose(f); return emptyXMLNode; } + fseek(f,0,SEEK_SET); + unsigned char *buf=(unsigned char*)malloc(l+4); + l=(int)fread(buf,1,l,f); + fclose(f); + buf[l]=0;buf[l+1]=0;buf[l+2]=0;buf[l+3]=0; +#ifdef _XMLWIDECHAR + if (guessWideCharChars) + { + if (!myIsTextWideChar(buf,l)) + { + XMLNode::XMLCharEncoding ce=XMLNode::char_encoding_legacy; + if ((buf[0]==0xef)&&(buf[1]==0xbb)&&(buf[2]==0xbf)) { headerSz=3; ce=XMLNode::char_encoding_UTF8; } + XMLSTR b2=myMultiByteToWideChar((const char*)(buf+headerSz),ce); + if (!b2) + { + // todo: unable to convert + } + free(buf); buf=(unsigned char*)b2; headerSz=0; + } else + { + if ((buf[0]==0xef)&&(buf[1]==0xff)) headerSz=2; + if ((buf[0]==0xff)&&(buf[1]==0xfe)) headerSz=2; + } + } else + { + if ((buf[0]==0xef)&&(buf[1]==0xff)) headerSz=2; + if ((buf[0]==0xff)&&(buf[1]==0xfe)) headerSz=2; + if ((buf[0]==0xef)&&(buf[1]==0xbb)&&(buf[2]==0xbf)) headerSz=3; + } +#else + if (guessWideCharChars) + { + if (myIsTextWideChar(buf,l)) + { + if ((buf[0]==0xef)&&(buf[1]==0xff)) headerSz=2; + if ((buf[0]==0xff)&&(buf[1]==0xfe)) headerSz=2; + char *b2=myWideCharToMultiByte((const wchar_t*)(buf+headerSz)); + free(buf); buf=(unsigned char*)b2; headerSz=0; + } else + { + if ((buf[0]==0xef)&&(buf[1]==0xbb)&&(buf[2]==0xbf)) headerSz=3; + } + } else + { + if ((buf[0]==0xef)&&(buf[1]==0xff)) headerSz=2; + if ((buf[0]==0xff)&&(buf[1]==0xfe)) headerSz=2; + if ((buf[0]==0xef)&&(buf[1]==0xbb)&&(buf[2]==0xbf)) headerSz=3; + } +#endif + + if (!buf) { if (pResults) pResults->error=eXMLErrorCharConversionError; return emptyXMLNode; } + XMLNode x=parseString((XMLSTR)(buf+headerSz),tag,pResults); + free(buf); + return x; +} + +static inline void charmemset(XMLSTR dest,XMLCHAR c,int l) { while (l--) *(dest++)=c; } +// private: +// Creates an user friendly XML string from a given element with +// appropriate white space and carriage returns. +// +// This recurses through all subnodes then adds contents of the nodes to the +// string. +int XMLNode::CreateXMLStringR(XMLNodeData *pEntry, XMLSTR lpszMarker, int nFormat) +{ + int nResult = 0; + int cb=nFormat<0?0:nFormat; + int cbElement; + int nChildFormat=-1; + int nElementI=pEntry->nChild+pEntry->nText+pEntry->nClear; + int i,j; + if ((nFormat>=0)&&(nElementI==1)&&(pEntry->nText==1)&&(!pEntry->isDeclaration)) nFormat=-2; + + assert(pEntry); + +#define LENSTR(lpsz) (lpsz ? xstrlen(lpsz) : 0) + + // If the element has no name then assume this is the head node. + cbElement = (int)LENSTR(pEntry->lpszName); + + if (cbElement) + { + // "isDeclaration) lpszMarker[nResult++]=_CXML('?'); + xstrcpy(&lpszMarker[nResult], pEntry->lpszName); + nResult+=cbElement; + lpszMarker[nResult++]=_CXML(' '); + + } else + { + nResult+=cbElement+2+cb; + if (pEntry->isDeclaration) nResult++; + } + + // Enumerate attributes and add them to the string + XMLAttribute *pAttr=pEntry->pAttribute; + for (i=0; inAttribute; i++) + { + // "Attrib + cb = (int)LENSTR(pAttr->lpszName); + if (cb) + { + if (lpszMarker) xstrcpy(&lpszMarker[nResult], pAttr->lpszName); + nResult += cb; + // "Attrib=Value " + if (pAttr->lpszValue) + { + cb=(int)ToXMLStringTool::lengthXMLString(pAttr->lpszValue); + if (lpszMarker) + { + lpszMarker[nResult]=_CXML('='); + lpszMarker[nResult+1]=_CXML('"'); + if (cb) ToXMLStringTool::toXMLUnSafe(&lpszMarker[nResult+2],pAttr->lpszValue); + lpszMarker[nResult+cb+2]=_CXML('"'); + } + nResult+=cb+3; + } + if (lpszMarker) lpszMarker[nResult] = _CXML(' '); + nResult++; + } + pAttr++; + } + + if (pEntry->isDeclaration) + { + if (lpszMarker) + { + lpszMarker[nResult-1]=_CXML('?'); + lpszMarker[nResult]=_CXML('>'); + } + nResult++; + if (nFormat!=-1) + { + if (lpszMarker) lpszMarker[nResult]=_CXML('\n'); + nResult++; + } + } else + // If there are child nodes we need to terminate the start tag + if (nElementI) + { + if (lpszMarker) lpszMarker[nResult-1]=_CXML('>'); + if (nFormat>=0) + { + if (lpszMarker) lpszMarker[nResult]=_CXML('\n'); + nResult++; + } + } else nResult--; + } + + // Calculate the child format for when we recurse. This is used to + // determine the number of spaces used for prefixes. + if (nFormat!=-1) + { + if (cbElement&&(!pEntry->isDeclaration)) nChildFormat=nFormat+1; + else nChildFormat=nFormat; + } + + // Enumerate through remaining children + for (i=0; ipOrder[i]; + switch((XMLElementType)(j&3)) + { + // Text nodes + case eNodeText: + { + // "Text" + XMLCSTR pChild=pEntry->pText[j>>2]; + cb = (int)ToXMLStringTool::lengthXMLString(pChild); + if (cb) + { + if (nFormat>=0) + { + if (lpszMarker) + { + charmemset(&lpszMarker[nResult],INDENTCHAR,nFormat+1); + ToXMLStringTool::toXMLUnSafe(&lpszMarker[nResult+nFormat+1],pChild); + lpszMarker[nResult+nFormat+1+cb]=_CXML('\n'); + } + nResult+=cb+nFormat+2; + } else + { + if (lpszMarker) ToXMLStringTool::toXMLUnSafe(&lpszMarker[nResult], pChild); + nResult += cb; + } + } + break; + } + + // Clear type nodes + case eNodeClear: + { + XMLClear *pChild=pEntry->pClear+(j>>2); + // "OpenTag" + cb = (int)LENSTR(pChild->lpszOpenTag); + if (cb) + { + if (nFormat!=-1) + { + if (lpszMarker) + { + charmemset(&lpszMarker[nResult], INDENTCHAR, nFormat+1); + xstrcpy(&lpszMarker[nResult+nFormat+1], pChild->lpszOpenTag); + } + nResult+=cb+nFormat+1; + } + else + { + if (lpszMarker)xstrcpy(&lpszMarker[nResult], pChild->lpszOpenTag); + nResult += cb; + } + } + + // "OpenTag Value" + cb = (int)LENSTR(pChild->lpszValue); + if (cb) + { + if (lpszMarker) xstrcpy(&lpszMarker[nResult], pChild->lpszValue); + nResult += cb; + } + + // "OpenTag Value CloseTag" + cb = (int)LENSTR(pChild->lpszCloseTag); + if (cb) + { + if (lpszMarker) xstrcpy(&lpszMarker[nResult], pChild->lpszCloseTag); + nResult += cb; + } + + if (nFormat!=-1) + { + if (lpszMarker) lpszMarker[nResult] = _CXML('\n'); + nResult++; + } + break; + } + + // Element nodes + case eNodeChild: + { + // Recursively add child nodes + nResult += CreateXMLStringR(pEntry->pChild[j>>2].d, lpszMarker ? lpszMarker + nResult : 0, nChildFormat); + break; + } + default: break; + } + } + + if ((cbElement)&&(!pEntry->isDeclaration)) + { + // If we have child entries we need to use long XML notation for + // closing the element - "blah blah blah" + if (nElementI) + { + // "\0" + if (lpszMarker) + { + if (nFormat >=0) + { + charmemset(&lpszMarker[nResult], INDENTCHAR,nFormat); + nResult+=nFormat; + } + + lpszMarker[nResult]=_CXML('<'); lpszMarker[nResult+1]=_CXML('/'); + nResult += 2; + xstrcpy(&lpszMarker[nResult], pEntry->lpszName); + nResult += cbElement; + + lpszMarker[nResult]=_CXML('>'); + if (nFormat == -1) nResult++; + else + { + lpszMarker[nResult+1]=_CXML('\n'); + nResult+=2; + } + } else + { + if (nFormat>=0) nResult+=cbElement+4+nFormat; + else if (nFormat==-1) nResult+=cbElement+3; + else nResult+=cbElement+4; + } + } else + { + // If there are no children we can use shorthand XML notation - + // "" + // "/>\0" + if (lpszMarker) + { + lpszMarker[nResult]=_CXML('/'); lpszMarker[nResult+1]=_CXML('>'); + if (nFormat != -1) lpszMarker[nResult+2]=_CXML('\n'); + } + nResult += nFormat == -1 ? 2 : 3; + } + } + + return nResult; +} + +#undef LENSTR + +// Create an XML string +// @param int nFormat - 0 if no formatting is required +// otherwise nonzero for formatted text +// with carriage returns and indentation. +// @param int *pnSize - [out] pointer to the size of the +// returned string not including the +// NULL terminator. +// @return XMLSTR - Allocated XML string, you must free +// this with free(). +XMLSTR XMLNode::createXMLString(int nFormat, int *pnSize) const +{ + if (!d) { if (pnSize) *pnSize=0; return NULL; } + + XMLSTR lpszResult = NULL; + int cbStr; + + // Recursively Calculate the size of the XML string + if (!dropWhiteSpace) nFormat=0; + nFormat = nFormat ? 0 : -1; + cbStr = CreateXMLStringR(d, 0, nFormat); + // Alllocate memory for the XML string + the NULL terminator and + // create the recursively XML string. + lpszResult=(XMLSTR)malloc((cbStr+1)*sizeof(XMLCHAR)); + CreateXMLStringR(d, lpszResult, nFormat); + lpszResult[cbStr]=_CXML('\0'); + if (pnSize) *pnSize = cbStr; + return lpszResult; +} + +int XMLNode::detachFromParent(XMLNodeData *d) +{ + XMLNode *pa=d->pParent->pChild; + int i=0; + while (((void*)(pa[i].d))!=((void*)d)) i++; + d->pParent->nChild--; + if (d->pParent->nChild) memmove(pa+i,pa+i+1,(d->pParent->nChild-i)*sizeof(XMLNode)); + else { free(pa); d->pParent->pChild=NULL; } + return removeOrderElement(d->pParent,eNodeChild,i); +} + +XMLNode::~XMLNode() +{ + if (!d) return; + d->ref_count--; + emptyTheNode(0); +} +void XMLNode::deleteNodeContent() +{ + if (!d) return; + if (d->pParent) { detachFromParent(d); d->pParent=NULL; d->ref_count--; } + emptyTheNode(1); +} +void XMLNode::emptyTheNode(char force) +{ + XMLNodeData *dd=d; // warning: must stay this way! + if ((dd->ref_count==0)||force) + { + if (d->pParent) detachFromParent(d); + int i; + XMLNode *pc; + for(i=0; inChild; i++) + { + pc=dd->pChild+i; + pc->d->pParent=NULL; + pc->d->ref_count--; + pc->emptyTheNode(force); + } + myFree(dd->pChild); + for(i=0; inText; i++) free((void*)dd->pText[i]); + myFree(dd->pText); + for(i=0; inClear; i++) free((void*)dd->pClear[i].lpszValue); + myFree(dd->pClear); + for(i=0; inAttribute; i++) + { + free((void*)dd->pAttribute[i].lpszName); + if (dd->pAttribute[i].lpszValue) free((void*)dd->pAttribute[i].lpszValue); + } + myFree(dd->pAttribute); + myFree(dd->pOrder); + myFree((void*)dd->lpszName); + dd->nChild=0; dd->nText=0; dd->nClear=0; dd->nAttribute=0; + dd->pChild=NULL; dd->pText=NULL; dd->pClear=NULL; dd->pAttribute=NULL; + dd->pOrder=NULL; dd->lpszName=NULL; dd->pParent=NULL; + } + if (dd->ref_count==0) + { + free(dd); + d=NULL; + } +} + +XMLNode& XMLNode::operator=( const XMLNode& A ) +{ + // shallow copy + if (this != &A) + { + if (d) { d->ref_count--; emptyTheNode(0); } + d=A.d; + if (d) (d->ref_count) ++ ; + } + return *this; +} + +XMLNode::XMLNode(const XMLNode &A) +{ + // shallow copy + d=A.d; + if (d) (d->ref_count)++ ; +} + +XMLNode XMLNode::deepCopy() const +{ + if (!d) return XMLNode::emptyXMLNode; + XMLNode x(NULL,stringDup(d->lpszName),d->isDeclaration); + XMLNodeData *p=x.d; + int n=d->nAttribute; + if (n) + { + p->nAttribute=n; p->pAttribute=(XMLAttribute*)malloc(n*sizeof(XMLAttribute)); + while (n--) + { + p->pAttribute[n].lpszName=stringDup(d->pAttribute[n].lpszName); + p->pAttribute[n].lpszValue=stringDup(d->pAttribute[n].lpszValue); + } + } + if (d->pOrder) + { + n=(d->nChild+d->nText+d->nClear)*sizeof(int); p->pOrder=(int*)malloc(n); memcpy(p->pOrder,d->pOrder,n); + } + n=d->nText; + if (n) + { + p->nText=n; p->pText=(XMLCSTR*)malloc(n*sizeof(XMLCSTR)); + while(n--) p->pText[n]=stringDup(d->pText[n]); + } + n=d->nClear; + if (n) + { + p->nClear=n; p->pClear=(XMLClear*)malloc(n*sizeof(XMLClear)); + while (n--) + { + p->pClear[n].lpszCloseTag=d->pClear[n].lpszCloseTag; + p->pClear[n].lpszOpenTag=d->pClear[n].lpszOpenTag; + p->pClear[n].lpszValue=stringDup(d->pClear[n].lpszValue); + } + } + n=d->nChild; + if (n) + { + p->nChild=n; p->pChild=(XMLNode*)malloc(n*sizeof(XMLNode)); + while (n--) + { + p->pChild[n].d=NULL; + p->pChild[n]=d->pChild[n].deepCopy(); + p->pChild[n].d->pParent=p; + } + } + return x; +} + +XMLNode XMLNode::addChild(XMLNode childNode, int pos) +{ + XMLNodeData *dc=childNode.d; + if ((!dc)||(!d)) return childNode; + if (!dc->lpszName) + { + // this is a root node: todo: correct fix + int j=pos; + while (dc->nChild) + { + addChild(dc->pChild[0],j); + if (pos>=0) j++; + } + return childNode; + } + if (dc->pParent) { if ((detachFromParent(dc)<=pos)&&(dc->pParent==d)) pos--; } else dc->ref_count++; + dc->pParent=d; +// int nc=d->nChild; +// d->pChild=(XMLNode*)myRealloc(d->pChild,(nc+1),memoryIncrease,sizeof(XMLNode)); + d->pChild=(XMLNode*)addToOrder(0,&pos,d->nChild,d->pChild,sizeof(XMLNode),eNodeChild); + d->pChild[pos].d=dc; + d->nChild++; + return childNode; +} + +void XMLNode::deleteAttribute(int i) +{ + if ((!d)||(i<0)||(i>=d->nAttribute)) return; + d->nAttribute--; + XMLAttribute *p=d->pAttribute+i; + free((void*)p->lpszName); + if (p->lpszValue) free((void*)p->lpszValue); + if (d->nAttribute) memmove(p,p+1,(d->nAttribute-i)*sizeof(XMLAttribute)); else { free(p); d->pAttribute=NULL; } +} + +void XMLNode::deleteAttribute(XMLAttribute *a){ if (a) deleteAttribute(a->lpszName); } +void XMLNode::deleteAttribute(XMLCSTR lpszName) +{ + int j=0; + getAttribute(lpszName,&j); + if (j) deleteAttribute(j-1); +} + +XMLAttribute *XMLNode::updateAttribute_WOSD(XMLSTR lpszNewValue, XMLSTR lpszNewName,int i) +{ + if (!d) { if (lpszNewValue) free(lpszNewValue); if (lpszNewName) free(lpszNewName); return NULL; } + if (i>=d->nAttribute) + { + if (lpszNewName) return addAttribute_WOSD(lpszNewName,lpszNewValue); + return NULL; + } + XMLAttribute *p=d->pAttribute+i; + if (p->lpszValue&&p->lpszValue!=lpszNewValue) free((void*)p->lpszValue); + p->lpszValue=lpszNewValue; + if (lpszNewName&&p->lpszName!=lpszNewName) { free((void*)p->lpszName); p->lpszName=lpszNewName; }; + return p; +} + +XMLAttribute *XMLNode::updateAttribute_WOSD(XMLAttribute *newAttribute, XMLAttribute *oldAttribute) +{ + if (oldAttribute) return updateAttribute_WOSD((XMLSTR)newAttribute->lpszValue,(XMLSTR)newAttribute->lpszName,oldAttribute->lpszName); + return addAttribute_WOSD((XMLSTR)newAttribute->lpszName,(XMLSTR)newAttribute->lpszValue); +} + +XMLAttribute *XMLNode::updateAttribute_WOSD(XMLSTR lpszNewValue, XMLSTR lpszNewName,XMLCSTR lpszOldName) +{ + int j=0; + getAttribute(lpszOldName,&j); + if (j) return updateAttribute_WOSD(lpszNewValue,lpszNewName,j-1); + else + { + if (lpszNewName) return addAttribute_WOSD(lpszNewName,lpszNewValue); + else return addAttribute_WOSD(stringDup(lpszOldName),lpszNewValue); + } +} + +int XMLNode::indexText(XMLCSTR lpszValue) const +{ + if (!d) return -1; + int i,l=d->nText; + if (!lpszValue) { if (l) return 0; return -1; } + XMLCSTR *p=d->pText; + for (i=0; i=d->nText)) return; + d->nText--; + XMLCSTR *p=d->pText+i; + free((void*)*p); + if (d->nText) memmove(p,p+1,(d->nText-i)*sizeof(XMLCSTR)); else { free(p); d->pText=NULL; } + removeOrderElement(d,eNodeText,i); +} + +void XMLNode::deleteText(XMLCSTR lpszValue) { deleteText(indexText(lpszValue)); } + +XMLCSTR XMLNode::updateText_WOSD(XMLSTR lpszNewValue, int i) +{ + if (!d) { if (lpszNewValue) free(lpszNewValue); return NULL; } + if (i>=d->nText) return addText_WOSD(lpszNewValue); + XMLCSTR *p=d->pText+i; + if (*p!=lpszNewValue) { free((void*)*p); *p=lpszNewValue; } + return lpszNewValue; +} + +XMLCSTR XMLNode::updateText_WOSD(XMLSTR lpszNewValue, XMLCSTR lpszOldValue) +{ + if (!d) { if (lpszNewValue) free(lpszNewValue); return NULL; } + int i=indexText(lpszOldValue); + if (i>=0) return updateText_WOSD(lpszNewValue,i); + return addText_WOSD(lpszNewValue); +} + +void XMLNode::deleteClear(int i) +{ + if ((!d)||(i<0)||(i>=d->nClear)) return; + d->nClear--; + XMLClear *p=d->pClear+i; + free((void*)p->lpszValue); + if (d->nClear) memmove(p,p+1,(d->nClear-i)*sizeof(XMLClear)); else { free(p); d->pClear=NULL; } + removeOrderElement(d,eNodeClear,i); +} + +int XMLNode::indexClear(XMLCSTR lpszValue) const +{ + if (!d) return -1; + int i,l=d->nClear; + if (!lpszValue) { if (l) return 0; return -1; } + XMLClear *p=d->pClear; + for (i=0; ilpszValue); } + +XMLClear *XMLNode::updateClear_WOSD(XMLSTR lpszNewContent, int i) +{ + if (!d) { if (lpszNewContent) free(lpszNewContent); return NULL; } + if (i>=d->nClear) return addClear_WOSD(lpszNewContent); + XMLClear *p=d->pClear+i; + if (lpszNewContent!=p->lpszValue) { free((void*)p->lpszValue); p->lpszValue=lpszNewContent; } + return p; +} + +XMLClear *XMLNode::updateClear_WOSD(XMLSTR lpszNewContent, XMLCSTR lpszOldValue) +{ + if (!d) { if (lpszNewContent) free(lpszNewContent); return NULL; } + int i=indexClear(lpszOldValue); + if (i>=0) return updateClear_WOSD(lpszNewContent,i); + return addClear_WOSD(lpszNewContent); +} + +XMLClear *XMLNode::updateClear_WOSD(XMLClear *newP,XMLClear *oldP) +{ + if (oldP) return updateClear_WOSD((XMLSTR)newP->lpszValue,(XMLSTR)oldP->lpszValue); + return NULL; +} + +int XMLNode::nChildNode(XMLCSTR name) const +{ + if (!d) return 0; + int i,j=0,n=d->nChild; + XMLNode *pc=d->pChild; + for (i=0; id->lpszName, name)==0) j++; + pc++; + } + return j; +} + +XMLNode XMLNode::getChildNode(XMLCSTR name, int *j) const +{ + if (!d) return emptyXMLNode; + int i=0,n=d->nChild; + if (j) i=*j; + XMLNode *pc=d->pChild+i; + for (; id->lpszName, name)) + { + if (j) *j=i+1; + return *pc; + } + pc++; + } + return emptyXMLNode; +} + +XMLNode XMLNode::getChildNode(XMLCSTR name, int j) const +{ + if (!d) return emptyXMLNode; + if (j>=0) + { + int i=0; + while (j-->0) getChildNode(name,&i); + return getChildNode(name,&i); + } + int i=d->nChild; + while (i--) if (!xstricmp(name,d->pChild[i].d->lpszName)) break; + if (i<0) return emptyXMLNode; + return getChildNode(i); +} + +XMLNode XMLNode::getChildNodeByPath(XMLCSTR _path, char createMissing, XMLCHAR sep) +{ + XMLSTR path=stringDup(_path); + XMLNode x=getChildNodeByPathNonConst(path,createMissing,sep); + if (path) free(path); + return x; +} + +XMLNode XMLNode::getChildNodeByPathNonConst(XMLSTR path, char createIfMissing, XMLCHAR sep) +{ + if ((!path)||(!(*path))) return *this; + XMLNode xn,xbase=*this; + XMLCHAR *tend1,sepString[2]; sepString[0]=sep; sepString[1]=0; + tend1=xstrstr(path,sepString); + while(tend1) + { + *tend1=0; + xn=xbase.getChildNode(path); + if (xn.isEmpty()) + { + if (createIfMissing) xn=xbase.addChild(path); + else { *tend1=sep; return XMLNode::emptyXMLNode; } + } + *tend1=sep; + xbase=xn; + path=tend1+1; + tend1=xstrstr(path,sepString); + } + xn=xbase.getChildNode(path); + if (xn.isEmpty()&&createIfMissing) xn=xbase.addChild(path); + return xn; +} + +XMLElementPosition XMLNode::positionOfText (int i) const { if (i>=d->nText ) i=d->nText-1; return findPosition(d,i,eNodeText ); } +XMLElementPosition XMLNode::positionOfClear (int i) const { if (i>=d->nClear) i=d->nClear-1; return findPosition(d,i,eNodeClear); } +XMLElementPosition XMLNode::positionOfChildNode(int i) const { if (i>=d->nChild) i=d->nChild-1; return findPosition(d,i,eNodeChild); } +XMLElementPosition XMLNode::positionOfText (XMLCSTR lpszValue) const { return positionOfText (indexText (lpszValue)); } +XMLElementPosition XMLNode::positionOfClear(XMLCSTR lpszValue) const { return positionOfClear(indexClear(lpszValue)); } +XMLElementPosition XMLNode::positionOfClear(XMLClear *a) const { if (a) return positionOfClear(a->lpszValue); return positionOfClear(); } +XMLElementPosition XMLNode::positionOfChildNode(XMLNode x) const +{ + if ((!d)||(!x.d)) return -1; + XMLNodeData *dd=x.d; + XMLNode *pc=d->pChild; + int i=d->nChild; + while (i--) if (pc[i].d==dd) return findPosition(d,i,eNodeChild); + return -1; +} +XMLElementPosition XMLNode::positionOfChildNode(XMLCSTR name, int count) const +{ + if (!name) return positionOfChildNode(count); + int j=0; + do { getChildNode(name,&j); if (j<0) return -1; } while (count--); + return findPosition(d,j-1,eNodeChild); +} + +XMLNode XMLNode::getChildNodeWithAttribute(XMLCSTR name,XMLCSTR attributeName,XMLCSTR attributeValue, int *k) const +{ + int i=0,j; + if (k) i=*k; + XMLNode x; + XMLCSTR t; + do + { + x=getChildNode(name,&i); + if (!x.isEmpty()) + { + if (attributeValue) + { + j=0; + do + { + t=x.getAttribute(attributeName,&j); + if (t&&(xstricmp(attributeValue,t)==0)) { if (k) *k=i; return x; } + } while (t); + } else + { + if (x.isAttributeSet(attributeName)) { if (k) *k=i; return x; } + } + } + } while (!x.isEmpty()); + return emptyXMLNode; +} + +// Find an attribute on an node. +XMLCSTR XMLNode::getAttribute(XMLCSTR lpszAttrib, int *j) const +{ + if (!d) return NULL; + int i=0,n=d->nAttribute; + if (j) i=*j; + XMLAttribute *pAttr=d->pAttribute+i; + for (; ilpszName, lpszAttrib)==0) + { + if (j) *j=i+1; + return pAttr->lpszValue; + } + pAttr++; + } + return NULL; +} + +char XMLNode::isAttributeSet(XMLCSTR lpszAttrib) const +{ + if (!d) return FALSE; + int i,n=d->nAttribute; + XMLAttribute *pAttr=d->pAttribute; + for (i=0; ilpszName, lpszAttrib)==0) + { + return TRUE; + } + pAttr++; + } + return FALSE; +} + +XMLCSTR XMLNode::getAttribute(XMLCSTR name, int j) const +{ + if (!d) return NULL; + int i=0; + while (j-->0) getAttribute(name,&i); + return getAttribute(name,&i); +} + +XMLNodeContents XMLNode::enumContents(int i) const +{ + XMLNodeContents c; + if (!d) { c.etype=eNodeNULL; return c; } + if (inAttribute) + { + c.etype=eNodeAttribute; + c.attrib=d->pAttribute[i]; + return c; + } + i-=d->nAttribute; + c.etype=(XMLElementType)(d->pOrder[i]&3); + i=(d->pOrder[i])>>2; + switch (c.etype) + { + case eNodeChild: c.child = d->pChild[i]; break; + case eNodeText: c.text = d->pText[i]; break; + case eNodeClear: c.clear = d->pClear[i]; break; + default: break; + } + return c; +} + +XMLCSTR XMLNode::getName() const { if (!d) return NULL; return d->lpszName; } +int XMLNode::nText() const { if (!d) return 0; return d->nText; } +int XMLNode::nChildNode() const { if (!d) return 0; return d->nChild; } +int XMLNode::nAttribute() const { if (!d) return 0; return d->nAttribute; } +int XMLNode::nClear() const { if (!d) return 0; return d->nClear; } +int XMLNode::nElement() const { if (!d) return 0; return d->nAttribute+d->nChild+d->nText+d->nClear; } +XMLClear XMLNode::getClear (int i) const { if ((!d)||(i>=d->nClear )) return emptyXMLClear; return d->pClear[i]; } +XMLAttribute XMLNode::getAttribute (int i) const { if ((!d)||(i>=d->nAttribute)) return emptyXMLAttribute; return d->pAttribute[i]; } +XMLCSTR XMLNode::getAttributeName (int i) const { if ((!d)||(i>=d->nAttribute)) return NULL; return d->pAttribute[i].lpszName; } +XMLCSTR XMLNode::getAttributeValue(int i) const { if ((!d)||(i>=d->nAttribute)) return NULL; return d->pAttribute[i].lpszValue; } +XMLCSTR XMLNode::getText (int i) const { if ((!d)||(i>=d->nText )) return NULL; return d->pText[i]; } +XMLNode XMLNode::getChildNode (int i) const { if ((!d)||(i>=d->nChild )) return emptyXMLNode; return d->pChild[i]; } +XMLNode XMLNode::getParentNode ( ) const { if ((!d)||(!d->pParent )) return emptyXMLNode; return XMLNode(d->pParent); } +char XMLNode::isDeclaration ( ) const { if (!d) return 0; return d->isDeclaration; } +char XMLNode::isEmpty ( ) const { return (d==NULL); } +XMLNode XMLNode::emptyNode ( ) { return XMLNode::emptyXMLNode; } + +XMLNode XMLNode::addChild(XMLCSTR lpszName, char isDeclaration, XMLElementPosition pos) + { return addChild_priv(0,stringDup(lpszName),isDeclaration,pos); } +XMLNode XMLNode::addChild_WOSD(XMLSTR lpszName, char isDeclaration, XMLElementPosition pos) + { return addChild_priv(0,lpszName,isDeclaration,pos); } +XMLAttribute *XMLNode::addAttribute(XMLCSTR lpszName, XMLCSTR lpszValue) + { return addAttribute_priv(0,stringDup(lpszName),stringDup(lpszValue)); } +XMLAttribute *XMLNode::addAttribute_WOSD(XMLSTR lpszName, XMLSTR lpszValuev) + { return addAttribute_priv(0,lpszName,lpszValuev); } +XMLCSTR XMLNode::addText(XMLCSTR lpszValue, XMLElementPosition pos) + { return addText_priv(0,stringDup(lpszValue),pos); } +XMLCSTR XMLNode::addText_WOSD(XMLSTR lpszValue, XMLElementPosition pos) + { return addText_priv(0,lpszValue,pos); } +XMLClear *XMLNode::addClear(XMLCSTR lpszValue, XMLCSTR lpszOpen, XMLCSTR lpszClose, XMLElementPosition pos) + { return addClear_priv(0,stringDup(lpszValue),lpszOpen,lpszClose,pos); } +XMLClear *XMLNode::addClear_WOSD(XMLSTR lpszValue, XMLCSTR lpszOpen, XMLCSTR lpszClose, XMLElementPosition pos) + { return addClear_priv(0,lpszValue,lpszOpen,lpszClose,pos); } +XMLCSTR XMLNode::updateName(XMLCSTR lpszName) + { return updateName_WOSD(stringDup(lpszName)); } +XMLAttribute *XMLNode::updateAttribute(XMLAttribute *newAttribute, XMLAttribute *oldAttribute) + { return updateAttribute_WOSD(stringDup(newAttribute->lpszValue),stringDup(newAttribute->lpszName),oldAttribute->lpszName); } +XMLAttribute *XMLNode::updateAttribute(XMLCSTR lpszNewValue, XMLCSTR lpszNewName,int i) + { return updateAttribute_WOSD(stringDup(lpszNewValue),stringDup(lpszNewName),i); } +XMLAttribute *XMLNode::updateAttribute(XMLCSTR lpszNewValue, XMLCSTR lpszNewName,XMLCSTR lpszOldName) + { return updateAttribute_WOSD(stringDup(lpszNewValue),stringDup(lpszNewName),lpszOldName); } +XMLCSTR XMLNode::updateText(XMLCSTR lpszNewValue, int i) + { return updateText_WOSD(stringDup(lpszNewValue),i); } +XMLCSTR XMLNode::updateText(XMLCSTR lpszNewValue, XMLCSTR lpszOldValue) + { return updateText_WOSD(stringDup(lpszNewValue),lpszOldValue); } +XMLClear *XMLNode::updateClear(XMLCSTR lpszNewContent, int i) + { return updateClear_WOSD(stringDup(lpszNewContent),i); } +XMLClear *XMLNode::updateClear(XMLCSTR lpszNewValue, XMLCSTR lpszOldValue) + { return updateClear_WOSD(stringDup(lpszNewValue),lpszOldValue); } +XMLClear *XMLNode::updateClear(XMLClear *newP,XMLClear *oldP) + { return updateClear_WOSD(stringDup(newP->lpszValue),oldP->lpszValue); } + +char XMLNode::setGlobalOptions(XMLCharEncoding _characterEncoding, char _guessWideCharChars, + char _dropWhiteSpace, char _removeCommentsInMiddleOfText) +{ + guessWideCharChars=_guessWideCharChars; dropWhiteSpace=_dropWhiteSpace; removeCommentsInMiddleOfText=_removeCommentsInMiddleOfText; +#ifdef _XMLWIDECHAR + if (_characterEncoding) characterEncoding=_characterEncoding; +#else + switch(_characterEncoding) + { + case char_encoding_UTF8: characterEncoding=_characterEncoding; XML_ByteTable=XML_utf8ByteTable; break; + case char_encoding_legacy: characterEncoding=_characterEncoding; XML_ByteTable=XML_legacyByteTable; break; + case char_encoding_ShiftJIS: characterEncoding=_characterEncoding; XML_ByteTable=XML_sjisByteTable; break; + case char_encoding_GB2312: characterEncoding=_characterEncoding; XML_ByteTable=XML_gb2312ByteTable; break; + case char_encoding_Big5: + case char_encoding_GBK: characterEncoding=_characterEncoding; XML_ByteTable=XML_gbk_big5_ByteTable; break; + default: return 1; + } +#endif + return 0; +} + +XMLNode::XMLCharEncoding XMLNode::guessCharEncoding(void *buf,int l, char useXMLEncodingAttribute) +{ +#ifdef _XMLWIDECHAR + return (XMLCharEncoding)0; +#else + if (l<25) return (XMLCharEncoding)0; + if (guessWideCharChars&&(myIsTextWideChar(buf,l))) return (XMLCharEncoding)0; + unsigned char *b=(unsigned char*)buf; + if ((b[0]==0xef)&&(b[1]==0xbb)&&(b[2]==0xbf)) return char_encoding_UTF8; + + // Match utf-8 model ? + XMLCharEncoding bestGuess=char_encoding_UTF8; + int i=0; + while (i>2 ]; + *(curr++)=base64EncodeTable[(inbuf[0]<<4)&0x3F]; + *(curr++)=base64Fillchar; + *(curr++)=base64Fillchar; + } else if (eLen==2) + { + j=(inbuf[0]<<8)|inbuf[1]; + *(curr++)=base64EncodeTable[ j>>10 ]; + *(curr++)=base64EncodeTable[(j>> 4)&0x3f]; + *(curr++)=base64EncodeTable[(j<< 2)&0x3f]; + *(curr++)=base64Fillchar; + } + *(curr++)=0; + return (XMLSTR)buf; +} + +unsigned int XMLParserBase64Tool::decodeSize(XMLCSTR data,XMLError *xe) +{ + if (!data) return 0; + if (xe) *xe=eXMLErrorNone; + int size=0; + unsigned char c; + //skip any extra characters (e.g. newlines or spaces) + while (*data) + { +#ifdef _XMLWIDECHAR + if (*data>255) { if (xe) *xe=eXMLErrorBase64DecodeIllegalCharacter; return 0; } +#endif + c=base64DecodeTable[(unsigned char)(*data)]; + if (c<97) size++; + else if (c==98) { if (xe) *xe=eXMLErrorBase64DecodeIllegalCharacter; return 0; } + data++; + } + if (xe&&(size%4!=0)) *xe=eXMLErrorBase64DataSizeIsNotMultipleOf4; + if (size==0) return 0; + do { data--; size--; } while(*data==base64Fillchar); size++; + return (unsigned int)((size*3)/4); +} + +unsigned char XMLParserBase64Tool::decode(XMLCSTR data, unsigned char *buf, int len, XMLError *xe) +{ + if (!data) return 0; + if (xe) *xe=eXMLErrorNone; + int i=0,p=0; + unsigned char d,c; + for(;;) + { + +#ifdef _XMLWIDECHAR +#define BASE64DECODE_READ_NEXT_CHAR(c) \ + do { \ + if (data[i]>255){ c=98; break; } \ + c=base64DecodeTable[(unsigned char)data[i++]]; \ + }while (c==97); \ + if(c==98){ if(xe)*xe=eXMLErrorBase64DecodeIllegalCharacter; return 0; } +#else +#define BASE64DECODE_READ_NEXT_CHAR(c) \ + do { c=base64DecodeTable[(unsigned char)data[i++]]; }while (c==97); \ + if(c==98){ if(xe)*xe=eXMLErrorBase64DecodeIllegalCharacter; return 0; } +#endif + + BASE64DECODE_READ_NEXT_CHAR(c) + if (c==99) { return 2; } + if (c==96) + { + if (p==(int)len) return 2; + if (xe) *xe=eXMLErrorBase64DecodeTruncatedData; + return 1; + } + + BASE64DECODE_READ_NEXT_CHAR(d) + if ((d==99)||(d==96)) { if (xe) *xe=eXMLErrorBase64DecodeTruncatedData; return 1; } + if (p==(int)len) { if (xe) *xe=eXMLErrorBase64DecodeBufferTooSmall; return 0; } + buf[p++]=(unsigned char)((c<<2)|((d>>4)&0x3)); + + BASE64DECODE_READ_NEXT_CHAR(c) + if (c==99) { if (xe) *xe=eXMLErrorBase64DecodeTruncatedData; return 1; } + if (p==(int)len) + { + if (c==96) return 2; + if (xe) *xe=eXMLErrorBase64DecodeBufferTooSmall; + return 0; + } + if (c==96) { if (xe) *xe=eXMLErrorBase64DecodeTruncatedData; return 1; } + buf[p++]=(unsigned char)(((d<<4)&0xf0)|((c>>2)&0xf)); + + BASE64DECODE_READ_NEXT_CHAR(d) + if (d==99 ) { if (xe) *xe=eXMLErrorBase64DecodeTruncatedData; return 1; } + if (p==(int)len) + { + if (d==96) return 2; + if (xe) *xe=eXMLErrorBase64DecodeBufferTooSmall; + return 0; + } + if (d==96) { if (xe) *xe=eXMLErrorBase64DecodeTruncatedData; return 1; } + buf[p++]=(unsigned char)(((c<<6)&0xc0)|d); + } +} +#undef BASE64DECODE_READ_NEXT_CHAR + +void XMLParserBase64Tool::alloc(int newsize) +{ + if ((!buf)&&(newsize)) { buf=malloc(newsize); buflen=newsize; return; } + if (newsize>buflen) { buf=realloc(buf,newsize); buflen=newsize; } +} + +unsigned char *XMLParserBase64Tool::decode(XMLCSTR data, int *outlen, XMLError *xe) +{ + if (xe) *xe=eXMLErrorNone; + if (!data) { *outlen=0; return (unsigned char*)""; } + unsigned int len=decodeSize(data,xe); + if (outlen) *outlen=len; + if (!len) return NULL; + alloc(len+1); + if(!decode(data,(unsigned char*)buf,len,xe)){ return NULL; } + return (unsigned char*)buf; +} + diff --git a/old/xmlParser.h b/old/xmlParser.h new file mode 100644 index 0000000..1187165 --- /dev/null +++ b/old/xmlParser.h @@ -0,0 +1,732 @@ +/****************************************************************************/ +/*! \mainpage XMLParser library + * \section intro_sec Introduction + * + * This is a basic XML parser written in ANSI C++ for portability. + * It works by using recursion and a node tree for breaking + * down the elements of an XML document. + * + * @version V2.44 + * @author Frank Vanden Berghen + * + * Copyright (c) 2002, Frank Vanden Berghen - All rights reserved.
+ * Commercialized by Business-Insight
+ * See the file AFPL-license.txt about the licensing terms + * + * \section tutorial First Tutorial + * You can follow a simple Tutorial to know the basics... + * + * \section usage General usage: How to include the XMLParser library inside your project. + * + * The library is composed of two files: xmlParser.cpp and + * xmlParser.h. These are the ONLY 2 files that you need when + * using the library inside your own projects. + * + * All the functions of the library are documented inside the comments of the file + * xmlParser.h. These comments can be transformed in + * full-fledged HTML documentation using the DOXYGEN software: simply type: "doxygen doxy.cfg" + * + * By default, the XMLParser library uses (char*) for string representation.To use the (wchar_t*) + * version of the library, you need to define the "_UNICODE" preprocessor definition variable + * (this is usually done inside your project definition file) (This is done automatically for you + * when using Visual Studio). + * + * \section example Advanced Tutorial and Many Examples of usage. + * + * Some very small introductory examples are described inside the Tutorial file + * xmlParser.html + * + * Some additional small examples are also inside the file xmlTest.cpp + * (for the "char*" version of the library) and inside the file + * xmlTestUnicode.cpp (for the "wchar_t*" + * version of the library). If you have a question, please review these additionnal examples + * before sending an e-mail to the author. + * + * To build the examples: + * - linux/unix: type "make" + * - solaris: type "make -f makefile.solaris" + * - windows: Visual Studio: double-click on xmlParser.dsw + * (under Visual Studio .NET, the .dsp and .dsw files will be automatically converted to .vcproj and .sln files) + * + * In order to build the examples you need some additional files: + * - linux/unix: makefile + * - solaris: makefile.solaris + * - windows: Visual Studio: *.dsp, xmlParser.dsw and also xmlParser.lib and xmlParser.dll + * + * \section debugging Debugging with the XMLParser library + * + * \subsection debugwin Debugging under WINDOWS + * + * Inside Visual C++, the "debug versions" of the memory allocation functions are + * very slow: Do not forget to compile in "release mode" to get maximum speed. + * When I had to debug a software that was using the XMLParser Library, it was usually + * a nightmare because the library was sooOOOoooo slow in debug mode (because of the + * slow memory allocations in Debug mode). To solve this + * problem, during all the debugging session, I am now using a very fast DLL version of the + * XMLParser Library (the DLL is compiled in release mode). Using the DLL version of + * the XMLParser Library allows me to have lightening XML parsing speed even in debug! + * Other than that, the DLL version is useless: In the release version of my tool, + * I always use the normal, ".cpp"-based, XMLParser Library (I simply include the + * xmlParser.cpp and + * xmlParser.h files into the project). + * + * The file XMLNodeAutoexp.txt contains some + * "tweaks" that improve substancially the display of the content of the XMLNode objects + * inside the Visual Studio Debugger. Believe me, once you have seen inside the debugger + * the "smooth" display of the XMLNode objects, you cannot live without it anymore! + * + * \subsection debuglinux Debugging under LINUX/UNIX + * + * The speed of the debug version of the XMLParser library is tolerable so no extra + * work.has been done. + * + ****************************************************************************/ + +#ifndef __INCLUDE_XML_NODE__ +#define __INCLUDE_XML_NODE__ + +#include + +#if defined(UNICODE) || defined(_UNICODE) +// If you comment the next "define" line then the library will never "switch to" _UNICODE (wchar_t*) mode (16/32 bits per characters). +// This is useful when you get error messages like: +// 'XMLNode::openFileHelper' : cannot convert parameter 2 from 'const char [5]' to 'const wchar_t *' +// The _XMLWIDECHAR preprocessor variable force the XMLParser library into either utf16/32-mode (the proprocessor variable +// must be defined) or utf8-mode(the pre-processor variable must be undefined). +#define _XMLWIDECHAR +#endif + +#if defined(WIN32) || defined(UNDER_CE) || defined(_WIN32) || defined(WIN64) || defined(__BORLANDC__) +// comment the next line if you are under windows and the compiler is not Microsoft Visual Studio (6.0 or .NET) or Borland +#define _XMLWINDOWS +#endif + +#ifdef XMLDLLENTRY +#undef XMLDLLENTRY +#endif +#ifdef _USE_XMLPARSER_DLL +#ifdef _DLL_EXPORTS_ +#define XMLDLLENTRY __declspec(dllexport) +#else +#define XMLDLLENTRY __declspec(dllimport) +#endif +#else +#define XMLDLLENTRY +#endif + +// uncomment the next line if you want no support for wchar_t* (no need for the or libraries anymore to compile) +//#define XML_NO_WIDE_CHAR + +#ifdef XML_NO_WIDE_CHAR +#undef _XMLWINDOWS +#undef _XMLWIDECHAR +#endif + +#ifdef _XMLWINDOWS +#include +#else +#define XMLDLLENTRY +#ifndef XML_NO_WIDE_CHAR +#include // to have 'wcsrtombs' for ANSI version + // to have 'mbsrtowcs' for WIDECHAR version +#endif +#endif + +// Some common types for char set portable code +#ifdef _XMLWIDECHAR + #define _CXML(c) L ## c + #define XMLCSTR const wchar_t * + #define XMLSTR wchar_t * + #define XMLCHAR wchar_t +#else + #define _CXML(c) c + #define XMLCSTR const char * + #define XMLSTR char * + #define XMLCHAR char +#endif +#ifndef FALSE + #define FALSE 0 +#endif /* FALSE */ +#ifndef TRUE + #define TRUE 1 +#endif /* TRUE */ + + +/// Enumeration for XML parse errors. +typedef enum XMLError +{ + eXMLErrorNone = 0, + eXMLErrorMissingEndTag, + eXMLErrorNoXMLTagFound, + eXMLErrorEmpty, + eXMLErrorMissingTagName, + eXMLErrorMissingEndTagName, + eXMLErrorUnmatchedEndTag, + eXMLErrorUnmatchedEndClearTag, + eXMLErrorUnexpectedToken, + eXMLErrorNoElements, + eXMLErrorFileNotFound, + eXMLErrorFirstTagNotFound, + eXMLErrorUnknownCharacterEntity, + eXMLErrorCharacterCodeAbove255, + eXMLErrorCharConversionError, + eXMLErrorCannotOpenWriteFile, + eXMLErrorCannotWriteFile, + + eXMLErrorBase64DataSizeIsNotMultipleOf4, + eXMLErrorBase64DecodeIllegalCharacter, + eXMLErrorBase64DecodeTruncatedData, + eXMLErrorBase64DecodeBufferTooSmall +} XMLError; + + +/// Enumeration used to manage type of data. Use in conjunction with structure XMLNodeContents +typedef enum XMLElementType +{ + eNodeChild=0, + eNodeAttribute=1, + eNodeText=2, + eNodeClear=3, + eNodeNULL=4 +} XMLElementType; + +/// Structure used to obtain error details if the parse fails. +typedef struct XMLResults +{ + enum XMLError error; + int nLine,nColumn; +} XMLResults; + +/// Structure for XML clear (unformatted) node (usually comments) +typedef struct XMLClear { + XMLCSTR lpszValue; XMLCSTR lpszOpenTag; XMLCSTR lpszCloseTag; +} XMLClear; + +/// Structure for XML attribute. +typedef struct XMLAttribute { + XMLCSTR lpszName; XMLCSTR lpszValue; +} XMLAttribute; + +/// XMLElementPosition are not interchangeable with simple indexes +typedef int XMLElementPosition; + +struct XMLNodeContents; + +/** @defgroup XMLParserGeneral The XML parser */ + +/// Main Class representing a XML node +/** + * All operations are performed using this class. + * \note The constructors of the XMLNode class are protected, so use instead one of these four methods to get your first instance of XMLNode: + *
    + *
  • XMLNode::parseString
  • + *
  • XMLNode::parseFile
  • + *
  • XMLNode::openFileHelper
  • + *
  • XMLNode::createXMLTopNode (or XMLNode::createXMLTopNode_WOSD)
  • + *
*/ +typedef struct XMLDLLENTRY XMLNode +{ + private: + + struct XMLNodeDataTag; + + /// Constructors are protected, so use instead one of: XMLNode::parseString, XMLNode::parseFile, XMLNode::openFileHelper, XMLNode::createXMLTopNode + XMLNode(struct XMLNodeDataTag *pParent, XMLSTR lpszName, char isDeclaration); + /// Constructors are protected, so use instead one of: XMLNode::parseString, XMLNode::parseFile, XMLNode::openFileHelper, XMLNode::createXMLTopNode + XMLNode(struct XMLNodeDataTag *p); + + public: + static XMLCSTR getVersion();///< Return the XMLParser library version number + + /** @defgroup conversions Parsing XML files/strings to an XMLNode structure and Rendering XMLNode's to files/string. + * @ingroup XMLParserGeneral + * @{ */ + + /// Parse an XML string and return the root of a XMLNode tree representing the string. + static XMLNode parseString (XMLCSTR lpXMLString, XMLCSTR tag=NULL, XMLResults *pResults=NULL); + /**< The "parseString" function parse an XML string and return the root of a XMLNode tree. The "opposite" of this function is + * the function "createXMLString" that re-creates an XML string from an XMLNode tree. If the XML document is corrupted, the + * "parseString" method will initialize the "pResults" variable with some information that can be used to trace the error. + * If you still want to parse the file, you can use the APPROXIMATE_PARSING option as explained inside the note at the + * beginning of the "xmlParser.cpp" file. + * + * @param lpXMLString the XML string to parse + * @param tag the name of the first tag inside the XML file. If the tag parameter is omitted, this function returns a node that represents the head of the xml document including the declaration term (). + * @param pResults a pointer to a XMLResults variable that will contain some information that can be used to trace the XML parsing error. You can have a user-friendly explanation of the parsing error with the "getError" function. + */ + + /// Parse an XML file and return the root of a XMLNode tree representing the file. + static XMLNode parseFile (XMLCSTR filename, XMLCSTR tag=NULL, XMLResults *pResults=NULL); + /**< The "parseFile" function parse an XML file and return the root of a XMLNode tree. The "opposite" of this function is + * the function "writeToFile" that re-creates an XML file from an XMLNode tree. If the XML document is corrupted, the + * "parseFile" method will initialize the "pResults" variable with some information that can be used to trace the error. + * If you still want to parse the file, you can use the APPROXIMATE_PARSING option as explained inside the note at the + * beginning of the "xmlParser.cpp" file. + * + * @param filename the path to the XML file to parse + * @param tag the name of the first tag inside the XML file. If the tag parameter is omitted, this function returns a node that represents the head of the xml document including the declaration term (). + * @param pResults a pointer to a XMLResults variable that will contain some information that can be used to trace the XML parsing error. You can have a user-friendly explanation of the parsing error with the "getError" function. + */ + + /// Parse an XML file and return the root of a XMLNode tree representing the file. A very crude error checking is made. An attempt to guess the Char Encoding used in the file is made. + static XMLNode openFileHelper(XMLCSTR filename, XMLCSTR tag=NULL); + /**< The "openFileHelper" function reports to the screen all the warnings and errors that occurred during parsing of the XML file. + * This function also tries to guess char Encoding (UTF-8, ASCII or SHIT-JIS) based on the first 200 bytes of the file. Since each + * application has its own way to report and deal with errors, you should rather use the "parseFile" function to parse XML files + * and program yourself thereafter an "error reporting" tailored for your needs (instead of using the very crude "error reporting" + * mechanism included inside the "openFileHelper" function). + * + * If the XML document is corrupted, the "openFileHelper" method will: + * - display an error message on the console (or inside a messageBox for windows). + * - stop execution (exit). + * + * I strongly suggest that you write your own "openFileHelper" method tailored to your needs. If you still want to parse + * the file, you can use the APPROXIMATE_PARSING option as explained inside the note at the beginning of the "xmlParser.cpp" file. + * + * @param filename the path of the XML file to parse. + * @param tag the name of the first tag inside the XML file. If the tag parameter is omitted, this function returns a node that represents the head of the xml document including the declaration term (). + */ + + static XMLCSTR getError(XMLError error); ///< this gives you a user-friendly explanation of the parsing error + + /// Create an XML string starting from the current XMLNode. + XMLSTR createXMLString(int nFormat=1, int *pnSize=NULL) const; + /**< The returned string should be free'd using the "freeXMLString" function. + * + * If nFormat==0, no formatting is required otherwise this returns an user friendly XML string from a given element + * with appropriate white spaces and carriage returns. if pnSize is given it returns the size in character of the string. */ + + /// Save the content of an xmlNode inside a file + XMLError writeToFile(XMLCSTR filename, + const char *encoding=NULL, + char nFormat=1) const; + /**< If nFormat==0, no formatting is required otherwise this returns an user friendly XML string from a given element with appropriate white spaces and carriage returns. + * If the global parameter "characterEncoding==encoding_UTF8", then the "encoding" parameter is ignored and always set to "utf-8". + * If the global parameter "characterEncoding==encoding_ShiftJIS", then the "encoding" parameter is ignored and always set to "SHIFT-JIS". + * If "_XMLWIDECHAR=1", then the "encoding" parameter is ignored and always set to "utf-16". + * If no "encoding" parameter is given the "ISO-8859-1" encoding is used. */ + /** @} */ + + /** @defgroup navigate Navigate the XMLNode structure + * @ingroup XMLParserGeneral + * @{ */ + XMLCSTR getName() const; ///< name of the node + XMLCSTR getText(int i=0) const; ///< return ith text field + int nText() const; ///< nbr of text field + XMLNode getParentNode() const; ///< return the parent node + XMLNode getChildNode(int i=0) const; ///< return ith child node + XMLNode getChildNode(XMLCSTR name, int i) const; ///< return ith child node with specific name (return an empty node if failing). If i==-1, this returns the last XMLNode with the given name. + XMLNode getChildNode(XMLCSTR name, int *i=NULL) const; ///< return next child node with specific name (return an empty node if failing) + XMLNode getChildNodeWithAttribute(XMLCSTR tagName, + XMLCSTR attributeName, + XMLCSTR attributeValue=NULL, + int *i=NULL) const; ///< return child node with specific name/attribute (return an empty node if failing) + XMLNode getChildNodeByPath(XMLCSTR path, char createNodeIfMissing=0, XMLCHAR sep='/'); + ///< return the first child node with specific path + XMLNode getChildNodeByPathNonConst(XMLSTR path, char createNodeIfMissing=0, XMLCHAR sep='/'); + ///< return the first child node with specific path. + + int nChildNode(XMLCSTR name) const; ///< return the number of child node with specific name + int nChildNode() const; ///< nbr of child node + XMLAttribute getAttribute(int i=0) const; ///< return ith attribute + XMLCSTR getAttributeName(int i=0) const; ///< return ith attribute name + XMLCSTR getAttributeValue(int i=0) const; ///< return ith attribute value + char isAttributeSet(XMLCSTR name) const; ///< test if an attribute with a specific name is given + XMLCSTR getAttribute(XMLCSTR name, int i) const; ///< return ith attribute content with specific name (return a NULL if failing) + XMLCSTR getAttribute(XMLCSTR name, int *i=NULL) const; ///< return next attribute content with specific name (return a NULL if failing) + int nAttribute() const; ///< nbr of attribute + XMLClear getClear(int i=0) const; ///< return ith clear field (comments) + int nClear() const; ///< nbr of clear field + XMLNodeContents enumContents(XMLElementPosition i) const; ///< enumerate all the different contents (attribute,child,text, clear) of the current XMLNode. The order is reflecting the order of the original file/string. NOTE: 0 <= i < nElement(); + int nElement() const; ///< nbr of different contents for current node + char isEmpty() const; ///< is this node Empty? + char isDeclaration() const; ///< is this node a declaration + XMLNode deepCopy() const; ///< deep copy (duplicate/clone) a XMLNode + static XMLNode emptyNode(); ///< return XMLNode::emptyXMLNode; + /** @} */ + + ~XMLNode(); + XMLNode(const XMLNode &A); ///< to allow shallow/fast copy: + XMLNode& operator=( const XMLNode& A ); ///< to allow shallow/fast copy: + + XMLNode(): d(NULL){}; + static XMLNode emptyXMLNode; + static XMLClear emptyXMLClear; + static XMLAttribute emptyXMLAttribute; + + /** @defgroup xmlModify Create or Update the XMLNode structure + * @ingroup XMLParserGeneral + * The functions in this group allows you to create from scratch (or update) a XMLNode structure. Start by creating your top + * node with the "createXMLTopNode" function and then add new nodes with the "addChild" function. The parameter 'pos' gives + * the position where the childNode, the text or the XMLClearTag will be inserted. The default value (pos=-1) inserts at the + * end. The value (pos=0) insert at the beginning (Insertion at the beginning is slower than at the end).
+ * + * REMARK: 0 <= pos < nChild()+nText()+nClear()
+ */ + + /** @defgroup creation Creating from scratch a XMLNode structure + * @ingroup xmlModify + * @{ */ + static XMLNode createXMLTopNode(XMLCSTR lpszName, char isDeclaration=FALSE); ///< Create the top node of an XMLNode structure + XMLNode addChild(XMLCSTR lpszName, char isDeclaration=FALSE, XMLElementPosition pos=-1); ///< Add a new child node + XMLNode addChild(XMLNode nodeToAdd, XMLElementPosition pos=-1); ///< If the "nodeToAdd" has some parents, it will be detached from it's parents before being attached to the current XMLNode + XMLAttribute *addAttribute(XMLCSTR lpszName, XMLCSTR lpszValuev); ///< Add a new attribute + XMLCSTR addText(XMLCSTR lpszValue, XMLElementPosition pos=-1); ///< Add a new text content + XMLClear *addClear(XMLCSTR lpszValue, XMLCSTR lpszOpen=NULL, XMLCSTR lpszClose=NULL, XMLElementPosition pos=-1); + /**< Add a new clear tag + * @param lpszOpen default value "" + */ + /** @} */ + + /** @defgroup xmlUpdate Updating Nodes + * @ingroup xmlModify + * Some update functions: + * @{ + */ + XMLCSTR updateName(XMLCSTR lpszName); ///< change node's name + XMLAttribute *updateAttribute(XMLAttribute *newAttribute, XMLAttribute *oldAttribute); ///< if the attribute to update is missing, a new one will be added + XMLAttribute *updateAttribute(XMLCSTR lpszNewValue, XMLCSTR lpszNewName=NULL,int i=0); ///< if the attribute to update is missing, a new one will be added + XMLAttribute *updateAttribute(XMLCSTR lpszNewValue, XMLCSTR lpszNewName,XMLCSTR lpszOldName);///< set lpszNewName=NULL if you don't want to change the name of the attribute if the attribute to update is missing, a new one will be added + XMLCSTR updateText(XMLCSTR lpszNewValue, int i=0); ///< if the text to update is missing, a new one will be added + XMLCSTR updateText(XMLCSTR lpszNewValue, XMLCSTR lpszOldValue); ///< if the text to update is missing, a new one will be added + XMLClear *updateClear(XMLCSTR lpszNewContent, int i=0); ///< if the clearTag to update is missing, a new one will be added + XMLClear *updateClear(XMLClear *newP,XMLClear *oldP); ///< if the clearTag to update is missing, a new one will be added + XMLClear *updateClear(XMLCSTR lpszNewValue, XMLCSTR lpszOldValue); ///< if the clearTag to update is missing, a new one will be added + /** @} */ + + /** @defgroup xmlDelete Deleting Nodes or Attributes + * @ingroup xmlModify + * Some deletion functions: + * @{ + */ + /// The "deleteNodeContent" function forces the deletion of the content of this XMLNode and the subtree. + void deleteNodeContent(); + /**< \note The XMLNode instances that are referring to the part of the subtree that has been deleted CANNOT be used anymore!!. Unexpected results will occur if you continue using them. */ + void deleteAttribute(int i=0); ///< Delete the ith attribute of the current XMLNode + void deleteAttribute(XMLCSTR lpszName); ///< Delete the attribute with the given name (the "strcmp" function is used to find the right attribute) + void deleteAttribute(XMLAttribute *anAttribute); ///< Delete the attribute with the name "anAttribute->lpszName" (the "strcmp" function is used to find the right attribute) + void deleteText(int i=0); ///< Delete the Ith text content of the current XMLNode + void deleteText(XMLCSTR lpszValue); ///< Delete the text content "lpszValue" inside the current XMLNode (direct "pointer-to-pointer" comparison is used to find the right text) + void deleteClear(int i=0); ///< Delete the Ith clear tag inside the current XMLNode + void deleteClear(XMLCSTR lpszValue); ///< Delete the clear tag "lpszValue" inside the current XMLNode (direct "pointer-to-pointer" comparison is used to find the clear tag) + void deleteClear(XMLClear *p); ///< Delete the clear tag "p" inside the current XMLNode (direct "pointer-to-pointer" comparison on the lpszName of the clear tag is used to find the clear tag) + /** @} */ + + /** @defgroup xmlWOSD ???_WOSD functions. + * @ingroup xmlModify + * The strings given as parameters for the "add" and "update" methods that have a name with + * the postfix "_WOSD" (that means "WithOut String Duplication")(for example "addText_WOSD") + * will be free'd by the XMLNode class. For example, it means that this is incorrect: + * \code + * xNode.addText_WOSD("foo"); + * xNode.updateAttribute_WOSD("#newcolor" ,NULL,"color"); + * \endcode + * In opposition, this is correct: + * \code + * xNode.addText("foo"); + * xNode.addText_WOSD(stringDup("foo")); + * xNode.updateAttribute("#newcolor" ,NULL,"color"); + * xNode.updateAttribute_WOSD(stringDup("#newcolor"),NULL,"color"); + * \endcode + * Typically, you will never do: + * \code + * char *b=(char*)malloc(...); + * xNode.addText(b); + * free(b); + * \endcode + * ... but rather: + * \code + * char *b=(char*)malloc(...); + * xNode.addText_WOSD(b); + * \endcode + * ('free(b)' is performed by the XMLNode class) + * @{ */ + static XMLNode createXMLTopNode_WOSD(XMLSTR lpszName, char isDeclaration=FALSE); ///< Create the top node of an XMLNode structure + XMLNode addChild_WOSD(XMLSTR lpszName, char isDeclaration=FALSE, XMLElementPosition pos=-1); ///< Add a new child node + XMLAttribute *addAttribute_WOSD(XMLSTR lpszName, XMLSTR lpszValue); ///< Add a new attribute + XMLCSTR addText_WOSD(XMLSTR lpszValue, XMLElementPosition pos=-1); ///< Add a new text content + XMLClear *addClear_WOSD(XMLSTR lpszValue, XMLCSTR lpszOpen=NULL, XMLCSTR lpszClose=NULL, XMLElementPosition pos=-1); ///< Add a new clear Tag + + XMLCSTR updateName_WOSD(XMLSTR lpszName); ///< change node's name + XMLAttribute *updateAttribute_WOSD(XMLAttribute *newAttribute, XMLAttribute *oldAttribute); ///< if the attribute to update is missing, a new one will be added + XMLAttribute *updateAttribute_WOSD(XMLSTR lpszNewValue, XMLSTR lpszNewName=NULL,int i=0); ///< if the attribute to update is missing, a new one will be added + XMLAttribute *updateAttribute_WOSD(XMLSTR lpszNewValue, XMLSTR lpszNewName,XMLCSTR lpszOldName); ///< set lpszNewName=NULL if you don't want to change the name of the attribute if the attribute to update is missing, a new one will be added + XMLCSTR updateText_WOSD(XMLSTR lpszNewValue, int i=0); ///< if the text to update is missing, a new one will be added + XMLCSTR updateText_WOSD(XMLSTR lpszNewValue, XMLCSTR lpszOldValue); ///< if the text to update is missing, a new one will be added + XMLClear *updateClear_WOSD(XMLSTR lpszNewContent, int i=0); ///< if the clearTag to update is missing, a new one will be added + XMLClear *updateClear_WOSD(XMLClear *newP,XMLClear *oldP); ///< if the clearTag to update is missing, a new one will be added + XMLClear *updateClear_WOSD(XMLSTR lpszNewValue, XMLCSTR lpszOldValue); ///< if the clearTag to update is missing, a new one will be added + /** @} */ + + /** @defgroup xmlPosition Position helper functions (use in conjunction with the update&add functions + * @ingroup xmlModify + * These are some useful functions when you want to insert a childNode, a text or a XMLClearTag in the + * middle (at a specified position) of a XMLNode tree already constructed. The value returned by these + * methods is to be used as last parameter (parameter 'pos') of addChild, addText or addClear. + * @{ */ + XMLElementPosition positionOfText(int i=0) const; + XMLElementPosition positionOfText(XMLCSTR lpszValue) const; + XMLElementPosition positionOfClear(int i=0) const; + XMLElementPosition positionOfClear(XMLCSTR lpszValue) const; + XMLElementPosition positionOfClear(XMLClear *a) const; + XMLElementPosition positionOfChildNode(int i=0) const; + XMLElementPosition positionOfChildNode(XMLNode x) const; + XMLElementPosition positionOfChildNode(XMLCSTR name, int i=0) const; ///< return the position of the ith childNode with the specified name if (name==NULL) return the position of the ith childNode + /** @} */ + + /// Enumeration for XML character encoding. + typedef enum XMLCharEncoding + { + char_encoding_error=0, + char_encoding_UTF8=1, + char_encoding_legacy=2, + char_encoding_ShiftJIS=3, + char_encoding_GB2312=4, + char_encoding_Big5=5, + char_encoding_GBK=6 // this is actually the same as Big5 + } XMLCharEncoding; + + /** \addtogroup conversions + * @{ */ + + /// Sets the global options for the conversions + static char setGlobalOptions(XMLCharEncoding characterEncoding=XMLNode::char_encoding_UTF8, char guessWideCharChars=1, + char dropWhiteSpace=1, char removeCommentsInMiddleOfText=1); + /**< The "setGlobalOptions" function allows you to change four global parameters that affect string & file + * parsing. First of all, you most-probably will never have to change these 3 global parameters. + * + * @param guessWideCharChars If "guessWideCharChars"=1 and if this library is compiled in WideChar mode, then the + * XMLNode::parseFile and XMLNode::openFileHelper functions will test if the file contains ASCII + * characters. If this is the case, then the file will be loaded and converted in memory to + * WideChar before being parsed. If 0, no conversion will be performed. + * + * @param guessWideCharChars If "guessWideCharChars"=1 and if this library is compiled in ASCII/UTF8/char* mode, then the + * XMLNode::parseFile and XMLNode::openFileHelper functions will test if the file contains WideChar + * characters. If this is the case, then the file will be loaded and converted in memory to + * ASCII/UTF8/char* before being parsed. If 0, no conversion will be performed. + * + * @param characterEncoding This parameter is only meaningful when compiling in char* mode (multibyte character mode). + * In wchar_t* (wide char mode), this parameter is ignored. This parameter should be one of the + * three currently recognized encodings: XMLNode::encoding_UTF8, XMLNode::encoding_ascii, + * XMLNode::encoding_ShiftJIS. + * + * @param dropWhiteSpace In most situations, text fields containing only white spaces (and carriage returns) + * are useless. Even more, these "empty" text fields are annoying because they increase the + * complexity of the user's code for parsing. So, 99% of the time, it's better to drop + * the "empty" text fields. However The XML specification indicates that no white spaces + * should be lost when parsing the file. So to be perfectly XML-compliant, you should set + * dropWhiteSpace=0. A note of caution: if you set "dropWhiteSpace=0", the parser will be + * slower and your code will be more complex. + * + * @param removeCommentsInMiddleOfText To explain this parameter, let's consider this code: + * \code + * XMLNode x=XMLNode::parseString("foobarchu","a"); + * \endcode + * If removeCommentsInMiddleOfText=0, then we will have: + * \code + * x.getText(0) -> "foo" + * x.getText(1) -> "bar" + * x.getText(2) -> "chu" + * x.getClear(0) --> "" + * x.getClear(1) --> "" + * \endcode + * If removeCommentsInMiddleOfText=1, then we will have: + * \code + * x.getText(0) -> "foobar" + * x.getText(1) -> "chu" + * x.getClear(0) --> "" + * \endcode + * + * \return "0" when there are no errors. If you try to set an unrecognized encoding then the return value will be "1" to signal an error. + * + * \note Sometime, it's useful to set "guessWideCharChars=0" to disable any conversion + * because the test to detect the file-type (ASCII/UTF8/char* or WideChar) may fail (rarely). */ + + /// Guess the character encoding of the string (ascii, utf8 or shift-JIS) + static XMLCharEncoding guessCharEncoding(void *buffer, int bufLen, char useXMLEncodingAttribute=1); + /**< The "guessCharEncoding" function try to guess the character encoding. You most-probably will never + * have to use this function. It then returns the appropriate value of the global parameter + * "characterEncoding" described in the XMLNode::setGlobalOptions. The guess is based on the content of a buffer of length + * "bufLen" bytes that contains the first bytes (minimum 25 bytes; 200 bytes is a good value) of the + * file to be parsed. The XMLNode::openFileHelper function is using this function to automatically compute + * the value of the "characterEncoding" global parameter. There are several heuristics used to do the + * guess. One of the heuristic is based on the "encoding" attribute. The original XML specifications + * forbids to use this attribute to do the guess but you can still use it if you set + * "useXMLEncodingAttribute" to 1 (this is the default behavior and the behavior of most parsers). + * If an inconsistency in the encoding is detected, then the return value is "0". */ + /** @} */ + + private: + // these are functions and structures used internally by the XMLNode class (don't bother about them): + + typedef struct XMLNodeDataTag // to allow shallow copy and "intelligent/smart" pointers (automatic delete): + { + XMLCSTR lpszName; // Element name (=NULL if root) + int nChild, // Number of child nodes + nText, // Number of text fields + nClear, // Number of Clear fields (comments) + nAttribute; // Number of attributes + char isDeclaration; // Whether node is an XML declaration - '' + struct XMLNodeDataTag *pParent; // Pointer to parent element (=NULL if root) + XMLNode *pChild; // Array of child nodes + XMLCSTR *pText; // Array of text fields + XMLClear *pClear; // Array of clear fields + XMLAttribute *pAttribute; // Array of attributes + int *pOrder; // order of the child_nodes,text_fields,clear_fields + int ref_count; // for garbage collection (smart pointers) + } XMLNodeData; + XMLNodeData *d; + + char parseClearTag(void *px, void *pa); + char maybeAddTxT(void *pa, XMLCSTR tokenPStr); + int ParseXMLElement(void *pXML); + void *addToOrder(int memInc, int *_pos, int nc, void *p, int size, XMLElementType xtype); + int indexText(XMLCSTR lpszValue) const; + int indexClear(XMLCSTR lpszValue) const; + XMLNode addChild_priv(int,XMLSTR,char,int); + XMLAttribute *addAttribute_priv(int,XMLSTR,XMLSTR); + XMLCSTR addText_priv(int,XMLSTR,int); + XMLClear *addClear_priv(int,XMLSTR,XMLCSTR,XMLCSTR,int); + void emptyTheNode(char force); + static inline XMLElementPosition findPosition(XMLNodeData *d, int index, XMLElementType xtype); + static int CreateXMLStringR(XMLNodeData *pEntry, XMLSTR lpszMarker, int nFormat); + static int removeOrderElement(XMLNodeData *d, XMLElementType t, int index); + static void exactMemory(XMLNodeData *d); + static int detachFromParent(XMLNodeData *d); +} XMLNode; + +/// This structure is given by the function XMLNode::enumContents. +typedef struct XMLNodeContents +{ + /// This dictates what's the content of the XMLNodeContent + enum XMLElementType etype; + /**< should be an union to access the appropriate data. Compiler does not allow union of object with constructor... too bad. */ + XMLNode child; + XMLAttribute attrib; + XMLCSTR text; + XMLClear clear; + +} XMLNodeContents; + +/** @defgroup StringAlloc String Allocation/Free functions + * @ingroup xmlModify + * @{ */ +/// Duplicate (copy in a new allocated buffer) the source string. +XMLDLLENTRY XMLSTR stringDup(XMLCSTR source, int cbData=-1); +/**< This is + * a very handy function when used with all the "XMLNode::*_WOSD" functions (\link xmlWOSD \endlink). + * @param cbData If !=0 then cbData is the number of chars to duplicate. New strings allocated with + * this function should be free'd using the "freeXMLString" function. */ + +/// to free the string allocated inside the "stringDup" function or the "createXMLString" function. +XMLDLLENTRY void freeXMLString(XMLSTR t); // {free(t);} +/** @} */ + +/** @defgroup atoX ato? like functions + * @ingroup XMLParserGeneral + * The "xmlto?" functions are equivalents to the atoi, atol, atof functions. + * The only difference is: If the variable "xmlString" is NULL, than the return value + * is "defautValue". These 6 functions are only here as "convenience" functions for the + * user (they are not used inside the XMLparser). If you don't need them, you can + * delete them without any trouble. + * + * @{ */ +XMLDLLENTRY char xmltob(XMLCSTR xmlString,char defautValue=0); +XMLDLLENTRY int xmltoi(XMLCSTR xmlString,int defautValue=0); +XMLDLLENTRY long long xmltol(XMLCSTR xmlString,long long defautValue=0); +XMLDLLENTRY double xmltof(XMLCSTR xmlString,double defautValue=.0); +XMLDLLENTRY XMLCSTR xmltoa(XMLCSTR xmlString,XMLCSTR defautValue=_CXML("")); +XMLDLLENTRY XMLCHAR xmltoc(XMLCSTR xmlString,const XMLCHAR defautValue=_CXML('\0')); +/** @} */ + +/** @defgroup ToXMLStringTool Helper class to create XML files using "printf", "fprintf", "cout",... functions. + * @ingroup XMLParserGeneral + * @{ */ +/// Helper class to create XML files using "printf", "fprintf", "cout",... functions. +/** The ToXMLStringTool class helps you creating XML files using "printf", "fprintf", "cout",... functions. + * The "ToXMLStringTool" class is processing strings so that all the characters + * &,",',<,> are replaced by their XML equivalent: + * \verbatim &, ", ', <, > \endverbatim + * Using the "ToXMLStringTool class" and the "fprintf function" is THE most efficient + * way to produce VERY large XML documents VERY fast. + * \note If you are creating from scratch an XML file using the provided XMLNode class + * you must not use the "ToXMLStringTool" class (because the "XMLNode" class does the + * processing job for you during rendering).*/ +typedef struct XMLDLLENTRY ToXMLStringTool +{ +public: + ToXMLStringTool(): buf(NULL),buflen(0){} + ~ToXMLStringTool(); + void freeBuffer();///