text: /*
  * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
  * its licensors.
  *
  * For complete copyright and license terms please see the LICENSE at the root of this
  * distribution (the "License"). All use of this software is governed by the License,
  * or, if provided, by the license below or the license accompanying this file. Do not
  * remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  *
  */
  // Original file Copyright Crytek GMBH or its affiliates, used under license.
  
  #include "StdAfx.h"
  #include "System.h"
  #include <time.h>
  
  #include <INetwork.h>
  #include <I3DEngine.h>
  #include <IAISystem.h>
  #include <IRenderer.h>
  #include <ICryPak.h>
  #include <IMovieSystem.h>
  #include <IEntitySystem.h>
  #include <IInput.h>
  #include <ILog.h>
  #include <IMusicSystem.h>
  #include <IScriptSystem.h>
  #include <ICryAnimation.h>
  #include <CryLibrary.h>
  #include <IGame.h>
  #include <IGameFramework.h>
  #include <IPlatformOS.h>
  #include <StringUtils.h>
  #include <AzCore/IO/SystemFile.h> // for AZ_MAX_PATH_LEN
  
  #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>
  
  // HAX to resolve IEntity redefinition
  #define __IEntity_INTERFACE_DEFINED__
  #include <shlobj.h>
  #endif
  
  #include "IDebugCallStack.h"
  
  #if defined(APPLE) || defined(LINUX)
  #include <pwd.h>
  #endif
  
  #include "XConsole.h"
  #include "Statistics.h"
  #include "CrySizerStats.h"
  #include "CrySizerImpl.h"
  #include "StreamEngine/StreamEngine.h"
  #include "LocalizedStringManager.h"
  #include "XML/XmlUtils.h"
  #include "AutoDetectSpec.h"
  #include "CryPak.h"
  
  #pragma warning(disable: 4244)
  
  #if defined(WIN32)
  LINK_SYSTEM_LIBRARY(wininet.lib)
  LINK_SYSTEM_LIBRARY(Winmm.lib)
  #endif
  
  #if defined(ANDROID)
      #include <SDL.h>
  #endif
  
  #if defined(APPLE)
  #include "SystemUtilsApple.h"
  #endif
  
  extern CMTSafeHeap* g_pPakHeap;
  
  // 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},
      {"CryScriptSystem.dll", g_szGroupCore},
      {"CryNetwork.dll", g_szGroupCore},
      {"CryPhysics.dll", g_szGroupCore},
      {"CryInput.dll", g_szGroupCore},
      {"CrySoundSystem.dll", g_szGroupCore},
  #ifdef WIN64
      {"CrySound64.dll", g_szGroupCore},
  #else
      {"CrySound.dll", g_szGroupCore},
  #endif
      {"CryFont.dll", g_szGroupCore},
      {"CryAISystem.dll", g_szGroupCore},
      {"CryEntitySystem.dll", g_szGroupCore},
      {"Cry3DEngine.dll", g_szGroupCore},
      //{"Game.dll", g_szGroupCore}, // you may need to change this to your own gamedll name.
      {"CryAction.dll", g_szGroupCore},
      {"CryAnimation.dll", g_szGroupCore},
      {"CryRenderD3D9.dll", g_szGroupCore},
      {"CryRenderD3D10.dll", g_szGroupCore},
      {"CryRenderOGL.dll", g_szGroupCore},
      {"CryRenderNULL.dll", g_szGroupCore}
  };
  
  //////////////////////////////////////////////////////////////////////////
  void CSystem::SetAffinity()
  {
      // the following code is only for Windows
  #ifdef WIN32
      // set the process affinity
      ICVar* pcvAffinityMask = GetIConsole()->GetCVar("sys_affinity");
      if (!pcvAffinityMask)
      {
          pcvAffinityMask = REGISTER_INT("sys_affinity", 0, VF_NULL, "");
      }
  
      if (pcvAffinityMask)
      {
          unsigned nAffinity = pcvAffinityMask->GetIVal();
          if (nAffinity)
          {
              typedef BOOL (WINAPI * FnSetProcessAffinityMask)(IN HANDLE hProcess, IN DWORD_PTR dwProcessAffinityMask);
              HMODULE hKernel = CryLoadLibrary ("kernel32.dll");
              if (hKernel)
              {
                  FnSetProcessAffinityMask SetProcessAffinityMask = (FnSetProcessAffinityMask)GetProcAddress(hKernel, "SetProcessAffinityMask");
                  if (SetProcessAffinityMask && !SetProcessAffinityMask(GetCurrentProcess(), nAffinity))
                  {
                      GetILog()->LogError("Error: Cannot set affinity mask %d, error code %d", nAffinity, GetLastError());
                  }
                  FreeLibrary (hKernel);
              }
          }
      }
  #endif
  }
  
  
  
  
  
  //! dumps the memory usage statistics to the log
  //////////////////////////////////////////////////////////////////////////
  void CSystem::DumpMemoryUsageStatistics(bool bUseKB)
  {
      //  CResourceCollector ResourceCollector;
  
      //  TickMemStats(nMSP_ForDump,&ResourceCollector);
      TickMemStats(nMSP_ForDump);
  
      CrySizerStatsRenderer StatsRenderer (this, m_pMemStats, 10, 0);
      StatsRenderer.dump(bUseKB);
  
      int iSizeInM = m_env.p3DEngine->GetTerrainSize();
  
      //  ResourceCollector.ComputeDependencyCnt();
  
      //  int iTerrainSectorSize = m_env.p3DEngine->GetTerrainSectorSize();
  
      //  if(iTerrainSectorSize)
      //      ResourceCollector.LogData(*GetILog(),AABB(Vec3(0,0,0),Vec3(iSizeInM,iSizeInM,0)),iSizeInM/iTerrainSectorSize);
  
      // since we've recalculated this mem stats for dumping, we'll want to calculate it anew the next time it's rendered
      SAFE_DELETE(m_pMemStats);
  }
  
  // collects the whole memory statistics into the given sizer object
  //////////////////////////////////////////////////////////////////////////
  
  
  #if defined(WIN32)
  #pragma pack(push,1)
  const 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 SmallModuleInfo* FindModuleInfo(std::vector<SmallModuleInfo>& vec, const char* name)
  {
      for (size_t i = 0; i < vec.size(); ++i)
      {
          if (!vec[i].name.compareNoCase(name))
          {
              return &vec[i];
          }
      }
  
      return 0;
  }
  
  //////////////////////////////////////////////////////////////////////////
  void CSystem::CollectMemInfo(SCryEngineStatsGlobalMemInfo& m_stats)
  {
      m_stats.totalUsedInModules = 0;
      m_stats.totalCodeAndStatic = 0;
      m_stats.countedMemoryModules = 0;
      m_stats.totalAllocatedInModules = 0;
      m_stats.totalNumAllocsInModules = 0;
  
      AZStd::vector<AZStd::string> szModules = GetModuleNames();
      const int numModules = szModules.size();
  
      //////////////////////////////////////////////////////////////////////////
      // Hardcoded value for the OS memory allocation.
      //////////////////////////////////////////////////////////////////////////
      for (int i = 0; i < numModules; i++)
      {
          const char* szModule = szModules[i].c_str();
  
          SCryEngineStatsModuleInfo moduleInfo;
          ZeroStruct(moduleInfo.memInfo);
          moduleInfo.moduleStaticSize = moduleInfo.SizeOfCode = moduleInfo.SizeOfInitializedData = moduleInfo.SizeOfUninitializedData = moduleInfo.usedInModule = 0;
          moduleInfo.name = szModule;
  
          if (!QueryModuleMemoryInfo(moduleInfo, i))
          {
              continue;
          }
  
          m_stats.totalNumAllocsInModules += moduleInfo.memInfo.num_allocations;
          m_stats.totalAllocatedInModules += moduleInfo.memInfo.allocated;
          m_stats.totalUsedInModules += moduleInfo.usedInModule;
          m_stats.countedMemoryModules++;
          m_stats.totalCodeAndStatic += moduleInfo.moduleStaticSize;
  
          m_stats.modules.push_back(moduleInfo);
      }
  }
  
  
  void CSystem::CollectMemStats (ICrySizer* pSizer, MemStatsPurposeEnum nPurpose, std::vector<SmallModuleInfo>* pStats)
  {
      std::vector<SmallModuleInfo> stats;
  #ifdef WIN32
      //////////////////////////////////////////////////////////////////////////
      AZStd::vector<AZStd::string> szModules = GetModuleNames();
      const int numModules = szModules.size();
  
      for (int i = 0; i < numModules; i++)
      {
          const char* szModule = szModules[i].c_str();
          HMODULE hModule = GetModuleHandle(szModule);
          if (!hModule)
          {
              continue;
          }
  
          //totalStatic += me.modBaseSize;
          typedef void (* PFN_MODULEMEMORY)(CryModuleMemoryInfo*);
          PFN_MODULEMEMORY fpCryModuleGetAllocatedMemory = (PFN_MODULEMEMORY)::GetProcAddress(hModule, "CryModuleGetMemoryInfo");
          if (!fpCryModuleGetAllocatedMemory)
          {
              continue;
          }
  
          PEHeader_DLL pe_header;
          PEHeader_DLL* header = &pe_header;
  
          const IMAGE_DOS_HEADER* dos_head = (IMAGE_DOS_HEADER*)hModule;
          if (dos_head->e_magic != IMAGE_DOS_SIGNATURE)
          {
              // Wrong pointer, not to PE header.
              continue;
          }
          header = (PEHeader_DLL*)(const void*)((char*)dos_head + dos_head->e_lfanew);
          //#endif
  
          SmallModuleInfo moduleInfo;
          moduleInfo.name = szModule;
          fpCryModuleGetAllocatedMemory(&moduleInfo.memInfo);
  
          if (nMSP_ForCrashLog == nPurpose)
          {
              int usedInModule = moduleInfo.memInfo.allocated - moduleInfo.memInfo.freed;
              int numAllocations = moduleInfo.memInfo.num_allocations;
              CryLogAlways("%s Used in module:%d Allocations:%d", szModule, usedInModule, numAllocations);
          }
          else
          {
              stats.push_back(moduleInfo);
          }
      }
  #endif
  
      if (pStats)
      {
          pStats->assign(stats.begin(), stats.end());
      }
  
      if (nMSP_ForCrashLog == nPurpose || nMSP_ForBudget == nPurpose)
      {
          return;
      }
  
      {
          SIZER_COMPONENT_NAME(pSizer, "CrySystem");
          {
              pSizer->AddObject(this, sizeof(*this));
              {
                  //SIZER_COMPONENT_NAME (pSizer, "$Allocations waste");
                  //const SmallModuleInfo* info = FindModuleInfo(stats, "CrySystem.dll");
                  //if (info)
                  //pSizer->AddObject(info, info->memInfo.allocated - info->memInfo.requested );
              }
  
  #ifndef AZ_MONOLITHIC_BUILD // Only when compiling as dynamic library
              {
                  //SIZER_COMPONENT_NAME(pSizer,"Strings");
                  //pSizer->AddObject( (this+1),string::_usedMemory(0) );
              }
              {
                  SIZER_COMPONENT_NAME(pSizer, "STL Allocator Waste");
                  CryModuleMemoryInfo meminfo;
                  ZeroStruct(meminfo);
                  CryGetMemoryInfoForModule(&meminfo);
                  pSizer->AddObject((this + 2), meminfo.STL_wasted);
              }
  #endif // AZ_MONOLITHIC_BUILD
  
              {
                  SIZER_COMPONENT_NAME(pSizer, "VFS");
                  if (m_pStreamEngine)
                  {
                      SIZER_COMPONENT_NAME(pSizer, "Stream Engine");
                      m_pStreamEngine->GetMemoryStatistics(pSizer);
                  }
                  if (m_env.pCryPak)
                  {
                      SIZER_COMPONENT_NAME(pSizer, "CryPak");
                      ((CCryPak*)m_env.pCryPak)->GetMemoryStatistics(pSizer);
                      g_pPakHeap->GetMemoryUsage(pSizer);
                  }
              }
              {
                  SIZER_COMPONENT_NAME(pSizer, "Localization Data");
                  m_pLocalizationManager->GetMemoryUsage(pSizer);
              }
              {
                  SIZER_COMPONENT_NAME(pSizer, "XML");
                  m_pXMLUtils->GetMemoryUsage(pSizer);
              }
              if (m_env.pConsole)
              {
                  SIZER_COMPONENT_NAME (pSizer, "Console");
                  m_env.pConsole->GetMemoryUsage (pSizer);
              }
  
              if (m_env.pLog)
              {
                  SIZER_COMPONENT_NAME (pSizer, "Log");
                  m_env.pLog->GetMemoryUsage(pSizer);
              }
          }
          if (m_pPlatformOS.get())
          {
              SIZER_COMPONENT_NAME(pSizer, "PlatformOS");
              m_pPlatformOS->GetMemoryUsage(pSizer);
          }
      }
  
      if (m_env.pCharacterManager)
      {
          SIZER_COMPONENT_NAME(pSizer, "CryAnimation");
          {
              {
                  SIZER_COMPONENT_NAME (pSizer, "$Allocations waste");
                  const SmallModuleInfo* info = FindModuleInfo(stats, "CryAnimation.dll");
                  if (info)
                  {
                      pSizer->AddObject(info, info->memInfo.allocated - info->memInfo.requested);
                  }
              }
  
              m_env.pCharacterManager->GetMemoryUsage(pSizer);
          }
      }
  
      if (m_env.pPhysicalWorld)
      {
          SIZER_COMPONENT_NAME(pSizer, "CryPhysics");
          {
              {
                  SIZER_COMPONENT_NAME (pSizer, "$Allocations waste");
                  const SmallModuleInfo* info = FindModuleInfo(stats, "CryPhysics.dll");
                  if (info)
                  {
                      pSizer->AddObject(info, info->memInfo.allocated - info->memInfo.requested);
                  }
              }
              m_env.pPhysicalWorld->GetMemoryStatistics (pSizer);
          }
      }
  
      if (m_env.p3DEngine)
      {
          SIZER_COMPONENT_NAME(pSizer, "Cry3DEngine");
          {
              m_env.p3DEngine->GetMemoryUsage (pSizer);
              {
                  SIZER_COMPONENT_NAME (pSizer, "$Allocations waste");
                  const SmallModuleInfo* info = FindModuleInfo(stats, "Cry3DEngine.dll");
                  if (info)
                  {
                      pSizer->AddObject(info, info->memInfo.allocated - info->memInfo.requested);
                  }
              }
          }
      }
  
      if (m_env.pRenderer)
      {
          SIZER_COMPONENT_NAME(pSizer, "CryRenderer");
          {
              {
                  SIZER_COMPONENT_NAME (pSizer, "$Allocations waste D3D9");
                  const SmallModuleInfo* info = FindModuleInfo(stats, "CryRenderD3D9.dll");
                  if (info)
                  {
                      pSizer->AddObject(info, info->memInfo.allocated - info->memInfo.requested);
                  }
              }
              {
                  SIZER_COMPONENT_NAME (pSizer, "$Allocations waste D3D10");
                  const SmallModuleInfo* info = FindModuleInfo(stats, "CryRenderD3D10.dll");
                  if (info)
                  {
                      pSizer->AddObject(info, info->memInfo.allocated - info->memInfo.requested);
                  }
              }
  
              m_env.pRenderer->GetMemoryUsage(pSizer);
          }
      }
  
      if (m_env.pCryFont)
      {
          SIZER_COMPONENT_NAME(pSizer, "CryFont");
          {
              {
                  SIZER_COMPONENT_NAME (pSizer, "$Allocations waste");
                  const SmallModuleInfo* info = FindModuleInfo(stats, "CryFont.dll");
                  if (info)
                  {
                      pSizer->AddObject(info, info->memInfo.allocated - info->memInfo.requested);
                  }
              }
  
              m_env.pCryFont->GetMemoryUsage(pSizer);
              // m_pIFont and m_pIFontUi are both counted in pCryFont sizing if they exist.
              // no need to manually add them here.
          }
      }
  
  	if (Audio::AudioSystemRequestBus::HasHandlers())
      {
          SIZER_COMPONENT_NAME(pSizer, "CrySoundSystem");
          {
              {
                  SIZER_COMPONENT_NAME (pSizer, "$Allocations waste");
                  const SmallModuleInfo* info = FindModuleInfo(stats, "CrySoundSystem.dll");
                  if (info)
                  {
                      pSizer->AddObject(info, info->memInfo.allocated - info->memInfo.requested);
                  }
              }
              if (m_env.pMusicSystem)
              {
                  SIZER_COMPONENT_NAME(pSizer, "Music");
                  m_env.pMusicSystem->GetMemoryUsage(pSizer);
              }
          }
      }
  
      if (m_env.pScriptSystem)
      {
          SIZER_COMPONENT_NAME(pSizer, "CryScriptSystem");
          {
              {
                  SIZER_COMPONENT_NAME (pSizer, "$Allocations waste");
                  const SmallModuleInfo* info = FindModuleInfo(stats, "CryScriptSystem.dll");
                  if (info)
                  {
                      pSizer->AddObject(info, info->memInfo.allocated - info->memInfo.requested);
                  }
              }
              m_env.pScriptSystem->GetMemoryStatistics(pSizer);
          }
      }
  
      if (m_env.pAISystem)
      {
          SIZER_COMPONENT_NAME(pSizer, "CryAISystem");
          {
              {
                  SIZER_COMPONENT_NAME (pSizer, "$Allocations waste");
                  const SmallModuleInfo* info = FindModuleInfo(stats, "CryAISystem.dll");
                  if (info)
                  {
                      pSizer->AddObject(info, info->memInfo.allocated - info->memInfo.requested);
                  }
              }
              m_env.pAISystem->GetMemoryStatistics (pSizer);
          }
      }
  
      if (m_env.pGame)
      {
          {
              SIZER_COMPONENT_NAME(pSizer, "Game");
              {
                  SIZER_COMPONENT_NAME (pSizer, "$Allocations waste");
                  const char* szGameDllName = gEnv->pConsole->GetCVar("sys_dll_game")->GetString();
                  const SmallModuleInfo* info = FindModuleInfo(stats, szGameDllName);
                  if (info)
                  {
                      pSizer->AddObject(info, info->memInfo.allocated - info->memInfo.requested);
                  }
              }
  
              m_env.pGame->GetMemoryStatistics (pSizer);
              m_env.pGame->GetIGameFramework()->GetMemoryUsage(pSizer);
          }
      }
  
      if (m_env.pNetwork)
      {
          SIZER_COMPONENT_NAME(pSizer, "Network");
          {
              SIZER_COMPONENT_NAME (pSizer, "$Allocations waste");
              const SmallModuleInfo* info = FindModuleInfo(stats, "CryNetwork.dll");
              if (info)
              {
                  pSizer->AddObject(info, info->memInfo.allocated - info->memInfo.requested);
              }
          }
  
          m_env.pNetwork->GetMemoryStatistics(pSizer);
      }
  
      if (m_env.pEntitySystem)
      {
          SIZER_COMPONENT_NAME(pSizer, "CryEntitySystem");
          {
              SIZER_COMPONENT_NAME (pSizer, "$Allocations waste");
              const SmallModuleInfo* info = FindModuleInfo(stats, "CryEntitySystem.dll");
              if (info)
              {
                  pSizer->AddObject(info, info->memInfo.allocated - info->memInfo.requested);
              }
          }
  
          m_env.pEntitySystem->GetMemoryStatistics(pSizer);
      }
  
      {
          SIZER_COMPONENT_NAME(pSizer, "UserData");
          if (m_pUserCallback)
          {
              m_pUserCallback->GetMemoryUsage(pSizer);
          }
      }
  
  #ifdef WIN32
      {
          SIZER_COMPONENT_NAME(pSizer, "Code");
          GetExeSizes (pSizer, nPurpose);
      }
  #endif
  
      pSizer->End();
  }
  
  //////////////////////////////////////////////////////////////////////////
  const char* CSystem::GetUserName()
  {
  #if defined(WIN32)
      static const int iNameBufferSize = 1024;
      static char szNameBuffer[iNameBufferSize];
      memset(szNameBuffer, 0, iNameBufferSize);
  
      DWORD dwSize = iNameBufferSize;
  #if defined(WIN32) || defined(WIN64)
      wchar_t nameW[iNameBufferSize];
      ::GetUserNameW(nameW, &dwSize);
      cry_strcpy(szNameBuffer, CryStringUtils::WStrToUTF8(nameW));
  #else
      IPlatformOS::TUserName userName;
      if (GetPlatformOS()->UserGetName(GetPlatformOS()->GetFirstSignedInUser(), userName))
      {
          cry_strcpy(szNameBuffer, iNameBufferSize, userName.c_str());
      }
  #endif
      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(SystemUtilsApple::GetUserName(szNameBuffer, iNameBufferSize))
      {
          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)
      {
          string suffix;
          for (int instance = 0;; ++instance)
          {
              suffix.Format("(%d)", instance);
  
              HANDLE instanceMutex = CreateMutex(NULL, TRUE, "LumberyardApplication" + suffix);
              // search for duplicates
              if (GetLastError() != ERROR_ALREADY_EXISTS)
              {
                  m_iApplicationInstance = instance;
                  break;
              }
          }
      }
  
      return m_iApplicationInstance;
  #else
      return 0;
  #endif
  }
  
  
  // refreshes the m_pMemStats if necessary; creates it if it's not created
  //////////////////////////////////////////////////////////////////////////
  void CSystem::TickMemStats(MemStatsPurposeEnum nPurpose, IResourceCollector* pResourceCollector)
  {
      // gather the statistics, if required
      // if there's  no object, or if it's time to recalculate, or if it's for dump, then recalculate it
      if (!m_pMemStats || (m_env.pRenderer->GetFrameID(false) % m_cvMemStats->GetIVal()) == 0 || nPurpose == nMSP_ForDump)
      {
          if (!m_pMemStats)
          {
              if (m_cvMemStats->GetIVal() < 4 && m_cvMemStats->GetIVal())
              {
                  GetILog()->LogToConsole("memstats is too small (%d). Performance impact can be significant. Please set to a greater value.", m_cvMemStats->GetIVal());
              }
              m_pMemStats = new CrySizerStats();
          }
  
          if (!m_pSizer)
          {
              m_pSizer = new CrySizerImpl();
          }
  
          m_pSizer->SetResourceCollector(pResourceCollector);
  
          m_pMemStats->startTimer(0, GetITimer());
          CollectMemStats (m_pSizer, nPurpose);
          m_pMemStats->stopTimer(0, GetITimer());
  
          m_pMemStats->startTimer(1, GetITimer());
          CrySizerStatsBuilder builder (m_pSizer);
          builder.build (m_pMemStats);
          m_pMemStats->stopTimer(1, GetITimer());
  
          m_pMemStats->startTimer(2, GetITimer());
          m_pSizer->clear();
          m_pMemStats->stopTimer(2, GetITimer());
      }
      else
      {
          m_pMemStats->incAgeFrames();
      }
  }
  
  //#define __HASXP
  
  // these 2 functions are duplicated in System.cpp in editor
  //////////////////////////////////////////////////////////////////////////
  #if !defined(LINUX)
  extern int CryStats(char* buf);
  #endif
  int CSystem::DumpMMStats(bool log)
  {
  #if defined(LINUX)
      return 0;
  #else
      if (log)
      {
          char buf[1024];
          int n = CryStats(buf);
          GetILog()->Log(buf);
          return n;
      }
      else
      {
          return CryStats(NULL);
      };
  #endif
  };
  
  //////////////////////////////////////////////////////////////////////////
  struct CryDbgModule
  {
      HANDLE heap;
      WIN_HMODULE handle;
      string name;
      DWORD dwSize;
  };
  
  //////////////////////////////////////////////////////////////////////////
  void CSystem::DebugStats(bool checkpoint, bool leaks)
  {
  #ifdef WIN32
      std::vector<CryDbgModule> dbgmodules;
  
      //////////////////////////////////////////////////////////////////////////
      // Use windows Performance Monitoring API to enumerate all modules of current process.
      //////////////////////////////////////////////////////////////////////////
      HANDLE hSnapshot;
      hSnapshot = CreateToolhelp32Snapshot (TH32CS_SNAPMODULE, 0);
      if (hSnapshot != INVALID_HANDLE_VALUE)
      {
          MODULEENTRY32 me;
          memset (&me, 0, sizeof(me));
          me.dwSize = sizeof(me);
  
          if (Module32First (hSnapshot, &me))
          {
              // the sizes of each module group
              do
              {
                  CryDbgModule module;
                  module.handle = me.hModule;
                  module.name = me.szModule;
                  module.dwSize = me.modBaseSize;
                  dbgmodules.push_back(module);
              } while (Module32Next(hSnapshot, &me));
          }
          CloseHandle (hSnapshot);
      }
      //////////////////////////////////////////////////////////////////////////
  
  
      ILog* log = GetILog();
      int totalal = 0, totalbl = 0, nolib = 0;
  
  #ifdef _DEBUG
      int extrastats[10];
  #endif
  
      int totalUsedInModules = 0;
      int countedMemoryModules = 0;
      for (int i = 0; i < (int)(dbgmodules.size()); i++)
      {
          if (!dbgmodules[i].handle)
          {
              CryLogAlways("WARNING: <CrySystem> CSystem::DebugStats: NULL handle for %s", dbgmodules[i].name.c_str());
              nolib++;
              continue;
          }
          ;
  
          typedef int (* PFN_MODULEMEMORY)();
          PFN_MODULEMEMORY fpCryModuleGetAllocatedMemory = (PFN_MODULEMEMORY)::GetProcAddress((HMODULE)dbgmodules[i].handle, "CryModuleGetAllocatedMemory");
          if (fpCryModuleGetAllocatedMemory)
          {
              int allocatedMemory = fpCryModuleGetAllocatedMemory();
              totalUsedInModules += allocatedMemory;
              countedMemoryModules++;
              CryLogAlways("%8d K used in Module %s: ", allocatedMemory / 1024, dbgmodules[i].name.c_str());
          }
  
  #ifdef _DEBUG
          typedef void (* PFNUSAGESUMMARY)(ILog* log, const char*, int*);
          typedef void (* PFNCHECKPOINT)();
          PFNUSAGESUMMARY fpu = (PFNUSAGESUMMARY)::GetProcAddress((HMODULE)dbgmodules[i].handle, "UsageSummary");
          PFNCHECKPOINT fpc = (PFNCHECKPOINT)::GetProcAddress((HMODULE)dbgmodules[i].handle, "CheckPoint");
          if (fpu && fpc)
          {
              if (checkpoint)
              {
                  fpc();
              }
              else
              {
                  extrastats[2] = (int)leaks;
                  fpu(log, dbgmodules[i].name.c_str(), extrastats);
                  totalal += extrastats[0];
                  totalbl += extrastats[1];
              };
          }
          else
          {
              CryLogAlways("WARNING: <CrySystem> CSystem::DebugStats: could not retrieve function from DLL %s", dbgmodules[i].name.c_str());
              nolib++;
          };
  #endif
  
          typedef HANDLE(* PFNGETDLLHEAP)();
          PFNGETDLLHEAP fpg = (PFNGETDLLHEAP)::GetProcAddress((HMODULE)dbgmodules[i].handle, "GetDLLHeap");
          if (fpg)
          {
              dbgmodules[i].heap = fpg();
          }
          ;
      }
      ;
  
      CryLogAlways("-------------------------------------------------------");
      CryLogAlways("%8d K Total Memory Allocated in %d Modules", totalUsedInModules / 1024, countedMemoryModules);
  #ifdef _DEBUG
      CryLogAlways("$8GRAND TOTAL: %d k, %d blocks (%d dlls not included)", totalal / 1024, totalbl, nolib);
      CryLogAlways("estimated debugalloc overhead: between %d k and %d k", totalbl * 36 / 1024, totalbl * 72 / 1024);
  #endif
  
      //////////////////////////////////////////////////////////////////////////
      // Get HeapQueryInformation pointer if on windows XP.
      //////////////////////////////////////////////////////////////////////////
      typedef BOOL (WINAPI * FUNC_HeapQueryInformation)(HANDLE, HEAP_INFORMATION_CLASS, PVOID, SIZE_T, PSIZE_T);
      FUNC_HeapQueryInformation pFnHeapQueryInformation = NULL;
      HMODULE hKernelInstance = CryLoadLibrary("Kernel32.dll");
      if (hKernelInstance)
      {
          pFnHeapQueryInformation = (FUNC_HeapQueryInformation)(::GetProcAddress(hKernelInstance, "HeapQueryInformation"));
      }
      //////////////////////////////////////////////////////////////////////////
  
      const int MAXHANDLES = 100;
      HANDLE handles[MAXHANDLES];
      int realnumh = GetProcessHeaps(MAXHANDLES, handles);
      char hinfo[1024];
      PROCESS_HEAP_ENTRY phe;
      CryLogAlways("$6--------------------- dump of windows heaps ---------------------");
      int nTotalC = 0, nTotalCP = 0, nTotalUC = 0, nTotalUCP = 0, totalo = 0;
      for (int i = 0; i < realnumh; i++)
      {
          HANDLE hHeap = handles[i];
          HeapCompact(hHeap, 0);
          hinfo[0] = 0;
          if (pFnHeapQueryInformation)
          {
              pFnHeapQueryInformation(hHeap, HeapCompatibilityInformation, hinfo, 1024, NULL);
          }
          else
          {
              for (int m = 0; m < (int)(dbgmodules.size()); m++)
              {
                  if (dbgmodules[m].heap == handles[i])
                  {
                      strcpy(hinfo, dbgmodules[m].name.c_str());
                  }
              }
          }
          phe.lpData = NULL;
          int nCommitted = 0, nUncommitted = 0, nOverhead = 0;
          int nCommittedPieces = 0, nUncommittedPieces = 0;
          int nPrevRegionIndex = -1;
          while (HeapWalk(hHeap, &phe))
          {
              if (phe.wFlags & PROCESS_HEAP_REGION)
              {
                  assert (++nPrevRegionIndex == phe.iRegionIndex);
                  nCommitted += phe.Region.dwCommittedSize;
                  nUncommitted +=  phe.Region.dwUnCommittedSize;
                  assert (phe.cbData == 0 || (phe.wFlags & PROCESS_HEAP_ENTRY_BUSY));
              }
              else
              if (phe.wFlags & PROCESS_HEAP_UNCOMMITTED_RANGE)
              {
                  nUncommittedPieces += phe.cbData;
              }
              else
              {
                  //if (phe.wFlags & PROCESS_HEAP_ENTRY_BUSY)
                  nCommittedPieces += phe.cbData;
              }
  
  
              {
                  /*
                  MEMORY_BASIC_INFORMATION mbi;
                  if (VirtualQuery(phe.lpData, &mbi,sizeof(mbi)) == sizeof(mbi))
                  {
                  if (mbi.State == MEM_COMMIT)
                  nCommittedPieces += phe.cbData;//mbi.RegionSize;
                  //else
                  //  nUncommitted += mbi.RegionSize;
                  }
                  else
                  nCommittedPieces += phe.cbData;
                  */
              }
  
              nOverhead += phe.cbOverhead;
          }
          ;
          int nCommittedMin = min(nCommitted, nCommittedPieces);
          int nCommittedMax = max(nCommitted, nCommittedPieces);
          CryLogAlways("* heap %8x: %6d (or ~%6d) K in use, %6d..%6d K uncommitted, %6d K overhead (%s)\n",
              handles[i], nCommittedPieces / 1024, nCommitted / 1024, nUncommittedPieces / 1024, nUncommitted / 1024, nOverhead / 1024, hinfo);
  
          nTotalC += nCommitted;
          nTotalCP += nCommittedPieces;
          nTotalUC += nUncommitted;
          nTotalUCP += nUncommittedPieces;
          totalo += nOverhead;
      }
      ;
      CryLogAlways("$6----------------- total in heaps: %d megs committed (win stats shows ~%d) (%d..%d uncommitted, %d k overhead) ---------------------", nTotalCP / 1024 / 1024, nTotalC / 1024 / 1024, nTotalUCP / 1024 / 1024, nTotalUC / 1024 / 1024, totalo / 1024);
  
  #endif //WIN32
  };
  
  #ifdef WIN32
  struct DumpHeap32Stats
  {
      DumpHeap32Stats()
          : dwFree(0)
          , dwMoveable(0)
          , dwFixed(0)
          , dwUnknown(0)
      {
      }
      void operator += (const DumpHeap32Stats& right)
      {
          dwFree += right.dwFree;
          dwMoveable += right.dwMoveable;
          dwFixed += right.dwFixed;
          dwUnknown += right.dwUnknown;
      }
      DWORD dwFree;
      DWORD dwMoveable;
      DWORD dwFixed;
      DWORD dwUnknown;
  };
  static void DumpHeap32 (const HEAPLIST32& hl, DumpHeap32Stats& stats)
  {
      HEAPENTRY32 he;
      memset (&he, 0, sizeof(he));
      he.dwSize = sizeof(he);
  
      if (Heap32First (&he, hl.th32ProcessID, hl.th32HeapID))
      {
          DumpHeap32Stats heap;
          do
          {
              if (he.dwFlags & LF32_FREE)
              {
                  heap.dwFree += he.dwBlockSize;
              }
              else
              if (he.dwFlags & LF32_MOVEABLE)
              {
                  heap.dwMoveable += he.dwBlockSize;
              }
              else
              if (he.dwFlags & LF32_FIXED)
              {
                  heap.dwFixed += he.dwBlockSize;
              }
              else
              {
                  heap.dwUnknown += he.dwBlockSize;
              }
          } while (Heap32Next (&he));
  
          CryLogAlways ("%08X  %6d %6d %6d (%d)", hl.th32HeapID, heap.dwFixed / 0x400, heap.dwFree / 0x400, heap.dwMoveable / 0x400, heap.dwUnknown / 0x400);
          stats += heap;
      }
      else
      {
          CryLogAlways ("%08X  empty or invalid");
      }
  }
  
  //////////////////////////////////////////////////////////////////////////
  class CStringOrder
  {
  public:
      bool operator () (const char* szLeft, const char* szRight) const {return _stricmp(szLeft, szRight) < 0; }
  };
  typedef std::map<const char*, unsigned, CStringOrder> StringToSizeMap;
  void AddSize (StringToSizeMap& mapSS, const char* szString, unsigned nSize)
  {
      StringToSizeMap::iterator it = mapSS.find (szString);
      if (it == mapSS.end())
      {
          mapSS.insert (StringToSizeMap::value_type(szString, nSize));
      }
      else
      {
          it->second += nSize;
      }
  }
  
  //////////////////////////////////////////////////////////////////////////
  const char* GetModuleGroup (const char* szString)
  {
      for (unsigned i = 0; i < sizeof(g_szModuleGroups) / sizeof(g_szModuleGroups[0]); ++i)
      {
          if (_stricmp(szString, g_szModuleGroups[i][0]) == 0)
          {
              return g_szModuleGroups[i][1];
          }
      }
      return "Other";
  }
  
  //////////////////////////////////////////////////////////////////////////
  void CSystem::GetExeSizes (ICrySizer* pSizer, MemStatsPurposeEnum nPurpose)
  {
      HANDLE hSnapshot;
      hSnapshot = CreateToolhelp32Snapshot (TH32CS_SNAPMODULE, 0);
      if (hSnapshot == INVALID_HANDLE_VALUE)
      {
          CryLogAlways ("Cannot get the module snapshot, error code %d", GetLastError());
          return;
      }
  
      DWORD dwProcessID = GetCurrentProcessId();
  
      MODULEENTRY32 me;
      memset (&me, 0, sizeof(me));
      me.dwSize = sizeof(me);
  
      if (Module32First (hSnapshot, &me))
      {
          // the sizes of each module group
          StringToSizeMap mapGroupSize;
          DWORD dwTotalModuleSize = 0;
          do
          {
              dwProcessID = me.th32ProcessID;
              const char* szGroup = GetModuleGroup (me.szModule);
              SIZER_COMPONENT_NAME(pSizer, szGroup);
              if (nPurpose == nMSP_ForDump)
              {
                  SIZER_COMPONENT_NAME(pSizer, me.szModule);
                  pSizer->AddObject(me.modBaseAddr, me.modBaseSize);
              }
              else
              {
                  pSizer->AddObject(me.modBaseAddr, me.modBaseSize);
              }
          } while (Module32Next(hSnapshot, &me));
      }
      else
      {
          CryLogAlways ("No modules to dump");
      }
  
      CloseHandle (hSnapshot);
  }
  
  #endif
  
  //////////////////////////////////////////////////////////////////////////
  void CSystem::DumpWinHeaps()
  {
  #ifdef WIN32
      //
      // Retrieve modules and log them; remember the process id
  
      HANDLE hSnapshot;
      hSnapshot = CreateToolhelp32Snapshot (TH32CS_SNAPMODULE, 0);
      if (hSnapshot == INVALID_HANDLE_VALUE)
      {
          CryLogAlways ("Cannot get the module snapshot, error code %d", GetLastError());
          return;
      }
  
      DWORD dwProcessID = GetCurrentProcessId();
  
      MODULEENTRY32 me;
      memset (&me, 0, sizeof(me));
      me.dwSize = sizeof(me);
  
      if (Module32First (hSnapshot, &me))
      {
          // the sizes of each module group
          StringToSizeMap mapGroupSize;
          DWORD dwTotalModuleSize = 0;
          CryLogAlways ("base        size  module");
          do
          {
              dwProcessID = me.th32ProcessID;
              const char* szGroup = GetModuleGroup (me.szModule);
              CryLogAlways ("%08X %8X  %25s   - %s", me.modBaseAddr, me.modBaseSize, me.szModule, _stricmp(szGroup, "Other") ? szGroup : "");
              dwTotalModuleSize += me.modBaseSize;
              AddSize (mapGroupSize, szGroup, me.modBaseSize);
          } while (Module32Next(hSnapshot, &me));
  
          CryLogAlways ("------------------------------------");
          for (StringToSizeMap::iterator it = mapGroupSize.begin(); it != mapGroupSize.end(); ++it)
          {
              CryLogAlways ("         %6.3f Mbytes  - %s", double(it->second) / 0x100000, it->first);
          }
          CryLogAlways ("------------------------------------");
          CryLogAlways ("         %6.3f Mbytes  - TOTAL", double(dwTotalModuleSize) / 0x100000);
          CryLogAlways ("------------------------------------");
      }
      else
      {
          CryLogAlways ("No modules to dump");
      }
  
      CloseHandle (hSnapshot);
  
      //
      // Retrieve the heaps and dump each of them with a special function
  
      hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPHEAPLIST, 0);
      if (hSnapshot == INVALID_HANDLE_VALUE)
      {
          CryLogAlways ("Cannot get the heap LIST snapshot, error code %d", GetLastError());
          return;
      }
  
      HEAPLIST32 hl;
      memset (&hl, 0, sizeof(hl));
      hl.dwSize = sizeof(hl);
  
      CryLogAlways ("__Heap__   fixed   free   move (unknown)");
      if (Heap32ListFirst (hSnapshot, &hl))
      {
          DumpHeap32Stats stats;
          do
          {
              DumpHeap32 (hl, stats);
          } while (Heap32ListNext (hSnapshot, &hl));
  
          CryLogAlways ("-------------------------------------------------");
          CryLogAlways ("$6          %6.3f %6.3f %6.3f (%.3f) Mbytes", double(stats.dwFixed) / 0x100000, double(stats.dwFree) / 0x100000, double(stats.dwMoveable) / 0x100000, double(stats.dwUnknown) / 0x100000);
          CryLogAlways ("-------------------------------------------------");
      }
      else
      {
          CryLogAlways ("No heaps to dump");
      }
  
      CloseHandle(hSnapshot);
  #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))
          {
              cry_strcpy(szBuffer, (char*)lpMsgBuf);
              LocalFree(lpMsgBuf);
          }
          else
          {
              return 0;
          }
  
          return szBuffer;
      }
  #else
      return 0;
  
  #endif //WIN32
  
      return 0;
  }
  
  //////////////////////////////////////////////////////////////////////////
  void CSystem::FatalError(const char* format, ...)
  {
      // format message
      va_list ArgList;
      char szBuffer[MAX_WARNING_LENGTH];
      const char* sPrefix = "";
      strcpy(szBuffer, sPrefix);
      va_start(ArgList, format);
      _vsnprintf(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("<CrySystem> Last System Error: %s", szSysErrorMessage);
      }
  
      if (const char* pLoadingProfilerCallstack = GetLoadingProfilerCallstack())
      {
          if (pLoadingProfilerCallstack[0])
          {
              CryLogAlways("<CrySystem> LoadingProfilerCallstack: %s", pLoadingProfilerCallstack);
          }
      }
  
      if (GetUserCallback())
      {
          GetUserCallback()->OnError(szBuffer);
      }
  
      assert(szBuffer[0] >= ' ');
      //  strcpy(szBuffer,szBuffer+1);    // remove verbosity tag since it is not supported by ::MessageBox
  
      LogSystemInfo();
  
      CollectMemStats(0, nMSP_ForCrashLog);
  
      OutputDebugString(szBuffer);
  #ifdef WIN32
      OnFatalError(szBuffer);
      if (!g_cvars.sys_no_crash_dialog)
      {
          ICVar* pFullscreen = (gEnv && gEnv->pConsole) ? gEnv->pConsole->GetCVar("r_Fullscreen") : 0;
          if (pFullscreen && pFullscreen->GetIVal() != 0 && gEnv->pRenderer && gEnv->pRenderer->GetHWND())
          {
              ::ShowWindow((HWND)gEnv->pRenderer->GetHWND(), SW_MINIMIZE);
          }
          ::MessageBox(NULL, szBuffer, "Lumberyard Error", MB_OK | MB_ICONERROR | MB_SYSTEMMODAL);
      }
  
      // Dump callstack.
  #endif
  #if defined (WIN32)
      //Triggers a fatal error, so the DebugCallstack can create the error.log and terminate the application
      IDebugCallStack::instance()->FatalError(szBuffer);
  #endif
  
      CryDebugBreak();
  
      // app can not continue
  #ifdef _DEBUG
  
  #if defined(WIN32) && !defined(WIN64)
      DEBUG_BREAK;
  #endif
  
  #else
  
  #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
  
      _exit(1);
  #endif
  }
  
  void CSystem::ReportBug(const char* format, ...)
  {
  #if defined (WIN32)
      va_list ArgList;
      char szBuffer[MAX_WARNING_LENGTH];
      const char* sPrefix = "";
      strcpy(szBuffer, sPrefix);
      va_start(ArgList, format);
      _vsnprintf(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)
      int nMaxCount = nCount;
      nCount = 0;
      IDebugCallStack::instance()->CollectCurrentCallStack();
      static std::vector<string> funcs;
      funcs.clear();
      IDebugCallStack::instance()->getCallStack(funcs);
  
      nCount = ((int)funcs.size()) - 1; // Remove this function from the call stack.
      if (nCount < 0)
      {
          nCount = 0;
      }
      else if (nCount > nMaxCount)
      {
          nCount = nMaxCount;
      }
  
      for (int i = 0; i < nMaxCount && i < nCount; i++)
      {
          pFunctions[i] = funcs[i + 1].c_str();
      }
  #else
      nCount = 0;
  #endif
  }
  
  //////////////////////////////////////////////////////////////////////////
  void CSystem::debug_LogCallStack(int nMaxFuncs, int nFlags)
  {
      if (nMaxFuncs > 32)
      {
          nMaxFuncs = 32;
      }
      // Print call stack for each find.
      const char* funcs[32];
      int nCount = nMaxFuncs;
      int nCurFrame = 0;
      if (m_env.pRenderer)
      {
          nCurFrame = (int)m_env.pRenderer->GetFrameID(false);
      }
      CryLogAlways("    ----- CallStack (Frame: %d) -----", nCurFrame);
      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]);
      }
  }
  
  //////////////////////////////////////////////////////////////////////////
  // Support relaunching for windows media center edition.
  //////////////////////////////////////////////////////////////////////////
  #if defined(WIN32)
  #if (_WIN32_WINNT < 0x0501)
  #define SM_MEDIACENTER          87
  #endif
  bool CSystem::ReLaunchMediaCenter()
  {
      // Skip if not running on a Media Center
      if (GetSystemMetrics(SM_MEDIACENTER) == 0)
      {
          return false;
      }
  
      // Get the path to Media Center
      char szExpandedPath[AZ_MAX_PATH_LEN];
      if (!ExpandEnvironmentStrings("%SystemRoot%\\ehome\\ehshell.exe", szExpandedPath, AZ_MAX_PATH_LEN))
      {
          return false;
      }
  
      // Skip if ehshell.exe doesn't exist
      if (GetFileAttributes(szExpandedPath) == 0xFFFFFFFF)
      {
          return false;
      }
  
      // Launch ehshell.exe
      INT_PTR result = (INT_PTR)ShellExecute(NULL, TEXT("open"), szExpandedPath, NULL, NULL, SW_SHOWNORMAL);
      return (result > 32);
  }
  #else
  bool CSystem::ReLaunchMediaCenter()
  {
      return false;
  }
  #endif //defined(WIN32)
  
  //////////////////////////////////////////////////////////////////////////
  #if defined(WIN32)
  void CSystem::LogSystemInfo()
  {
      //////////////////////////////////////////////////////////////////////
      // Write the system informations to the log
      //////////////////////////////////////////////////////////////////////
  
      char szBuffer[1024];
      char szProfileBuffer[128];
      char szLanguageBuffer[64];
      //char szCPUModel[64];
      char* pChar = 0;
  
      MEMORYSTATUSEX MemoryStatus;
      MemoryStatus.dwLength = sizeof(MemoryStatus);
  
      DEVMODE DisplayConfig;
      OSVERSIONINFO OSVerInfo;
      OSVerInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  
      // log Windows type
      Win32SysInspect::GetOS(m_env.pi.winVer, m_env.pi.win64Bit, szBuffer, sizeof(szBuffer));
      CryLogAlways(szBuffer);
  
      // log user name
      CryLog("User name: \"%s\"", GetUserName());
  
      // log system language
      GetLocaleInfo(LOCALE_SYSTEM_DEFAULT, LOCALE_SENGLANGUAGE, szLanguageBuffer, sizeof(szLanguageBuffer));
      sprintf(szBuffer, "System language: %s", szLanguageBuffer);
      CryLogAlways(szBuffer);
  
      // log Windows directory
      GetWindowsDirectory(szBuffer, sizeof(szBuffer));
      string str = "Windows Directory: \"";
      str += szBuffer;
      str += "\"";
      CryLogAlways(str);
  
      //////////////////////////////////////////////////////////////////////
      // Send system time & date
      //////////////////////////////////////////////////////////////////////
  
      str = "Local time is ";
      _strtime(szBuffer);
      str += szBuffer;
      str += " ";
      _strdate(szBuffer);
      str += szBuffer;
      sprintf(szBuffer, ", system running for %d minutes", GetTickCount() / 60000);
      str += szBuffer;
      CryLogAlways(str);
  
      //////////////////////////////////////////////////////////////////////
      // Send system memory status
      //////////////////////////////////////////////////////////////////////
  
      GlobalMemoryStatusEx(&MemoryStatus);
      sprintf(szBuffer, "%I64dMB physical memory installed, %I64dMB available, %I64dMB virtual memory installed, %ld percent of memory in use",
          MemoryStatus.ullTotalPhys  / 1048576 + 1,
          MemoryStatus.ullAvailPhys / 1048576,
          MemoryStatus.ullTotalVirtual / 1048576,
          MemoryStatus.dwMemoryLoad);
      CryLogAlways(szBuffer);
  
      if (GetISystem()->GetIMemoryManager())
      {
          IMemoryManager::SProcessMemInfo memCounters;
          GetISystem()->GetIMemoryManager()->GetProcessMemInfo(memCounters);
  
          uint64 PagefileUsage = memCounters.PagefileUsage;
          uint64 PeakPagefileUsage = memCounters.PeakPagefileUsage;
          uint64 WorkingSetSize = memCounters.WorkingSetSize;
          sprintf(szBuffer, "PageFile usage: %I64dMB, Working Set: %I64dMB, Peak PageFile usage: %I64dMB,",
              (uint64)PagefileUsage / (1024 * 1024),
              (uint64)WorkingSetSize / (1024 * 1024),
              (uint64)PeakPagefileUsage / (1024 * 1024));
          CryLogAlways(szBuffer);
      }
  
      //////////////////////////////////////////////////////////////////////
      // Send display settings
      //////////////////////////////////////////////////////////////////////
  
      EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &DisplayConfig);
      GetPrivateProfileString("boot.description", "display.drv",
          "(Unknown graphics card)", szProfileBuffer, sizeof(szProfileBuffer),
          "system.ini");
      sprintf(szBuffer, "Current display mode is %dx%dx%d, %s",
          DisplayConfig.dmPelsWidth, DisplayConfig.dmPelsHeight,
          DisplayConfig.dmBitsPerPel, szProfileBuffer);
      CryLogAlways(szBuffer);
  
      //////////////////////////////////////////////////////////////////////
      // Send input device configuration
      //////////////////////////////////////////////////////////////////////
  
      str = "";
      // Detect the keyboard type
      switch (GetKeyboardType(0))
      {
      case 1:
          str = "IBM PC/XT (83-key)";
          break;
      case 2:
          str = "ICO (102-key)";
          break;
      case 3:
          str = "IBM PC/AT (84-key)";
          break;
      case 4:
          str = "IBM enhanced (101/102-key)";
          break;
      case 5:
          str = "Nokia 1050";
          break;
      case 6:
          str = "Nokia 9140";
          break;
      case 7:
          str = "Japanese";
          break;
      default:
          str = "Unknown";
          break;
      }
  
      // Any mouse attached ?
      if (!GetSystemMetrics(SM_MOUSEPRESENT))
      {
          CryLogAlways(str + " keyboard and no mouse installed");
      }
      else
      {
          sprintf(szBuffer, " keyboard and %i+ button mouse installed",
              GetSystemMetrics(SM_CMOUSEBUTTONS));
          CryLogAlways(str + szBuffer);
      }
  
      CryLogAlways("--------------------------------------------------------------------------------");
  }
  #else
  void CSystem::LogSystemInfo()
  {
  }
  #endif
  
  #if (defined(WIN32) || defined(WIN64))
  //////////////////////////////////////////////////////////////////////////
  bool CSystem::GetWinGameFolder(char* szMyDocumentsPath, int maxPathSize)
  {
      bool bSucceeded  = false;
      // check Vista and later OS first
  
      HMODULE shell32 = LoadLibraryA("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
                  cry_strcpy(szMyDocumentsPath, maxPathSize, CryStringUtils::WStrToUTF8(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)
          {
              cry_strcpy(szMyDocumentsPath, maxPathSize, CryStringUtils::WStrToUTF8(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 = GetNamedSecurityInfo(".", 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(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)
  
      _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);
          }
      }
  
  #endif //#if defined(WIN32) && !defined(WIN64)
  
  #ifdef WIN32
      _mm_setcsr(_mm_getcsr() & ~0x280 | (type > 0 ? 0 : 0x280));
  #endif
  
      #endif //_RELEASE
  }
  