123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555 |
- /*
- * 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 "CrySystem_precompiled.h"
- #include "System.h"
- #include <time.h>
- #include <IRenderer.h>
- #include <IMovieSystem.h>
- #include <ILog.h>
- #include <AzCore/Debug/StackTracer.h>
- #include <AzCore/IO/SystemFile.h> // for AZ_MAX_PATH_LEN
- #include <AzCore/std/allocator_stack.h>
- #if defined(AZ_RESTRICTED_PLATFORM)
- #undef AZ_RESTRICTED_SECTION
- #define SYSTEMWIN32_CPP_SECTION_1 1
- #define SYSTEMWIN32_CPP_SECTION_2 2
- #define SYSTEMWIN32_CPP_SECTION_3 3
- #endif
- #if defined(LINUX) || defined(APPLE)
- #include <unistd.h>
- #endif
- #ifdef WIN32
- #define WIN32_LEAN_AND_MEAN
- #include "windows.h"
- #include <float.h>
- #include <shellapi.h> // Needed for ShellExecute.
- #include <Psapi.h>
- #include <Aclapi.h>
- #include <shlobj.h>
- #endif
- #include "IDebugCallStack.h"
- #if defined(APPLE) || defined(LINUX)
- #include <pwd.h>
- #endif
- #include "XConsole.h"
- #include "LocalizedStringManager.h"
- #include "XML/XmlUtils.h"
- #if defined(WIN32)
- __pragma(comment(lib, "wininet.lib"))
- __pragma(comment(lib, "Winmm.lib"))
- #endif
- #if defined(APPLE)
- #include <AzCore/Utils/SystemUtilsApple_Platform.h>
- #endif
- // this is the list of modules that can be loaded into the game process
- // Each array element contains 2 strings: the name of the module (case-insensitive)
- // and the name of the group the module belongs to
- //////////////////////////////////////////////////////////////////////////
- const char g_szGroupCore[] = "CryEngine";
- const char* g_szModuleGroups[][2] = {
- {"Editor.exe", g_szGroupCore},
- {"CrySystem.dll", g_szGroupCore}
- };
- #if defined(WIN32)
- #pragma pack(push,1)
- struct PEHeader_DLL
- {
- DWORD signature;
- IMAGE_FILE_HEADER _head;
- IMAGE_OPTIONAL_HEADER opt_head;
- IMAGE_SECTION_HEADER* section_header; // actual number in NumberOfSections
- };
- #pragma pack(pop)
- #endif
- //////////////////////////////////////////////////////////////////////////
- const char* CSystem::GetUserName()
- {
- #if defined(WIN32) || defined(WIN64)
- static const int iNameBufferSize = 1024;
- static char szNameBuffer[iNameBufferSize];
- memset(szNameBuffer, 0, iNameBufferSize);
- DWORD dwSize = iNameBufferSize;
- wchar_t nameW[iNameBufferSize];
- ::GetUserNameW(nameW, &dwSize);
- AZStd::to_string(szNameBuffer, iNameBufferSize, { nameW, dwSize });
- return szNameBuffer;
- #else
- #if defined(LINUX)
- static uid_t uid = geteuid ();
- static struct passwd* pw = getpwuid (uid);
- if (pw)
- {
- return (pw->pw_name);
- }
- else
- {
- return NULL;
- }
- #elif defined(APPLE)
- static const int iNameBufferSize = 1024;
- static char szNameBuffer[iNameBufferSize];
- if (auto resultOutcome = AZ::SystemUtilsApple::GetUserName(AZStd::span(szNameBuffer));
- resultOutcome)
- {
- return szNameBuffer;
- }
- else
- {
- return "";
- }
- #else
- return "";
- #endif
- #endif
- }
- //////////////////////////////////////////////////////////////////////////
- int CSystem::GetApplicationInstance()
- {
- #ifdef WIN32
- // tools that declare themselves as in "tool mode" may not access @user@ and may also not lock it
- if (gEnv->IsInToolMode())
- {
- return 0;
- }
- // this code below essentially "locks" an instance of the USER folder to a specific running application
- if (m_iApplicationInstance == -1)
- {
- AZStd::wstring suffix;
- for (int instance = 0;; ++instance)
- {
- suffix = AZStd::wstring::format(L"O3DEApplication(%d)", instance);
- CreateMutexW(NULL, TRUE, suffix.c_str());
- // search for duplicates
- if (GetLastError() != ERROR_ALREADY_EXISTS)
- {
- m_iApplicationInstance = instance;
- break;
- }
- }
- }
- return m_iApplicationInstance;
- #else
- return 0;
- #endif
- }
- int CSystem::GetApplicationLogInstance([[maybe_unused]] const char* logFilePath)
- {
- #if AZ_TRAIT_OS_USE_WINDOWS_MUTEX
- AZStd::wstring suffix;
- int instance = 0;
- for (;; ++instance)
- {
- suffix = AZStd::wstring::format(L"%s(%d)", logFilePath, instance);
- CreateMutexW(NULL, TRUE, suffix.c_str());
- if (GetLastError() != ERROR_ALREADY_EXISTS)
- {
- break;
- }
- }
- return instance;
- #else
- return 0;
- #endif
- }
- //////////////////////////////////////////////////////////////////////////
- struct CryDbgModule
- {
- HANDLE heap;
- WIN_HMODULE handle;
- AZStd::string name;
- DWORD dwSize;
- };
- #ifdef WIN32
- //////////////////////////////////////////////////////////////////////////
- const char* GetModuleGroup (const char* szString)
- {
- for (unsigned i = 0; i < sizeof(g_szModuleGroups) / sizeof(g_szModuleGroups[0]); ++i)
- {
- if (azstricmp(szString, g_szModuleGroups[i][0]) == 0)
- {
- return g_szModuleGroups[i][1];
- }
- }
- return "Other";
- }
- #endif
- // Make system error message string
- //////////////////////////////////////////////////////////////////////////
- //! \return pointer to the null terminated error string or 0
- static const char* GetLastSystemErrorMessage()
- {
- #ifdef WIN32
- DWORD dwError = GetLastError();
- static char szBuffer[512]; // function will return pointer to this buffer
- if (dwError)
- {
- LPVOID lpMsgBuf = 0;
- if (FormatMessage(
- FORMAT_MESSAGE_ALLOCATE_BUFFER |
- FORMAT_MESSAGE_FROM_SYSTEM |
- FORMAT_MESSAGE_IGNORE_INSERTS,
- NULL,
- GetLastError(),
- MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
- (LPTSTR) &lpMsgBuf,
- 0,
- NULL))
- {
- azstrcpy(szBuffer, AZ_ARRAY_SIZE(szBuffer), (char*)lpMsgBuf);
- LocalFree(lpMsgBuf);
- }
- else
- {
- return 0;
- }
- return szBuffer;
- }
- #endif //WIN32
- return 0;
- }
- //////////////////////////////////////////////////////////////////////////
- void CSystem::FatalError(const char* format, ...)
- {
- // Guard against reentrancy - out of memory fatal errors can become reentrant since logging can try to alloc.
- static bool currentlyReportingError = false;
- if (currentlyReportingError == true)
- {
- return;
- }
- currentlyReportingError = true;
- // format message
- va_list ArgList;
- char szBuffer[MAX_WARNING_LENGTH];
- const char* sPrefix = "";
- azstrcpy(szBuffer, MAX_WARNING_LENGTH, sPrefix);
- va_start(ArgList, format);
- azvsnprintf(szBuffer + strlen(sPrefix), MAX_WARNING_LENGTH - strlen(sPrefix), format, ArgList);
- va_end(ArgList);
- // get system error message before any attempt to write into log
- const char* szSysErrorMessage = GetLastSystemErrorMessage();
- CryLogAlways("=============================================================================");
- CryLogAlways("*ERROR");
- CryLogAlways("=============================================================================");
- // write both messages into log
- CryLogAlways("%s", szBuffer);
- if (szSysErrorMessage)
- {
- CryLogAlways("Last System Error: %s", szSysErrorMessage);
- }
- if (GetUserCallback())
- {
- GetUserCallback()->OnError(szBuffer);
- }
- assert(szBuffer[0] >= ' ');
- // strcpy(szBuffer,szBuffer+1); // remove verbosity tag since it is not supported by ::MessageBox
- AZ::Debug::Platform::OutputToDebugger("CrySystem", szBuffer);
- #ifdef WIN32
- OnFatalError(szBuffer);
- if (!g_cvars.sys_no_crash_dialog)
- {
- AZStd::wstring szBufferW;
- AZStd::to_wstring(szBufferW, szBuffer);
- ::MessageBoxW(NULL, szBufferW.c_str(), L"Open 3D Engine Error", MB_OK | MB_ICONERROR | MB_SYSTEMMODAL);
- }
- // Dump callstack.
- IDebugCallStack::instance()->FatalError(szBuffer);
- #endif
- // app can not continue
- AZ::Debug::Trace::Instance().Break();
- #ifdef _DEBUG
- #if defined(WIN32) || defined(WIN64)
- _flushall();
- // on windows, _exit does all sorts of things which can cause cleanup to fail during a crash, we need to terminate instead.
- TerminateProcess(GetCurrentProcess(), 1);
- #endif
- #if defined(AZ_RESTRICTED_PLATFORM)
- #define AZ_RESTRICTED_SECTION SYSTEMWIN32_CPP_SECTION_2
- #include AZ_RESTRICTED_FILE(SystemWin32_cpp)
- #endif
- #if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED)
- #undef AZ_RESTRICTED_SECTION_IMPLEMENTED
- #else
- _exit(1);
- #endif
- #endif
- }
- void CSystem::ReportBug([[maybe_unused]] const char* format, ...)
- {
- #if defined (WIN32)
- va_list ArgList;
- char szBuffer[MAX_WARNING_LENGTH];
- const char* sPrefix = "";
- azstrcpy(szBuffer, MAX_WARNING_LENGTH, sPrefix);
- va_start(ArgList, format);
- azvsnprintf(szBuffer + strlen(sPrefix), MAX_WARNING_LENGTH - strlen(sPrefix), format, ArgList);
- va_end(ArgList);
- IDebugCallStack::instance()->ReportBug(szBuffer);
- #endif
- }
- //////////////////////////////////////////////////////////////////////////
- void CSystem::debug_GetCallStack(const char** pFunctions, int& nCount)
- {
- #if defined(WIN32)
- using namespace AZ::Debug;
- int nMaxCount = nCount;
- StackFrame* frames = (StackFrame*)AZ_ALLOCA(sizeof(StackFrame)*nMaxCount);
- unsigned int numFrames = StackRecorder::Record(frames, nMaxCount, 1);
- SymbolStorage::StackLine* textLines = (SymbolStorage::StackLine*)AZ_ALLOCA(sizeof(SymbolStorage::StackLine)*nMaxCount);
- SymbolStorage::DecodeFrames(frames, numFrames, textLines);
- for (unsigned int i = 0; i < numFrames; i++)
- {
- pFunctions[i] = textLines[i];
- }
- nCount = numFrames;
- #define AZ_RESTRICTED_SECTION_IMPLEMENTED
- #elif defined(AZ_RESTRICTED_PLATFORM)
- #define AZ_RESTRICTED_SECTION SYSTEMWIN32_CPP_SECTION_3
- #include AZ_RESTRICTED_FILE(SystemWin32_cpp)
- #endif
- #if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED)
- #undef AZ_RESTRICTED_SECTION_IMPLEMENTED
- #else
- AZ_UNUSED(pFunctions);
- nCount = 0;
- #endif
- }
- //////////////////////////////////////////////////////////////////////////
- void CSystem::debug_LogCallStack(int nMaxFuncs, [[maybe_unused]] int nFlags)
- {
- if (nMaxFuncs > 32)
- {
- nMaxFuncs = 32;
- }
- // Print call stack for each find.
- const char* funcs[32];
- int nCount = nMaxFuncs;
- GetISystem()->debug_GetCallStack(funcs, nCount);
- for (int i = 1; i < nCount; i++) // start from 1 to skip this function.
- {
- CryLogAlways(" %02d) %s", i, funcs[i]);
- }
- }
- #if (defined(WIN32) || defined(WIN64))
- //////////////////////////////////////////////////////////////////////////
- bool CSystem::GetWinGameFolder(char* szMyDocumentsPath, int maxPathSize)
- {
- bool bSucceeded = false;
- // check Vista and later OS first
- HMODULE shell32 = LoadLibraryW(L"Shell32.dll");
- if (shell32)
- {
- typedef long (__stdcall * T_SHGetKnownFolderPath)(REFKNOWNFOLDERID rfid, unsigned long dwFlags, void* hToken, wchar_t** ppszPath);
- T_SHGetKnownFolderPath SHGetKnownFolderPath = (T_SHGetKnownFolderPath)GetProcAddress(shell32, "SHGetKnownFolderPath");
- if (SHGetKnownFolderPath)
- {
- // We must be running Vista or newer
- wchar_t* wMyDocumentsPath;
- HRESULT hr = SHGetKnownFolderPath(FOLDERID_SavedGames, KF_FLAG_CREATE | KF_FLAG_DONT_UNEXPAND, NULL, &wMyDocumentsPath);
- bSucceeded = SUCCEEDED(hr);
- if (bSucceeded)
- {
- // Convert from UNICODE to UTF-8
- AZStd::to_string(szMyDocumentsPath, maxPathSize, wMyDocumentsPath);
- CoTaskMemFree(wMyDocumentsPath);
- }
- }
- FreeLibrary(shell32);
- }
- if (!bSucceeded)
- {
- // check pre-vista OS if not succeeded before
- wchar_t wMyDocumentsPath[AZ_MAX_PATH_LEN];
- bSucceeded = SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_PERSONAL | CSIDL_FLAG_CREATE, NULL, 0, wMyDocumentsPath));
- if (bSucceeded)
- {
- AZStd::to_string(szMyDocumentsPath, maxPathSize, wMyDocumentsPath);
- }
- }
- return bSucceeded;
- }
- #endif
- //////////////////////////////////////////////////////////////////////////
- void CSystem::DetectGameFolderAccessRights()
- {
- // This code is trying to figure out if the current folder we are now running under have write access.
- // By default assume folder is not writable.
- // If folder is writable game.log is saved there, otherwise it is saved in user documents folder.
- #if defined(WIN32)
- DWORD DesiredAccess = FILE_GENERIC_WRITE;
- DWORD GrantedAccess = 0;
- DWORD dwRes = 0;
- PACL pDACL = NULL;
- PSECURITY_DESCRIPTOR pSD = NULL;
- HANDLE hClientToken = 0;
- PRIVILEGE_SET PrivilegeSet;
- DWORD PrivilegeSetLength = sizeof(PrivilegeSet);
- BOOL bAccessStatus = FALSE;
- // Get a pointer to the existing DACL.
- dwRes = GetNamedSecurityInfoW(L".", SE_FILE_OBJECT,
- DACL_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION,
- NULL, NULL, &pDACL, NULL, &pSD);
- if (ERROR_SUCCESS != dwRes)
- {
- //
- assert(0);
- }
- if (!ImpersonateSelf(SecurityIdentification))
- {
- return;
- }
- if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &hClientToken) && hClientToken != 0)
- {
- return;
- }
- GENERIC_MAPPING GenMap;
- GenMap.GenericRead = FILE_GENERIC_READ;
- GenMap.GenericWrite = FILE_GENERIC_WRITE;
- GenMap.GenericExecute = FILE_GENERIC_EXECUTE;
- GenMap.GenericAll = FILE_ALL_ACCESS;
- MapGenericMask(&DesiredAccess, &GenMap);
- if (!AccessCheck(pSD, hClientToken, DesiredAccess, &GenMap, &PrivilegeSet, &PrivilegeSetLength, &GrantedAccess, &bAccessStatus))
- {
- RevertToSelf();
- CloseHandle(hClientToken);
- return;
- }
- CloseHandle(hClientToken);
- RevertToSelf();
- if (bAccessStatus)
- {
- m_bGameFolderWritable = true;
- }
- #elif defined(MOBILE)
- char cwd[AZ_MAX_PATH_LEN];
- if (getcwd(cwd, AZ_MAX_PATH_LEN) != NULL)
- {
- if (0 == access(cwd, W_OK))
- {
- m_bGameFolderWritable = true;
- }
- }
- #endif //WIN32
- }
- /////////////////////////////////`/////////////////////////////////////////
- void CSystem::EnableFloatExceptions([[maybe_unused]] int type)
- {
- #ifndef _RELEASE
- #if defined(WIN32)
- #if defined(WIN32) && !defined(WIN64)
- // Optimization
- // Enable DAZ/FZ
- // Denormals Are Zeros
- // Flush-to-Zero
- _controlfp(_DN_FLUSH, _MCW_DN);
- #endif //#if defined(WIN32) && !defined(WIN64)
- AZ_PUSH_DISABLE_WARNING(4996, "-Wunknown-warning-option")
- _controlfp(_DN_FLUSH, _MCW_DN);
- if (type == 0)
- {
- // mask all floating exceptions off.
- _controlfp(_EM_INEXACT | _EM_UNDERFLOW | _EM_OVERFLOW | _EM_INVALID | _EM_DENORMAL | _EM_ZERODIVIDE, _MCW_EM);
- }
- else
- {
- // Clear pending exceptions
- _fpreset();
- if (type == 1)
- {
- // enable just the most important fp-exceptions.
- _controlfp(_EM_INEXACT | _EM_UNDERFLOW | _EM_OVERFLOW, _MCW_EM); // Enable floating point exceptions.
- }
- if (type == 2)
- {
- // enable ALL floating point exceptions.
- _controlfp(_EM_INEXACT, _MCW_EM);
- }
- }
- AZ_POP_DISABLE_WARNING
- #endif //#if defined(WIN32) && !defined(WIN64)
- #ifdef WIN32
- _mm_setcsr(_mm_getcsr() & ~0x280 | (type > 0 ? 0 : 0x280));
- #endif
- #endif //_RELEASE
- }
|