/* * Copyright (c) Contributors to the Open 3D Engine Project. * For complete copyright and license terms please see the LICENSE at the root of this distribution. * * SPDX-License-Identifier: Apache-2.0 OR MIT * */ #include // Description : Linux/Mac port support for Win32API calls #if AZ_TRAIT_LEGACY_CRYCOMMON_USE_WINDOWS_STUBS #include "platform.h" // Note: This should be first to get consistent debugging definitions #include #include #if defined(AZ_RESTRICTED_PLATFORM) #undef AZ_RESTRICTED_SECTION #define WINBASE_CPP_SECTION_1 1 #define WINBASE_CPP_SECTION_2 2 #define WINBASE_CPP_SECTION_3 3 #define WINBASE_CPP_SECTION_4 4 #define WINBASE_CPP_SECTION_5 5 #define WINBASE_CPP_SECTION_6 6 #endif #if defined(AZ_RESTRICTED_PLATFORM) #define AZ_RESTRICTED_SECTION WINBASE_CPP_SECTION_1 #include AZ_RESTRICTED_FILE(WinBase_cpp) #endif #if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED) #undef AZ_RESTRICTED_SECTION_IMPLEMENTED #else #include #endif #include #include #include #include #include #ifdef APPLE #include #include #include // for total physical memory on Mac #include // for CryMessageBox #include // host_statistics #include #include #include #endif #include #if defined(LINUX) || defined(APPLE) #include #include #include #include #endif #if AZ_TRAIT_COMPILER_DEFINE_FS_ERRNO_TYPE typedef int FS_ERRNO_TYPE; #include #endif bool IsBadReadPtr(void* ptr, unsigned int size) { //too complicated to really support it return ptr ? false : true; } ////////////////////////////////////////////////////////////////////////// char* _strtime(char* date) { azstrcpy(date, AZ_ARRAY_SIZE(date), "0:0:0"); return date; } ////////////////////////////////////////////////////////////////////////// char* _strdate(char* date) { azstrcpy(date, AZ_ARRAY_SIZE(date), "0"); return date; } ////////////////////////////////////////////////////////////////////////// char* strlwr (char* str) { char* cp; /* traverses string for C locale conversion */ for (cp = str; *cp; ++cp) { if ('A' <= *cp && *cp <= 'Z') { *cp += 'a' - 'A'; } } return str; } char* strupr (char* str) { char* cp; /* traverses string for C locale conversion */ for (cp = str; *cp; ++cp) { if ('a' <= *cp && *cp <= 'z') { *cp += 'A' - 'a'; } } return str; } char* ltoa (long i, char* a, int radix) { if (a == nullptr) { return nullptr; } strcpy (a, "0"); if (i && radix > 1 && radix < 37) { char buf[35]; unsigned long u = i, p = 34; buf[p] = 0; if (i < 0 && radix == 10) { u = -i; } while (u) { unsigned int d = u % radix; buf[--p] = d < 10 ? '0' + d : 'a' + d - 10; u /= radix; } if (i < 0 && radix == 10) { buf[--p] = '-'; } strcpy (a, buf + p); } return a; } #if AZ_TRAIT_COMPILER_DEFINE_WCSICMP // For Linux it's redefined to wcscasecmp and wcsncasecmp' int wcsicmp (const wchar_t* s1, const wchar_t* s2) { wint_t c1, c2; if (s1 == s2) { return 0; } do { c1 = towlower(*s1++); c2 = towlower(*s2++); } while (c1 && c1 == c2); return (int) (c1 - c2); } int wcsnicmp (const wchar_t* s1, const wchar_t* s2, size_t count) { wint_t c1, c2; if (s1 == s2 || count == 0) { return 0; } do { c1 = towlower(*s1++); c2 = towlower(*s2++); } while ((--count) && c1 && (c1 == c2)); return (int) (c1 - c2); } #endif #if defined(ANDROID) // not defined in android-19 or prior size_t wcsnlen(const wchar_t* str, size_t maxLen) { size_t length; for (length = 0; length < maxLen; ++length, ++str) { if (!*str) { break; } } return length; } char* stpcpy(char* dest, const char* str) { while (*str != '\0') { *dest++ = *str++; } *dest = '\0'; return dest; } #endif void _makepath(char* path, const char* drive, const char* dir, const char* filename, const char* ext) { char ch; char tmp[MAX_PATH]; if (!path) { return; } tmp[0] = '\0'; if (drive && drive[0]) { tmp[0] = drive[0]; tmp[1] = ':'; tmp[2] = 0; } if (dir && dir[0]) { azstrcat(tmp, MAX_PATH, dir); ch = tmp[strlen(tmp) - 1]; if (ch != '/' && ch != '\\') { azstrcat(tmp, MAX_PATH, "\\"); } } if (filename && filename[0]) { azstrcat(tmp, MAX_PATH, filename); if (ext && ext[0]) { if (ext[0] != '.') { azstrcat(tmp, MAX_PATH, "."); } azstrcat(tmp, MAX_PATH, ext); } } azstrcpy(path, strlen(tmp) + 1, tmp); } char* _ui64toa(unsigned long long value, char* str, int radix) { if (str == nullptr) { return nullptr; } char buffer[65]; char* pos; int digit; pos = &buffer[64]; *pos = '\0'; do { digit = value % radix; value = value / radix; if (digit < 10) { *--pos = '0' + digit; } else { *--pos = 'a' + digit - 10; } /* if */ } while (value != 0L); memcpy(str, pos, &buffer[64] - pos + 1); return str; } long long _atoi64(const char* str) { if (str == nullptr) { return -1; } unsigned long long RunningTotal = 0; char bMinus = 0; while (*str == ' ' || (*str >= '\011' && *str <= '\015')) { str++; } /* while */ if (*str == '+') { str++; } else if (*str == '-') { bMinus = 1; str++; } /* if */ while (*str >= '0' && *str <= '9') { RunningTotal = RunningTotal * 10 + *str - '0'; str++; } /* while */ return bMinus ? ((long long)-RunningTotal) : (long long)RunningTotal; } #if defined(AZ_RESTRICTED_PLATFORM) #define AZ_RESTRICTED_SECTION WINBASE_CPP_SECTION_2 #include AZ_RESTRICTED_FILE(WinBase_cpp) #endif #if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED) #undef AZ_RESTRICTED_SECTION_IMPLEMENTED #else bool QueryPerformanceCounter(LARGE_INTEGER* counter) { #if defined(LINUX) // replaced gettimeofday // http://fixunix.com/kernel/378888-gettimeofday-resolution-linux.html timespec tv; clock_gettime(CLOCK_MONOTONIC, &tv); counter->QuadPart = (uint64)tv.tv_sec * 1000000 + tv.tv_nsec / 1000; return true; #elif defined(APPLE) counter->QuadPart = mach_absolute_time(); return true; #else return false; #endif } bool QueryPerformanceFrequency(LARGE_INTEGER* frequency) { #if defined(LINUX) // On Linux we'll use gettimeofday(). The API resolution is microseconds, // so we'll report that to the caller. frequency->u.LowPart = 1000000; frequency->u.HighPart = 0; return true; #elif defined(APPLE) static mach_timebase_info_data_t s_kTimeBaseInfoData; if (s_kTimeBaseInfoData.denom == 0) { mach_timebase_info(&s_kTimeBaseInfoData); } // mach_timebase_info_data_t expresses the tick period in nanoseconds frequency->QuadPart = 1e+9 * (uint64_t)s_kTimeBaseInfoData.denom / (uint64_t)s_kTimeBaseInfoData.numer; return true; #else return false; #endif } #endif void _splitpath(const char* inpath, char* drv, char* dir, char* fname, char* ext) { if (drv) { drv[0] = 0; } typedef AZStd::fixed_string path_stack_string; const path_stack_string inPath(inpath); AZStd::string::size_type s = inPath.rfind('/', inPath.size());//position of last / path_stack_string fName; if (s == AZStd::string::npos) { if (dir) { dir[0] = 0; } fName = inpath; //assign complete string as rest } else { if (dir) { azstrcpy(dir, AZ_MAX_PATH_LEN, (inPath.substr((AZStd::string::size_type)0, (AZStd::string::size_type)(s + 1))).c_str()); //assign directory } fName = inPath.substr((AZStd::string::size_type)(s + 1)); //assign remaining string as rest } if (fName.size() == 0) { if (ext) { ext[0] = 0; } if (fname) { fname[0] = 0; } } else { //dir and drive are now set s = fName.find(".", (AZStd::string::size_type)0);//position of first . if (s == AZStd::string::npos) { if (ext) { ext[0] = 0; } if (fname) { azstrcpy(fname, fName.size() + 1, fName.c_str()); //assign filename } } else { if (ext) { azstrcpy(ext, AZ_MAX_PATH_LEN, (fName.substr(s)).c_str()); //assign extension including . } if (fname) { if (s == 0) { fname[0] = 0; } else { azstrcpy(fname, AZ_MAX_PATH_LEN, (fName.substr((AZStd::string::size_type)0, s)).c_str()); //assign filename } } } } } //-----------------------------------------other stuff------------------------------------------------------------------- void GlobalMemoryStatus(LPMEMORYSTATUS lpmem) { //not complete implementation #if defined(AZ_RESTRICTED_PLATFORM) #define AZ_RESTRICTED_SECTION WINBASE_CPP_SECTION_3 #include AZ_RESTRICTED_FILE(WinBase_cpp) #endif #if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED) #undef AZ_RESTRICTED_SECTION_IMPLEMENTED #elif defined(APPLE) // Retrieve dwTotalPhys int kMIB[] = {CTL_HW, HW_MEMSIZE}; uint64_t dwTotalPhys; size_t ulength = sizeof(dwTotalPhys); if (sysctl(kMIB, 2, &dwTotalPhys, &ulength, NULL, 0) != 0) { gEnv->pLog->LogError("sysctl failed\n"); } else { lpmem->dwTotalPhys = static_cast(dwTotalPhys); } // Get the page size mach_port_t kHost(mach_host_self()); vm_size_t uPageSize; if (host_page_size(kHost, &uPageSize) != 0) { gEnv->pLog->LogError("host_page_size failed\n"); } else { // Get memory statistics vm_statistics_data_t kVMStats; mach_msg_type_number_t uCount(sizeof(kVMStats) / sizeof(natural_t)); if (host_statistics(kHost, HOST_VM_INFO, (host_info_t)&kVMStats, &uCount) != 0) { gEnv->pLog->LogError("host_statistics failed\n"); } else { // Calculate dwAvailPhys lpmem->dwAvailPhys = uPageSize * kVMStats.free_count; } } #else FILE* f; lpmem->dwMemoryLoad = 0; lpmem->dwTotalPhys = 16 * 1024 * 1024; lpmem->dwAvailPhys = 16 * 1024 * 1024; lpmem->dwTotalPageFile = 16 * 1024 * 1024; lpmem->dwAvailPageFile = 16 * 1024 * 1024; azfopen(&f, "/proc/meminfo", "r"); if (f) { char buffer[256]; memset(buffer, '0', 256); int total, used, free, shared, buffers, cached; lpmem->dwLength = sizeof(MEMORYSTATUS); lpmem->dwTotalPhys = lpmem->dwAvailPhys = 0; lpmem->dwTotalPageFile = lpmem->dwAvailPageFile = 0; while (fgets(buffer, sizeof(buffer), f)) { if (azsscanf(buffer, "Mem: %d %d %d %d %d %d", &total, &used, &free, &shared, &buffers, &cached)) { lpmem->dwTotalPhys += total; lpmem->dwAvailPhys += free + buffers + cached; } if (azsscanf(buffer, "Swap: %d %d %d", &total, &used, &free)) { lpmem->dwTotalPageFile += total; lpmem->dwAvailPageFile += free; } if (azsscanf(buffer, "MemTotal: %d", &total)) { lpmem->dwTotalPhys = total * 1024; } if (azsscanf(buffer, "MemFree: %d", &free)) { lpmem->dwAvailPhys = free * 1024; } if (azsscanf(buffer, "SwapTotal: %d", &total)) { lpmem->dwTotalPageFile = total * 1024; } if (azsscanf(buffer, "SwapFree: %d", &free)) { lpmem->dwAvailPageFile = free * 1024; } if (azsscanf(buffer, "Buffers: %d", &buffers)) { lpmem->dwAvailPhys += buffers * 1024; } if (azsscanf(buffer, "Cached: %d", &cached)) { lpmem->dwAvailPhys += cached * 1024; } } fclose(f); if (lpmem->dwTotalPhys) { DWORD TotalPhysical = lpmem->dwTotalPhys + lpmem->dwTotalPageFile; DWORD AvailPhysical = lpmem->dwAvailPhys + lpmem->dwAvailPageFile; lpmem->dwMemoryLoad = (TotalPhysical - AvailPhysical) / (TotalPhysical / 100); } } #endif } static const int YearLengths[2] = {DAYSPERNORMALYEAR, DAYSPERLEAPYEAR}; static const int MonthLengths[2][MONSPERYEAR] = { { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } }; static int IsLeapYear(int Year) { return Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0) ? 1 : 0; } static void NormalizeTimeFields(short* FieldToNormalize, short* CarryField, int Modulus) { *FieldToNormalize = (short) (*FieldToNormalize - Modulus); *CarryField = (short) (*CarryField + 1); } static bool TimeFieldsToTime(PTIME_FIELDS tfTimeFields, PLARGE_INTEGER Time) { #define SECSPERMIN 60 #define MINSPERHOUR 60 #define HOURSPERDAY 24 #define MONSPERYEAR 12 #define EPOCHYEAR 1601 #define SECSPERDAY 86400 #define TICKSPERMSEC 10000 #define TICKSPERSEC 10000000 #define SECSPERHOUR 3600 int CurYear, CurMonth; LONGLONG rcTime; TIME_FIELDS TimeFields = *tfTimeFields; rcTime = 0; while (TimeFields.Second >= SECSPERMIN) { NormalizeTimeFields(&TimeFields.Second, &TimeFields.Minute, SECSPERMIN); } while (TimeFields.Minute >= MINSPERHOUR) { NormalizeTimeFields(&TimeFields.Minute, &TimeFields.Hour, MINSPERHOUR); } while (TimeFields.Hour >= HOURSPERDAY) { NormalizeTimeFields(&TimeFields.Hour, &TimeFields.Day, HOURSPERDAY); } while (TimeFields.Day > MonthLengths[IsLeapYear(TimeFields.Year)][TimeFields.Month - 1]) { NormalizeTimeFields(&TimeFields.Day, &TimeFields.Month, SECSPERMIN); } while (TimeFields.Month > MONSPERYEAR) { NormalizeTimeFields(&TimeFields.Month, &TimeFields.Year, MONSPERYEAR); } for (CurYear = EPOCHYEAR; CurYear < TimeFields.Year; CurYear++) { rcTime += YearLengths[IsLeapYear(CurYear)]; } for (CurMonth = 1; CurMonth < TimeFields.Month; CurMonth++) { rcTime += MonthLengths[IsLeapYear(CurYear)][CurMonth - 1]; } rcTime += TimeFields.Day - 1; rcTime *= SECSPERDAY; rcTime += TimeFields.Hour * SECSPERHOUR + TimeFields.Minute * SECSPERMIN + TimeFields.Second; rcTime *= TICKSPERSEC; rcTime += TimeFields.Milliseconds * TICKSPERMSEC; Time->QuadPart = rcTime; return true; } BOOL SystemTimeToFileTime(const SYSTEMTIME* syst, LPFILETIME ft) { TIME_FIELDS tf; LARGE_INTEGER t; tf.Year = syst->wYear; tf.Month = syst->wMonth; tf.Day = syst->wDay; tf.Hour = syst->wHour; tf.Minute = syst->wMinute; tf.Second = syst->wSecond; tf.Milliseconds = syst->wMilliseconds; TimeFieldsToTime(&tf, &t); ft->dwLowDateTime = t.u.LowPart; ft->dwHighDateTime = t.u.HighPart; return TRUE; } ////////////////////////////////////////////////////////////////////////// #if defined(AZ_RESTRICTED_PLATFORM) #define AZ_RESTRICTED_SECTION WINBASE_CPP_SECTION_4 #include AZ_RESTRICTED_FILE(WinBase_cpp) #endif #if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED) #undef AZ_RESTRICTED_SECTION_IMPLEMENTED #else threadID GetCurrentThreadId() { return threadID(pthread_self()); } #endif #include #include ////////////////////////////////////////////////////////////////////////// DWORD Sleep(DWORD dwMilliseconds) { #if defined(LINUX) || defined(APPLE) std::this_thread::sleep_for(std::chrono::milliseconds(dwMilliseconds)); return 0; #define AZ_RESTRICTED_SECTION_IMPLEMENTED #elif defined(AZ_RESTRICTED_PLATFORM) #define AZ_RESTRICTED_SECTION WINBASE_CPP_SECTION_5 #include AZ_RESTRICTED_FILE(WinBase_cpp) #endif #if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED) #undef AZ_RESTRICTED_SECTION_IMPLEMENTED #else timeval tv, start, now; uint64 tStart; memset(&tv, 0, sizeof tv); memset(&start, 0, sizeof start); memset(&now, 0, sizeof now); gettimeofday(&now, NULL); start = now; tStart = (uint64)start.tv_sec * 1000000 + start.tv_usec; while (true) { uint64 tNow, timePassed, timeRemaining; tNow = (uint64)now.tv_sec * 1000000 + now.tv_usec; timePassed = tNow - tStart; if (timePassed >= dwMilliseconds) { break; } timeRemaining = dwMilliseconds * 1000 - timePassed; tv.tv_sec = timeRemaining / 1000000; tv.tv_usec = timeRemaining % 1000000; select(1, NULL, NULL, NULL, &tv); gettimeofday(&now, NULL); } return 0; #endif } #if defined(LINUX) || defined(APPLE) BOOL GetComputerName(LPSTR lpBuffer, LPDWORD lpnSize) { if (!lpBuffer || !lpnSize) { return FALSE; } int err = gethostname(lpBuffer, *lpnSize); if (-1 == err) { CryLog("GetComputerName falied [%d]\n", errno); return FALSE; } return TRUE; } #endif #if AZ_TRAIT_COMPILER_DEFINE_GETCURRENTPROCESSID DWORD GetCurrentProcessId(void) { return (DWORD)getpid(); } #endif ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// void CrySleep(unsigned int dwMilliseconds) { Sleep(dwMilliseconds); } ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// void CryMessageBox(const char* lpText, const char* lpCaption, [[maybe_unused]] unsigned int uType) { #ifdef WIN32 # error WIN32 is defined in WinBase.cpp (it is a non-Windows file) #elif defined(MAC) CFStringRef strText = CFStringCreateWithCString(NULL, lpText, kCFStringEncodingMacRoman); CFStringRef strCaption = CFStringCreateWithCString(NULL, lpCaption, kCFStringEncodingMacRoman); CFStringRef strOk = CFSTR("OK"); CFStringRef strCancel = CFSTR("Cancel"); CFStringRef strRetry = CFSTR("Retry"); CFStringRef strYes = CFSTR("Yes"); CFStringRef strNo = CFSTR("No"); CFStringRef strAbort = CFSTR("Abort"); CFStringRef strIgnore = CFSTR("Ignore"); CFStringRef strTryAgain = CFSTR("Try Again"); CFStringRef strContinue = CFSTR("Continue"); CFStringRef defaultButton = nullptr; CFStringRef alternativeButton = nullptr; CFStringRef otherButton = nullptr; switch (uType & 0xf) { case MB_OKCANCEL: defaultButton = strOk; alternativeButton = strCancel; break; case MB_ABORTRETRYIGNORE: defaultButton = strAbort; alternativeButton = strRetry; otherButton = strIgnore; break; case MB_YESNOCANCEL: defaultButton = strYes; alternativeButton = strNo; otherButton = strCancel; break; case MB_YESNO: defaultButton = strYes; alternativeButton = strNo; break; case MB_RETRYCANCEL: defaultButton = strRetry; alternativeButton = strCancel; break; case MB_CANCELTRYCONTINUE: defaultButton = strCancel; alternativeButton = strTryAgain; otherButton = strContinue; break; case MB_OK: default: defaultButton = strOk; break; } CFOptionFlags kResult; CFUserNotificationDisplayAlert( 0, // no timeout kCFUserNotificationNoteAlertLevel, //change it depending message_type flags ( MB_ICONASTERISK.... etc.) NULL, //icon url, use default, you can change it depending message_type flags NULL, //not used NULL, //localization of strings strText, //header text strCaption, //message text defaultButton, //default "ok" text in button alternativeButton, //alternate button title otherButton, //other button title, null--> no other button &kResult //response flags ); if (strCaption) { CFRelease(strCaption); } if (strText) { CFRelease(strText); } #else printf("Messagebox: cap: %s text:%s\n", lpCaption ? lpCaption : " ", lpText ? lpText : " "); #endif } #if defined(LINUX) || defined(APPLE) threadID CryGetCurrentThreadId() { return GetCurrentThreadId(); } #endif//LINUX APPLE #if defined(APPLE) || defined(LINUX) // WinAPI debug functions. AZ_DLL_EXPORT void OutputDebugString(const char* outputString) { #if !defined(_RELEASE) // Emulates dev tools output in Xcode and cmd line launch with idevicedebug. fprintf(stdout, "%s", outputString); #endif } #endif #endif // AZ_TRAIT_LEGACY_CRYCOMMON_USE_WINDOWS_STUBS