/** * MaNGOS is a full featured server for World of Warcraft, supporting * the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8 * * Copyright (C) 2005-2020 MaNGOS * * This program 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 2 of the License, or * (at your option) any later version. * * This program 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 this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * World of Warcraft, and all World of Warcraft or Warcraft art, images, * and lore are copyrighted by Blizzard Entertainment, Inc. */ #include "Util.h" #include "Timer.h" #include "utf8.h" #include "RNGen.h" #include #include #include "Log/Log.h" static ACE_Time_Value g_SystemTickTime = ACE_OS::gettimeofday(); uint32 WorldTimer::m_iTime = 0; uint32 WorldTimer::m_iPrevTime = 0; uint32 WorldTimer::tickTime() { return m_iTime; } uint32 WorldTimer::tickPrevTime() { return m_iPrevTime; } uint32 WorldTimer::tick() { // save previous world tick time m_iPrevTime = m_iTime; // get the new one and don't forget to persist current system time in m_SystemTickTime m_iTime = WorldTimer::getMSTime_internal(); // return tick diff return getMSTimeDiff(m_iPrevTime, m_iTime); } uint32 WorldTimer::getMSTime() { return getMSTime_internal(); } uint32 WorldTimer::getMSTime_internal() { // get current time const ACE_Time_Value currTime = ACE_OS::gettimeofday(); // calculate time diff between two world ticks // special case: curr_time < old_time - we suppose that our time has not ticked at all // this should be constant value otherwise it is possible that our time can start ticking backwards until next world tick!!! uint64 diff = 0; (currTime - g_SystemTickTime).msec(diff); // lets calculate current world time uint32 iRes = uint32(diff % UI64LIT(0x00000000FFFFFFFF)); return iRes; } ////////////////////////////////////////////////////////////////////////// int32 irand(int32 min, int32 max) { return RNG::instance()->rand_i(min,max); } uint32 urand(uint32 min, uint32 max) { return RNG::instance()->rand_u(min,max); } float frand(float min, float max) { return RNG::instance()->rand_f(min, max); } uint32 rand32() { return RNG::instance()->rand(); } double rand_norm(void) { return RNG::instance()->rand_d(0.0, 1.0); } float rand_norm_f(void) { return RNG::instance()->rand_f(0.0, 1.0); } double rand_chance(void) { return RNG::instance()->rand_d(0.0, 100.0); } float rand_chance_f(void) { return RNG::instance()->rand_f(0.0, 100.0); } Tokens StrSplit(const std::string& src, const std::string& sep) { Tokens r; std::string s; for (std::string::const_iterator i = src.begin(); i != src.end(); ++i) { if (sep.find(*i) != std::string::npos) { if (s.length()) { r.push_back(s); } s = ""; } else { s += *i; } } if (s.length()) { r.push_back(s); } return r; } uint32 GetUInt32ValueFromArray(Tokens const& data, uint16 index) { if (index >= data.size()) { return 0; } return (uint32)atoi(data[index].c_str()); } float GetFloatValueFromArray(Tokens const& data, uint16 index) { float result; uint32 temp = GetUInt32ValueFromArray(data, index); memcpy(&result, &temp, sizeof(result)); return result; } void stripLineInvisibleChars(std::string& str) { static std::string invChars = " \t\7\n"; size_t wpos = 0; bool space = false; for (size_t pos = 0; pos < str.size(); ++pos) { if (invChars.find(str[pos]) != std::string::npos) { if (!space) { str[wpos++] = ' '; space = true; } } else { if (wpos != pos) { str[wpos++] = str[pos]; } else { ++wpos; } space = false; } } if (wpos < str.size()) { str.erase(wpos, str.size()); } } std::string secsToTimeString(time_t timeInSecs, bool shortText, bool hoursOnly) { time_t secs = timeInSecs % MINUTE; time_t minutes = timeInSecs % HOUR / MINUTE; time_t hours = timeInSecs % DAY / HOUR; time_t days = timeInSecs / DAY; std::ostringstream ss; if (days) { ss << days << (shortText ? "d" : " Day(s) "); } if (hours || hoursOnly) { ss << hours << (shortText ? "h" : " Hour(s) "); } if (!hoursOnly) { if (minutes) { ss << minutes << (shortText ? "m" : " Minute(s) "); } if (secs || (!days && !hours && !minutes)) { ss << secs << (shortText ? "s" : " Second(s)."); } } return ss.str(); } uint32 TimeStringToSecs(const std::string& timestring) { uint32 secs = 0; uint32 buffer = 0; uint32 multiplier = 0; for (std::string::const_iterator itr = timestring.begin(); itr != timestring.end(); ++itr) { if (isdigit(*itr)) { buffer *= 10; buffer += (*itr) - '0'; } else { switch (*itr) { case 'd': multiplier = DAY; break; case 'h': multiplier = HOUR; break; case 'm': multiplier = MINUTE; break; case 's': multiplier = 1; break; default : return 0; // bad format } buffer *= multiplier; secs += buffer; buffer = 0; } } return secs; } std::string TimeToTimestampStr(time_t t) { tm* aTm = localtime(&t); // YYYY year // MM month (2 digits 01-12) // DD day (2 digits 01-31) // HH hour (2 digits 00-23) // MM minutes (2 digits 00-59) // SS seconds (2 digits 00-59) char buf[20]; snprintf(buf, 20, "%04d-%02d-%02d_%02d-%02d-%02d", aTm->tm_year + 1900, aTm->tm_mon + 1, aTm->tm_mday, aTm->tm_hour, aTm->tm_min, aTm->tm_sec); return std::string(buf); } /// Check if the string is a valid ip address representation bool IsIPAddress(char const* ipaddress) { if (!ipaddress) { return false; } // Let the big boys do it. // Drawback: all valid ip address formats are recognized e.g.: 12.23,121234,0xABCD) return ACE_OS::inet_addr(ipaddress) != INADDR_NONE; } std::string GetAddressString(ACE_INET_Addr const& addr) { char buf[ACE_MAX_FULLY_QUALIFIED_NAME_LEN + 16]; addr.addr_to_string(buf, ACE_MAX_FULLY_QUALIFIED_NAME_LEN + 16); return buf; } bool IsIPAddrInNetwork(ACE_INET_Addr const& net, ACE_INET_Addr const& addr, ACE_INET_Addr const& subnetMask) { uint32 mask = subnetMask.get_ip_address(); if ((net.get_ip_address() & mask) == (addr.get_ip_address() & mask)) { return true; } return false; } /// create PID file uint32 CreatePIDFile(const std::string& filename) { FILE* pid_file = fopen(filename.c_str(), "w"); if (pid_file == NULL) { return 0; } #ifdef WIN32 DWORD pid = GetCurrentProcessId(); #else pid_t pid = getpid(); #endif fprintf(pid_file, "%d", pid); fclose(pid_file); return (uint32)pid; } size_t utf8length(std::string& utf8str) { try { return utf8::distance(utf8str.c_str(), utf8str.c_str() + utf8str.size()); } catch (std::exception) { utf8str = ""; return 0; } } void utf8truncate(std::string& utf8str, size_t len) { try { size_t wlen = utf8::distance(utf8str.c_str(), utf8str.c_str() + utf8str.size()); if (wlen <= len) { return; } std::wstring wstr; wstr.resize(wlen); utf8::utf8to16(utf8str.c_str(), utf8str.c_str() + utf8str.size(), &wstr[0]); wstr.resize(len); char* oend = utf8::utf16to8(wstr.c_str(), wstr.c_str() + wstr.size(), &utf8str[0]); utf8str.resize(oend - (&utf8str[0])); // remove unused tail } catch (std::exception) { utf8str = ""; } } bool Utf8toWStr(char const* utf8str, size_t csize, wchar_t* wstr, size_t& wsize) { try { size_t len = utf8::distance(utf8str, utf8str + csize); if (len > wsize) { if (wsize > 0) { wstr[0] = L'\0'; } wsize = 0; return false; } wsize = len; utf8::utf8to16(utf8str, utf8str + csize, wstr); wstr[len] = L'\0'; } catch (std::exception) { if (wsize > 0) { wstr[0] = L'\0'; } wsize = 0; return false; } return true; } bool Utf8toWStr(const std::string& utf8str, std::wstring& wstr) { try { size_t len = utf8::distance(utf8str.c_str(), utf8str.c_str() + utf8str.size()); wstr.resize(len); if (len) { utf8::utf8to16(utf8str.c_str(), utf8str.c_str() + utf8str.size(), &wstr[0]); } } catch (std::exception) { wstr = L""; return false; } return true; } bool WStrToUtf8(wchar_t* wstr, size_t size, std::string& utf8str) { try { std::string utf8str2; utf8str2.resize(size * 4); // allocate for most long case char* oend = utf8::utf16to8(wstr, wstr + size, &utf8str2[0]); utf8str2.resize(oend - (&utf8str2[0])); // remove unused tail utf8str = utf8str2; } catch (std::exception) { utf8str = ""; return false; } return true; } bool WStrToUtf8(std::wstring wstr, std::string& utf8str) { try { std::string utf8str2; utf8str2.resize(wstr.size() * 4); // allocate for most long case char* oend = utf8::utf16to8(wstr.c_str(), wstr.c_str() + wstr.size(), &utf8str2[0]); utf8str2.resize(oend - (&utf8str2[0])); // remove unused tail utf8str = utf8str2; } catch (std::exception) { utf8str = ""; return false; } return true; } typedef wchar_t const* const* wstrlist; bool utf8ToConsole(const std::string& utf8str, std::string& conStr) { #if PLATFORM == PLATFORM_WINDOWS std::wstring wstr; if (!Utf8toWStr(utf8str, wstr)) { return false; } conStr.resize(wstr.size()); CharToOemBuffW(&wstr[0], &conStr[0], wstr.size()); #else // not implemented yet conStr = utf8str; #endif return true; } bool consoleToUtf8(const std::string& conStr, std::string& utf8str) { #if PLATFORM == PLATFORM_WINDOWS std::wstring wstr; wstr.resize(conStr.size()); OemToCharBuffW(&conStr[0], &wstr[0], conStr.size()); return WStrToUtf8(wstr, utf8str); #else // not implemented yet utf8str = conStr; return true; #endif } bool Utf8FitTo(const std::string& str, std::wstring search) { std::wstring temp; if (!Utf8toWStr(str, temp)) { return false; } // converting to lower case wstrToLower(temp); if (temp.find(search) == std::wstring::npos) { return false; } return true; } void vutf8printf(FILE* out, const char* str, va_list* ap) { #if PLATFORM == PLATFORM_WINDOWS char temp_buf[32 * 1024]; wchar_t wtemp_buf[32 * 1024]; size_t temp_len = vsnprintf(temp_buf, 32 * 1024, str, *ap); size_t wtemp_len = 32 * 1024 - 1; Utf8toWStr(temp_buf, temp_len, wtemp_buf, wtemp_len); CharToOemBuffW(&wtemp_buf[0], &temp_buf[0], wtemp_len + 1); fprintf(out, "%s", temp_buf); #else vfprintf(out, str, *ap); #endif } void hexEncodeByteArray(uint8* bytes, uint32 arrayLen, std::string& result) { std::ostringstream ss; for (uint32 i = 0; i < arrayLen; ++i) { for (uint8 j = 0; j < 2; ++j) { unsigned char nibble = 0x0F & (bytes[i] >> ((1 - j) * 4)); char encodedNibble; if (nibble < 0x0A) { encodedNibble = '0' + nibble; } else { encodedNibble = 'A' + nibble - 0x0A; } ss << encodedNibble; } } result = ss.str(); } std::string ByteArrayToHexStr(uint8 const* bytes, uint32 arrayLen, bool reverse /* = false */) { int32 init = 0; int32 end = arrayLen; int8 op = 1; if (reverse) { init = arrayLen - 1; end = -1; op = -1; } std::ostringstream ss; for (int32 i = init; i != end; i += op) { char buffer[4]; sprintf(buffer, "%02X", bytes[i]); ss << buffer; } return ss.str(); } void HexStrToByteArray(std::string const& str, uint8* out, bool reverse /*= false*/) { // string must have even number of characters if (str.length() & 1) { return; } int32 init = 0; int32 end = str.length(); int8 op = 1; if (reverse) { init = str.length() - 2; end = -2; op = -1; } uint32 j = 0; for (int32 i = init; i != end; i += 2 * op) { char buffer[3] = { str[i], str[i + 1], '\0' }; out[j++] = strtoul(buffer, NULL, 16); } } void utf8print(void* /*arg*/, const char* str) { #if PLATFORM == PLATFORM_WINDOWS wchar_t wtemp_buf[6000]; size_t wtemp_len = 6000 - 1; if (!Utf8toWStr(str, strlen(str), wtemp_buf, wtemp_len)) { return; } char temp_buf[6000]; CharToOemBuffW(&wtemp_buf[0], &temp_buf[0], wtemp_len + 1); printf("%s", temp_buf); #else printf("%s", str); #endif } void utf8printf(FILE* out, const char* str, ...) { va_list ap; va_start(ap, str); vutf8printf(out, str, &ap); va_end(ap); } int return_iCoreNumber() { #if defined(CLASSIC) return 0; #elif defined(TBC) return 1; #elif defined(WOTLK) return 2; #elif defined(CATA) return 3; #elif defined(MOP) return 4; #elif defined(WOD) return 5; #elif defined(LEGION) return 6; #else return -1; #endif } /// Print out the core banner void print_banner() { int iCoreNumber = return_iCoreNumber(); switch (iCoreNumber) { case 0: // CLASSIC sLog.outString(" to stop.\n" " __ __ _ _ ___ ___ ___ ____ \n" " | \\/ |__ _| \\| |/ __|/ _ \\/ __| /_ /___ _ _ ___ \n" " | |\\/| / _` | .` | (_ | (_) \\__ \\ / // -_) '_/ _ \\ \n" " |_| |_\\__,_|_|\\_|\\___|\\___/|___/ /___\\___|_| \\___/\n" " Powered By MaNGOS Core\n" "__________________________________________________________\n" "\n" "Website/Forum/Wiki/Issue Tracker: https://www.getmangos.eu\n" "__________________________________________________________\n" "\n"); break; case 1: // TBC sLog.outString(" to stop.\n" " __ __ _ _ ___ ___ ___ ___ \n" " | \\/ |__ _| \\| |/ __|/ _ \\/ __| / _ \\ ___ ___ \n" " | |\\/| / _` | .` | (_ | (_) \\__ \\ | (_) | \\/ -_) \n" " |_| |_\\__,_|_|\\_|\\___|\\___/|___/ \\___/|_||_\\___|\n" " Powered By MaNGOS Core\n" " __________________________________________________________\n" "\n" " Website/Forum/Wiki/Issue Tracker: https://www.getmangos.eu\n" " __________________________________________________________\n" "\n"); break; case 2: // WOTLK sLog.outString(" to stop.\n" " __ __ _ _ ___ ___ ___ _____ \n" " | \\/ |__ _| \\| |/ __|/ _ \\/ __| |_ _|_ __ _____\n" " | |\\/| / _` | .` | (_ | (_) \\__ \\ | | \\ V V / _ \\\n" " |_| |_\\__,_|_|\\_|\\___|\\___/|___/ |_| \\_/\\_/\\___/ \n" " Powered By MaNGOS Core\n" " __________________________________________________________\n" "\n" " Website/Forum/Wiki/Issue Tracker: https://www.getmangos.eu\n" " __________________________________________________________\n" "\n"); break; case 3: // CATA sLog.outString(" to stop.\n" " __ __ _ _ ___ ___ ___ _____ _ \n" " | \\/ |__ _| \\| |/ __|/ _ \\/ __| |_ _| |_ _ _ ___ ___ \n" " | |\\/| / _` | .` | (_ | (_) \\__ \\ | | | ' \\| '_/ -_) -_) \n" " |_| |_\\__,_|_|\\_|\\___|\\___/|___/ |_| |_||_|_| \\___\\___| \n" " Powered By MaNGOS Core\n" " __________________________________________________________\n" "\n" " Website/Forum/Wiki/Issue Tracker: https://www.getmangos.eu\n" " __________________________________________________________\n" "\n"); break; case 4: // MOP sLog.outString(" to stop.\n" " __ __ _ _ ___ ___ ___ _____ \n" " | \\/ |__ _| \\| |/ __|/ _ \\/ __| | __|__ _ _ _ _ \n" " | |\\/| / _` | .` | (_ | (_) \\__ \\ | _/ _ \\ || | '_|\n" " |_| |_\\__,_|_|\\_|\\___|\\___/|___/ |_|\\___/\\_,_|_| \n" " Powered By MaNGOS Core\n" " __________________________________________________________\n" "\n" " Website/Forum/Wiki/Issue Tracker: https://www.getmangos.eu\n" " __________________________________________________________\n" "\n"); break; default: sLog.outString(" to stop.\n" " __ __ _ _ ___ ___ ___ \n" " | \\/ |__ _| \\| |/ __|/ _ \\/ __| We have a problem ! \n" " | |\\/| / _` | .` | (_ | (_) \\__ \\ Your version of MaNGOS \n" " |_| |_\\__,_|_|\\_|\\___|\\___/|___/ could not be detected \n" " __________________________________________________________\n" "\n" " Website/Forum/Wiki/Issue Tracker: https://www.getmangos.eu\n" " __________________________________________________________\n" "\n"); break; } }