/* 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_seconds) { std::chrono::milliseconds duration(total_seconds); // Convert to days, hours, minutes, and seconds auto days = std::chrono::duration_cast>>(duration); duration -= days; auto hours = std::chrono::duration_cast(duration); duration -= hours; 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()); }