You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Code/CryEngine/CrySystem/SystemWin32.cpp

1536 lines
46 KiB
C++

/*
* 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 "CrySystem_precompiled.h"
#include "System.h"
#include <time.h>
#include <IRenderer.h>
#include <IMovieSystem.h>
#include <ILog.h>
#include <CryLibrary.h>
#include <StringUtils.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 "CrySizerStats.h"
#include "CrySizerImpl.h"
#include "StreamEngine/StreamEngine.h"
#include "LocalizedStringManager.h"
#include "XML/XmlUtils.h"
#include "AutoDetectSpec.h"
#if defined(WIN32)
__pragma(comment(lib, "wininet.lib"))
__pragma(comment(lib, "Winmm.lib"))
#endif
#if defined(APPLE)
#include "SystemUtilsApple.h"
#endif
static AZStd::vector<AZStd::string> GetModuleNames()
{
AZStd::vector<AZStd::string> moduleNames;
if (moduleNames.empty())
{
# ifdef MODULE_EXTENSION
# error MODULE_EXTENSION already defined!
# endif
# if defined(LINUX)
# define MODULE_EXTENSION ".so"
# elif defined(APPLE)
# define MODULE_EXTENSION ".dylib"
# else
# define MODULE_EXTENSION ".dll"
# endif
moduleNames.push_back("Cry3DEngine" MODULE_EXTENSION);
moduleNames.push_back("CryFont" MODULE_EXTENSION);
moduleNames.push_back("CrySystem" MODULE_EXTENSION);
#undef MODULE_EXTENSION
# if defined(LINUX)
moduleNames.push_back("CryRenderNULL.so");
# elif defined(APPLE)
moduleNames.push_back("CryRenderNULL.dylib");
# else
moduleNames.push_back("Editor.exe");
moduleNames.push_back("CryRenderD3D9.dll");
moduleNames.push_back("CryRenderD3D10.dll");
moduleNames.push_back("CryRenderNULL.dll");
//TODO: launcher?
# endif
}
return moduleNames;
}
static bool QueryModuleMemoryInfo([[maybe_unused]] SCryEngineStatsModuleInfo& moduleInfo, [[maybe_unused]] int index)
{
return false;
}
// 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},
{"CryFont.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);
// 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)
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;
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 );
}
{
SIZER_COMPONENT_NAME(pSizer, "VFS");
if (m_pStreamEngine)
{
SIZER_COMPONENT_NAME(pSizer, "Stream Engine");
m_pStreamEngine->GetMemoryStatistics(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_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.
}
}
{
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) || 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);
cry_strcpy(szNameBuffer, CryStringUtils::WStrToUTF8(nameW));
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);
CreateMutex(NULL, TRUE, "LumberyardApplication" + suffix);
// 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
string suffix;
int instance = 0;
for (;; ++instance)
{
suffix.Format("(%d)", instance);
CreateMutex(NULL, TRUE, logFilePath + suffix);
if (GetLastError() != ERROR_ALREADY_EXISTS)
{
break;
}
}
return instance;
#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([[maybe_unused]] bool checkpoint, [[maybe_unused]] 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);
}
//////////////////////////////////////////////////////////////////////////
int nolib = 0;
#ifdef _DEBUG
ILog* log = GetILog();
int totalal = 0;
int totalbl = 0;
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: 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: 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])
{
azstrcpy(hinfo, AZ_ARRAY_SIZE(hinfo), dbgmodules[m].name.c_str());
}
}
}
phe.lpData = NULL;
int nCommitted = 0, nUncommitted = 0, nOverhead = 0;
int nCommittedPieces = 0, nUncommittedPieces = 0;
#if !defined(NDEBUG)
int nPrevRegionIndex = -1;
#endif
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;
}
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 azstricmp(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 (azstricmp(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;
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, azstricmp(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, ...)
{
// 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
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, "Open 3D Engine 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);
#elif defined(AZ_RESTRICTED_PLATFORM)
#define AZ_RESTRICTED_SECTION SYSTEMWIN32_CPP_SECTION_1
#include AZ_RESTRICTED_FILE(SystemWin32_cpp)
#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
#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 (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;
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];
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 system language
GetLocaleInfo(LOCALE_SYSTEM_DEFAULT, LOCALE_SENGLANGUAGE, szLanguageBuffer, sizeof(szLanguageBuffer));
azsprintf(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 ";
azstrtime(szBuffer);
str += szBuffer;
str += " ";
_strdate_s(szBuffer);
str += szBuffer;
azsprintf(szBuffer, ", system running for %lu minutes", GetTickCount() / 60000);
str += szBuffer;
CryLogAlways(str);
//////////////////////////////////////////////////////////////////////
// Send system memory status
//////////////////////////////////////////////////////////////////////
GlobalMemoryStatusEx(&MemoryStatus);
azsprintf(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;
azsprintf(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");
azsprintf(szBuffer, "Current display mode is %lux%lux%lu, %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
{
azsprintf(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([[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
}