//
// name:		app.cpp
// description:	Basic application startup and shutdown
//
// rage headers
#if __XENON
#include "system/xtl.h"
#endif

#include "diag/tracker_remote2.h"
#include "grprofile/timebars.h"
#include "input/dongle.h"
#include "net/nethardware.h"
#include "profile/telemetry.h"
#include "rline/rlnp.h"
#include "rline/rlnptrophy.h"
#include "rline/scmatchmaking/rlscmatchmanager.h"
#include "system/controlMgr.h"
#include "system/fileMgr.h"
#include "system/StreamingInstall.winrt.h"
#include "system/param.h"
#include "system/system.h"
#include "telemetry/telemetry_rage.h"
#include "bink/movie.h"


//framework
#include "fwnet/netremotelog.h"
#include "fwsys/timer.h"

// game headers
#include "app.h"
#include "game.h"
#include "memorytest.h"

#include "audio/frontendaudioentity.h"
#include "audio/northaudioengine.h"
#include "audiohardware/driver.h"
#include "camera/CamInterface.h"
#include "camera/viewports/ViewportManager.h"
#include "control/gamelogic.h"
#include "Control/replay/Replay.h"
#include "core/dongle.h"
#include "Cutscene/CutSceneManagerNew.h"
#include "debug/debug.h"
#include "frontend/GameStreamMgr.h"
#include "frontend/landing_page/LandingPageArbiter.h"
#include "frontend/loadingscreens.h"
#include "frontend/PauseMenu.h"
#include "frontend/ProfileSettings.h"
#include "frontend/WarningScreen.h"
#include "game/Clock.h"
#include "ik/IkManager.h"
#include "modelinfo/mlomodelinfo.h"
#include "network/live/livemanager.h"
#include "network/events/NetworkEventTypes.h"
#include "network/Objects/NetworkObjectPopulationMgr.h"
#include "network/Sessions/NetworkSession.h"
#include "tools/SectorTools.h"
#include "tools/TracingStats.h"
#include "objects/DummyObject.h"
#include "pathserver/navgenparam.h"
#include "profile/cputrace.h"
#include "renderer/renderThread.h"
#include "renderer/DrawLists/drawListMgr.h"
#include "renderer/DrawLists/drawListNY.h"
#include "replaycoordinator/ReplayCoordinator.h"
#include "saveload/savegame_autoload.h"
#include "saveload/GenericGameStorage.h"
#include "scene/scene.h"
#include "script/script.h"
#include "streaming/populationstreaming.h"
#include "streaming/streaming.h"
#include "system/EntitlementManager.h"
#include "system/PMCPerfControl.h"
#include "system/gcmPerfmon.h"
#include "system/param.h"
#include "system/taskscheduler.h"
#include "system/telemetry.h"
#include "vehicles/vehiclepopulation.h"
#include "security/ragesecengine.h"
#include "Weapons/Projectiles/Projectile.h"
#if  __PPU
#include "sn/libsn.h"
#include <stdlib.h> // for malloc_stats
#include <sys/memory.h>

#include <np.h>
#include <cell/sysmodule.h>
#include <sysutil/sysutil_gamecontent.h>
#endif // __PPU

#if __WIN32PC && __STEAM_BUILD
#pragma warning(disable: 4265)
#include "../../3rdParty/Steam/public/steam/steam_api.h"
#pragma warning(error: 4265)
#pragma comment(lib, "steam_api64.lib")
#include "data/certificateverify.h"
#include "diag/seh.h"
#endif

//#if EPIC_API_SUPPORTED
//#include "system/epic.h"
//#endif

//#if RSG_LAUNCHER_CHECK

//#define CHECK_BOOTSTRAP_PROCESS "PlayGTAV.exe"
//#define CHECK_PARENT_PROCESS	"GTAVLauncher.exe"
//#define CHECK_MTL_PROCESS		"Launcher.exe"
//#define CHECK_MTL_STEAMHELPER	"RockstarSteamHelper.exe"

//#include "system/xtl.h"
//#include <tlhelp32.h>
//#include <Psapi.h>
//#endif // RSG_LAUNCHER_CHECK

#include "Control/replay/ReplaySettings.h"
#include "Network/Objects/NetworkObjectMgr.h"

#if !__FINAL
PARAM(trackerreport, "[GAME] Filename for the initial memory track");
namespace rage 
{
	XPARAM(forceboothdd);
	XPARAM(noSocialClub);
	XPARAM(BlockOnLostFocus);
	extern char* XEX_TITLE_ID_HDD;	//	defined in gta5\src\dev\game\Core\main.cpp
}

#endif // !__FINAL

#if RAGE_TIMEBARS
#if __FINAL_LOGGING
NOSTRIP_PARAM(dumptimebarsonlongmainthread, "[app] Dump timebars if the main thread takes a certain amount of time (optionally time in ms as argument)");
NOSTRIP_PARAM(disableframemarkers, "[PROFILE] Disable frame encapsulating user markers, make Razor dump all per frame timings into one large bucket");
#else
PARAM(dumptimebarsonlongmainthread, "[app] Dump timebars if the main thread takes a certain amount of time (optionally time in ms as argument)");
PARAM(disableframemarkers, "[PROFILE] Disable frame encapsulating user markers, make Razor dump all per frame timings into one large bucket");
#endif
#endif // RAGE_TIMEBARS

#if RSG_PC
NOSTRIP_PC_XPARAM(benchmark);
#endif

#if RAGE_TRACKING
NOSTRIP_XPARAM(autoMemTrack);
#endif // RAGE_TRACKING

// Builds given to Natural Motion must be dongled. The NM_BUILD preprocessor variable is be defined locally when
// creating a build from NM.
#ifdef NM_BUILD
#define USE_DONGLE	(ENABLE_DONGLE & 1)
#else // NM_BUILD
#define USE_DONGLE	(ENABLE_DONGLE & 1)
#endif // NM_BUILD

#if USE_DONGLE
PARAM(testdongle, "dongle string to encode");
#endif
#if ENABLE_DONGLE
static const char* sEncodeString = "testingtesting123";
static const char* sMCFileName = "root.sys";
#endif
#if !__FINAL
sysTimer initTimer;
#endif
#if __BANK
bool g_maponlyHogEnabled = false;
bool g_cheapmodeHogEnabled = false;
bool g_maponlyExtraHogEnabled = false;
bool g_stealExtraMemoryEnabled = false;
#endif

#if RSG_ORBIS || BACKTRACE_ENABLED
static const size_t sCoredumpHandlerStackSize = 16 * 1024;
static char s_ThreadlStack[256];
static char s_debugInfoSummary[1024];

static void ChanneledOutputForStackTrace(const char* fmt, ...)
{
	char buffer[256];
	va_list args;
	va_start(args, fmt);
	vformatf(buffer, sizeof(buffer), fmt, args);
	va_end(args);
	safecat(s_ThreadlStack, buffer);
	safecat(s_ThreadlStack, "\n");
}

#if RSG_ORBIS
static void WriteUserDataAsText(const char* format, ...)
{
	static char formatted_text[1024]; // temporary log buffer
	va_list ap;
	va_start(ap, format);
	int size = vsnprintf(formatted_text, sizeof(formatted_text), format, ap);
	va_end(ap);

	sceCoredumpWriteUserData(formatted_text, size);
}
#else
static HANDLE s_crashContextHandle = INVALID_HANDLE_VALUE;
static void WriteUserDataAsText(const char* format, ...)
{
	if (s_crashContextHandle == INVALID_HANDLE_VALUE)
		return;

	static char formatted_text[1024]; // temporary log buffer
	va_list ap;
	va_start(ap, format);
	int size = vsnprintf(formatted_text, sizeof(formatted_text), format, ap);
	va_end(ap);

	WriteFile(s_crashContextHandle, formatted_text, (DWORD)size, NULL, NULL);
}
#endif

//The maximum total size for user data is 524288 bytes (512 Kb)
static int CoredumpHandler(void * UNUSED_PARAM(pCommon))
{
	WriteUserDataAsText("###### DEBUG INFO ######\n\n");

	//-------
// 	int streamVirtual=0, streamPhysical=0;
// 	CStreamingDebug::CalculateRequestedObjectSizes(streamVirtual, streamPhysical);
// 	streamVirtual  >>= 10;
// 	streamPhysical >>= 10;
	u32 milliseconds = fwTimer::GetSystemTimeInMilliseconds();
	u32 seconds = (u32) (milliseconds / 1000) % 60 ;
	u32 minutes = (u32) ((milliseconds / (1000*60)) % 60);
	u32 hours   = (u32) ((milliseconds / (1000*60*60)) % 24);
	char gameState[32];
	CApp* app = CApp::GetInstance();
	if (app)
	{
		switch (app->GetState())
		{
		case CApp::State_InitSystem:		sprintf(gameState, "System Init");		break;
		case CApp::State_InitGame:			sprintf(gameState, "Game Init");		break;
		case CApp::State_RunGame:			sprintf(gameState, "Game Running");		break;
		case CApp::State_ShutdownGame:		sprintf(gameState, "Game Shutdown");	break;
		case CApp::State_ShutdownSystem:	sprintf(gameState, "System Shutdown");	break;
		default:							sprintf(gameState, "Unknown");			break;
		}
	}
	WriteUserDataAsText("============ SYSTEM INFO ============\n");
	WriteUserDataAsText("Game State : %s\n", gameState);
	WriteUserDataAsText("System Time (%dh %dm %ds)\n", hours, minutes, seconds);
	WriteUserDataAsText("Raw System Time (%dms)\n", sysTimer::GetSystemMsTime());
	WriteUserDataAsText("Frame (%d) - Paused (%s)\n", fwTimer::GetFrameCount(), fwTimer::IsGamePaused()?"yes":"no");
	WriteUserDataAsText("Streaming Requests (%d)\n", strStreamingEngine::GetInfo().GetNumberRealObjectsRequested_Cached());
	WriteUserDataAsText("  HDD: %s, ODD: %s\n", IsDeviceLocked(pgStreamer::HARDDRIVE)?"locked":"unlocked", IsDeviceLocked(pgStreamer::OPTICAL)?"locked":"unlocked");
	//WriteUserDataAsText("Streaming Memory (Physical:%dKb | Virtual:%dKb)\n", streamPhysical, streamVirtual);
	strStreamingAllocator& strAlloc = strStreamingEngine::GetAllocator();
	WriteUserDataAsText("Streaming Memory Occupancy :\n");
	WriteUserDataAsText("  Physical : used(%dMb) - available(%dMb)\n", strAlloc.GetPhysicalMemoryUsed_Cached()>>20, strAlloc.GetPhysicalMemoryAvailable_Cached()>>20);
	WriteUserDataAsText("  Virtual  : used(%dMb) - available(%dMb)\n", strAlloc.GetVirtualMemoryUsed_Cached()>>20, strAlloc.GetVirtualMemoryAvailable_Cached()>>20);
	WriteUserDataAsText("  External : physical used(%dMb) - virtual used (%dMb)\n\n", strAlloc.GetExternalPhysicalMemoryUsed_Cached()>>20, strAlloc.GetExternalVirtualMemoryUsed_Cached()>>20);

	//-------
	WriteUserDataAsText("============ MISC INFO ============\n");
	WriteUserDataAsText("Version : %s\n", CDebug::GetVersionNumber());
		
	// Player and Camera Pos, Game time, current vehicle/mission
	Vector3 pos   = CGameWorld::FindLocalPlayerCoors_Safe();
	Vector3 posn  = camInterface::GetPos();
	Vector3 front = camInterface::GetFront();
	WriteUserDataAsText("Player Position (%f;%f;%f)\n", pos.x, pos.y, pos.z);
	WriteUserDataAsText("Camera Position (%f;%f;%f)\n", posn.x, posn.y, posn.z);
	WriteUserDataAsText("Camera Front (%f;%f;%f)\n", front.x, front.y, front.z);
	WriteUserDataAsText("Game Time (%d:%d)\n", CClock::GetHour(), CClock::GetMinute());
	WriteUserDataAsText("Wanted Level : %d\n", CGameWorld::sm_localPlayerWanted);
	WriteUserDataAsText("IK : %u\n", CBaseIkManager::GetDebugInfoAsUInt());

	// Arrest State
	{
		char arrested[32];
		eArrestState arrestState = CGameWorld::sm_localPlayerArrestState;
		switch (arrestState)
		{
		case eArrestState::ArrestState_Arrested:	sprintf(arrested, "Arrested");	break;
		case eArrestState::ArrestState_Max:			sprintf(arrested, "Max");		break;
		case eArrestState::ArrestState_None:
		default:									sprintf(arrested, "None");		break;
		}
		WriteUserDataAsText("Arrest State : %s\n", arrested);
	}
	
	// Player Health and Death State
	{
		char deathStr[32];
		eDeathState deathState = CGameWorld::sm_localPlayerDeathState;
		switch (deathState)
		{
		case eDeathState::DeathState_Alive:			sprintf(deathStr, "Alive");		break;
		case eDeathState::DeathState_Dead:			sprintf(deathStr, "Dead");		break;
		case eDeathState::DeathState_Dying:			sprintf(deathStr, "Dying");		break;
		case eDeathState::DeathState_Max:			sprintf(deathStr, "Max");		break;
		default:									sprintf(deathStr, "Unknown");	break;
		}
		WriteUserDataAsText("Player Health : %0.2f/%0.2f\n", CGameWorld::sm_localPlayerHealth, CGameWorld::sm_localPlayerMaxHealth);
		WriteUserDataAsText("Death State : %s\n", deathStr);
	}

	// Language
	{
		char sysLanguage[32];
		u32 language = CPauseMenu::sm_languageFromSystemLanguage;
		switch (language)
		{
		case LANGUAGE_ENGLISH:				sprintf(sysLanguage, "English");			break;
		case LANGUAGE_FRENCH:				sprintf(sysLanguage, "French");				break;
		case LANGUAGE_GERMAN:				sprintf(sysLanguage, "German");				break;
		case LANGUAGE_ITALIAN:				sprintf(sysLanguage, "Italian");			break;
		case LANGUAGE_SPANISH:				sprintf(sysLanguage, "Spanish");			break;
		case LANGUAGE_PORTUGUESE:			sprintf(sysLanguage, "Portuguese");			break;
		case LANGUAGE_POLISH:				sprintf(sysLanguage, "Polish");				break;
		case LANGUAGE_RUSSIAN:				sprintf(sysLanguage, "Russian");			break;
		case LANGUAGE_KOREAN:				sprintf(sysLanguage, "Korean");				break;
		case LANGUAGE_CHINESE_TRADITIONAL:  sprintf(sysLanguage, "Chinese");			break;
		case LANGUAGE_CHINESE_SIMPLIFIED:	sprintf(sysLanguage, "ChineseSimplified");	break;
		case LANGUAGE_JAPANESE:				sprintf(sysLanguage, "Japanese");			break;
		case LANGUAGE_MEXICAN:				sprintf(sysLanguage, "Mexican");			break;
		case LANGUAGE_UNDEFINED:
		default:							sprintf(sysLanguage, "Undefined");			break;
		}
		WriteUserDataAsText("System Language : %s\n", sysLanguage);
	}

	WriteUserDataAsText("Current Mission : %s\n", (CTheScripts::GetPlayerIsOnAMission() || CTheScripts::GetPlayerIsOnARandomEvent()) ? CDebug::GetCurrentMissionName() : "None");
	WriteUserDataAsText("Current Vehicle : %s\n", CGameWorld::GetFollowPlayerCarName() ? CGameWorld::GetFollowPlayerCarName() : "None");

	// __BANK has a lot more info since we can afford it
#if __BANK
	if( CutSceneManager::GetInstance() && CutSceneManager::GetInstance()->IsRunning() )
	{
		const CutSceneManager::DebugRenderState& cutsceneState = CutSceneManager::GetDebugRenderState();
		WriteUserDataAsText("Current Cutscene : %s\n", cutsceneState.cutsceneName);
	}
	else
		WriteUserDataAsText("Current Cutscene : None\n");
	
	CDebugScene::GetDebugSummary(s_debugInfoSummary, true);
	WriteUserDataAsText("%s", s_debugInfoSummary);
#else
	//num peds
	CPed::Pool * pPedPool = CPed::GetPool();
	if (pPedPool)
	{
		int totalCount = pPedPool->GetNoOfUsedSpaces();
		int otherCount   = CPedPopulation::ms_nNumOnFootOther + CPedPopulation::ms_nNumInVehOther;
		int missionCount = 0;
		int reusedCount = 0;
		int reusePool = 0;
		for(int i=0; i<pPedPool->GetSize(); i++)
		{
			if(const CPed * pPed = pPedPool->GetSlot(i))
			{
				missionCount += (pPed->PopTypeIsMission() ? 1 : 0);
				reusedCount += (pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_PedWasReused) ? 1 : 0);
				reusePool += (pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_PedIsInReusePool) ? 1 : 0);
			}
		}

		WriteUserDataAsText("Peds: %d/%d (mission: %d, scenario (foot/car) %d/%d, ambient: (foot/car) %d/%d, other: %d, reusePool: %d) Reused: %d\n",
			totalCount, pPedPool->GetSize(), missionCount,
			CPedPopulation::ms_nNumOnFootScenario,
			CPedPopulation::ms_nNumInVehScenario,
			CPedPopulation::ms_nNumOnFootAmbient,
			CPedPopulation::ms_nNumInVehAmbient, otherCount, reusePool, reusedCount);
	}

	//num cars
	CVehicle::Pool * pVehiclePool = CVehicle::GetPool();
	if (pVehiclePool)
	{
		int iVehiclePoolSize, iTotalNumVehicles, iNumMission, iNumParked, iNumAmbient, iNumReal, iNumRealTraffic, iNumDummy, iNumSuperDummy, iNumInReusePool, iNumBeingDeleted, iNumNetRegistered, iNumUnknown; 
		iTotalNumVehicles = 0;
		iNumMission = 0;
		iNumParked = 0;
		iNumAmbient = 0;
		iNumReal = 0;
		iNumRealTraffic = 0;
		iNumDummy = 0;
		iNumSuperDummy = 0;
		iNumInReusePool = 0;
		iNumBeingDeleted = 0;
		iNumNetRegistered = 0;
		iNumUnknown = 0;
		s32 i = (s32) pVehiclePool->GetSize();
		while(i--)
		{
			CVehicle * pVehicle = pVehiclePool->GetSlot(i);
			if (pVehicle)
			{
				iTotalNumVehicles++;

				if(pVehicle->IsBaseFlagSet(fwEntity::REMOVE_FROM_WORLD))
				{
					iNumBeingDeleted++;
				}

				if(pVehicle->GetNetworkObject())
				{
					iNumNetRegistered++;
				}

				if(pVehicle->GetIsInReusePool()) 
				{
					// in reuse pool
					iNumInReusePool++;
					continue; // don't count vehicles in the reuse pool towards the population counts
				}

				if (pVehicle->PopTypeIsMission())
				{
					if (pVehicle->m_nVehicleFlags.bCountAsParkedCarForPopulation)
						iNumParked++;
					else
						iNumMission++;
				}
				else if(pVehicle->PopTypeIsRandom())
				{
					if (pVehicle->PopTypeGet() == POPTYPE_RANDOM_PARKED)
						iNumParked++;
					else
						iNumAmbient++;
				}
				else
				{
					iNumUnknown++;
				}

				switch(pVehicle->GetVehicleAiLod().GetDummyMode())
				{
				case VDM_REAL:
					{
						iNumReal++;
						if(pVehicle->PopTypeGet() == POPTYPE_RANDOM_AMBIENT || pVehicle->PopTypeGet()== POPTYPE_RANDOM_PATROL)
						{
							iNumRealTraffic++;
						}
						break;
					}
				case VDM_DUMMY: { iNumDummy++; break; }
				case VDM_SUPERDUMMY: { iNumSuperDummy++; break; }
				default: { Assert(false); }
				}

			}
		}
		iVehiclePoolSize = (int) pVehiclePool->GetSize();
		WriteUserDataAsText("Cars: %d/%d/%d (ambient: %d, mission: %d, parked: %d, reusePool: %d unknown: %d deleted: %d net: %d) (real: %d(%d traffic), dummy: %d, superdummy: %d)\n",
			iTotalNumVehicles, iVehiclePoolSize, CVehiclePopulation::GetDesiredNumberAmbientVehicles_Cached(), iNumAmbient,
			iNumMission, iNumParked, iNumInReusePool, iNumUnknown, iNumBeingDeleted, iNumNetRegistered,
			iNumReal, iNumRealTraffic, iNumDummy, iNumSuperDummy);
	}

	// Objects
	CObject::Pool* objPool = CObject::GetPool();
	if (objPool)
	{
		int totalObjects = (int) objPool->GetNoOfUsedSpaces();
		int numOfPickups, numOfDoors, numOfWeapons, numOfParachutes, numOfCarParachutes, numOfHelmets, numOfProjectiles, numOfProjectileClones, numOfProjectileThrowns, numOfOwnedByScript, numOfOwnedByFrag, numOfOwnedByTemp, numOfOwnedByRandom, numOfOwnedByGame, numBeingDeleted;
		numOfPickups = 0;
		numOfDoors = 0;
		numOfWeapons = 0;
		numOfParachutes = 0;
		numOfCarParachutes = 0;
		numOfHelmets = 0;
		numOfProjectiles = 0;
		numOfProjectileThrowns = 0;
		numOfProjectileClones = 0;
		numBeingDeleted=0;
		numOfOwnedByScript = 0;
		numOfOwnedByFrag = 0;
		numOfOwnedByTemp = 0;
		numOfOwnedByRandom = 0;
		numOfOwnedByGame = 0;

		s32 i = (s32) totalObjects;
		while(i--)
		{
			CObject * pObject = objPool->GetSlot(i);
			if (pObject)
			{
				if(pObject->IsPickup())
				{
					numOfPickups++;
				}
				if(pObject->IsADoor())
				{
					numOfDoors++;
				}
				if(pObject->GetWeapon())
				{
					numOfWeapons++;
				}
				if(pObject->GetIsParachute())
				{
					numOfParachutes++;
				}
				if(pObject->GetIsCarParachute())
				{
					numOfCarParachutes++;
				}
				if(pObject->m_nObjectFlags.bIsHelmet)
				{
					numOfHelmets++;
				}
				if(pObject->GetAsProjectile())
				{
					numOfProjectiles++;

					const CProjectile* pProjectile = static_cast<const CObject *>(pObject)->GetAsProjectile();
					if(pProjectile && pProjectile->GetNetworkIdentifier().IsClone())
					{
						numOfProjectileClones++;
					}
				}
				if(pObject->GetAsProjectileThrown())
				{
					numOfProjectileThrowns++;
				}
				if(pObject->GetOwnedBy() == ENTITY_OWNEDBY_SCRIPT)
				{
					numOfOwnedByScript++;
				}
				if(pObject->GetOwnedBy() == ENTITY_OWNEDBY_FRAGMENT_CACHE)
				{
					numOfOwnedByFrag++;
				}
				if(pObject->GetOwnedBy() == ENTITY_OWNEDBY_TEMP)
				{
					numOfOwnedByTemp++;
				}
				if(pObject->GetOwnedBy() == ENTITY_OWNEDBY_RANDOM)
				{
					numOfOwnedByRandom++;
				}
				if(pObject->GetOwnedBy() == ENTITY_OWNEDBY_GAME)
				{
					numOfOwnedByGame++;
				}
				if(pObject->IsBaseFlagSet(fwEntity::REMOVE_FROM_WORLD))
				{
					numBeingDeleted++;
				}
			}
		}

		WriteUserDataAsText("Objects: %d/%d (Pickup:(%d), Doors:(%d), Weapons:(%d), Parachutes:(%d), CarParachutes:(%d), Helmets:(%d), Projectiles:(%d/%d/%d), OwnedByScript:(%d), OwnedByFrag:(%d), OwnedByTemp:(%d), OwnedByRand:(%d), OwnedByGame:(%d), BeingDeleted:(%d)",
			totalObjects, objPool->GetSize(), numOfPickups, numOfDoors, numOfWeapons, numOfParachutes, numOfCarParachutes, numOfHelmets, numOfProjectiles, numOfProjectileThrowns, numOfProjectileClones, numOfOwnedByScript, numOfOwnedByFrag, numOfOwnedByTemp, numOfOwnedByRandom, numOfOwnedByGame, numBeingDeleted);
	}

	CDummyObject::Pool* dummyObjPool = CDummyObject::GetPool();
	if(dummyObjPool)
	{
		int totalDummy = (int) dummyObjPool->GetNoOfUsedSpaces();
		WriteUserDataAsText("Dummy Objects: %d/%d\n",
			totalDummy, dummyObjPool->GetSize());
	}

	// Display active (and total) peds, ragdolls, objects, and vehicles.
	phLevelNew* phLvl = CPhysics::GetLevel();
	if (phLvl)
	{
		s32 instPedsActive = 0;
		s32 instPedsTotal = 0;
		s32 instVehiclesActive = 0;
		s32 instVehiclesTotal = 0;
		s32 instObjectsTotal = 0;
		s32 instObjectsActive = 0;
		s32 instRagdollsActive = 0;
		s32 instRagdollsTotal = 0;
		for(int i = 0; i < phLvl->GetMaxObjects(); ++i)
		{
			if(!phLvl->IsNonexistent(i))
			{
				phInst* pInst = phLvl->GetInstance(i);
				CEntity* pEntity = CPhysics::GetEntityFromInst(pInst);
				if(pEntity && pInst->GetClassType() != PH_INST_MAPCOL)
				{
					bool bActive = phLvl->IsActive(pInst->GetLevelIndex());
					if(pEntity->GetIsTypeVehicle())
					{
						instVehiclesTotal++;
						instVehiclesActive += bActive ? 1 : 0;
					}
					if(pEntity->GetIsTypeObject())
					{
						instObjectsTotal++;
						instObjectsActive += bActive ? 1 : 0;
					}
					if(pEntity->GetIsTypePed())
					{
						instPedsTotal++;
						instPedsActive += bActive ? 1 : 0;
						if(((CPed*)pEntity)->GetRagdollState()==RAGDOLL_STATE_PHYS)
						{
							instRagdollsTotal++;
							instRagdollsActive += bActive ? 1 : 0;
						}
					}
				}
			}
		}

		WriteUserDataAsText("Physics instances (active/total): objects %d/%d, cars: %d/%d, peds: %d/%d, ragdolls: %d/%d\n",
			instObjectsActive, instObjectsTotal, instVehiclesActive, instVehiclesTotal,
			instPedsActive, instPedsTotal, instRagdollsActive, instRagdollsTotal);
	}
#endif

	// Not available in final builds :(
#if !__NO_OUTPUT
	CInteriorInst* pIntInst = NULL;
	s32 roomIdx = -1;
	pIntInst = CPortalVisTracker::GetPrimaryInteriorInst();
	roomIdx  = CPortalVisTracker::GetPrimaryRoomIdx();

	if (pIntInst && roomIdx>0 && roomIdx!=INTLOC_INVALID_INDEX)
	{
		// get the interior and room names
		CMloModelInfo *pModelInfo = reinterpret_cast<CMloModelInfo*>(pIntInst->GetBaseModelInfo());

		const char* interiorName = "Unable to find Interior";
		if (pModelInfo)
		{
			interiorName = pModelInfo->GetModelName();
		}

		const char* roomName = "Unable To Find Room";
		if (pModelInfo)
		{
			roomName = pModelInfo->GetRooms()[roomIdx].m_name;
		}

		CInteriorProxy* pInteriorProxy = pIntInst->GetProxy();
		WriteUserDataAsText("Interior:<%s (%d)>; Room:<%s (%d)>; Group:<%d>\n\n", interiorName, CInteriorProxy::GetPool()->GetJustIndex(pInteriorProxy), roomName, roomIdx, pInteriorProxy->GetGroupId());
	}
	else
	{
		WriteUserDataAsText("Exterior\n\n");
	}
#endif

	//-------
	WriteUserDataAsText("============ NETWORK INFO ============\n");
	WriteUserDataAsText("Network Opening : %s\n", NetworkInterface::IsNetworkOpening() ? "Yes" : "No");
	WriteUserDataAsText("Network Open : %s\n", NetworkInterface::IsNetworkOpen() ? "Yes" : "No");
	WriteUserDataAsText("Network Closing : %s\n", NetworkInterface::IsNetworkClosing() ? "Yes" : "No");
	WriteUserDataAsText("Network Close Pending : %s\n", NetworkInterface::IsNetworkClosePending() ? "Yes" : "No");
	WriteUserDataAsText("Network Forcing Crash On Shutdown While Processing : %s\n", NetworkInterface::IsForcingCrashOnShutdownWhileProcessing() ? "Yes" : "No");

	if (NetworkInterface::IsGameInProgress())
	{
		WriteUserDataAsText("Game In Progress : Yes\n");
		WriteUserDataAsText("Number of physical players : %d\n", NetworkInterface::GetNumPhysicalPlayers());
		// 	WriteUserDataAsText("Network session state : %d\n", CNetwork::GetNetworkSession().GetStateAsInt());
		// 	WriteUserDataAsText("Network session transition state: %d\n", CNetwork::GetNetworkSession().GetTransitionStateAsInt());
		WriteUserDataAsText("Network session valid : %s\n", CNetwork::IsNetworkSessionValid() ? "Yes" : "No");
		if(CNetwork::IsNetworkSessionValid())
		{
			WriteUserDataAsText(CNetwork::GetNetworkSession().IsSessionActive() ? "Network Session Active : Yes\n" : "Network Session Active : No\n");
			WriteUserDataAsText("Network session transition member count : %d\n", CNetwork::GetNetworkSession().GetTransitionMemberCount());
			WriteUserDataAsText("Network Session in transition : %s\n", CNetwork::GetNetworkSession().IsTransitionActive() ? "Yes" : "No");
		}
		
		s32 fDesiredNumAmbientVehicles = static_cast<s32>(CVehiclePopulation::GetDesiredNumberAmbientVehicles());
		s32	TotalAmbientCarsOnMap      = CVehiclePopulation::ms_numRandomCars;

		s32 TargetNumLocalVehicles = CNetworkObjectPopulationMgr::GetLocalTargetNumVehicles();
		s32 TotalNumLocalVehicles  = CNetworkObjectPopulationMgr::GetNumCachedLocalVehicles();

		s32 pedMemoryUse     = CPopulationStreaming::GetCachedTotalMemUsedByPeds()     >> 20;
		s32 vehicleMemoryUse = CPopulationStreaming::GetCachedTotalMemUsedByVehicles() >> 20;
		s32 pedBudget        = gPopStreaming.GetCurrentMemForPeds()                    >> 20;
		s32 vehicleBudget    = gPopStreaming.GetCurrentMemForVehicles()                >> 20;

		WriteUserDataAsText("Ped Memory Use/Budget : %d/%d\n", pedMemoryUse, pedBudget);
		WriteUserDataAsText("Vehicle Memory Use/Budget : %d/%d\n", vehicleMemoryUse, vehicleBudget);
		WriteUserDataAsText("Reassignment in progress : %s\n", netObjectReassignMgr::IsReassignmentInProgress_Cached() ? "Yes" : "No");
		WriteUserDataAsText("Total Ambient Cars On Map : %d\n", TotalAmbientCarsOnMap);
		WriteUserDataAsText("Desired Number of Ambient Vehicles : %d\n", fDesiredNumAmbientVehicles);
		WriteUserDataAsText("Total Number of Local Vehicles : %d\n", TotalNumLocalVehicles);
		WriteUserDataAsText("Target Number of Local Vehicles : %d\n\n", TargetNumLocalVehicles);
				
		netGameEvent::Pool *pEventPool = netGameEvent::GetPool();
		if (pEventPool)
		{
			s32 usedEvents = pEventPool->GetNoOfUsedSpaces();
			s32 freeSpaces = pEventPool->GetNoOfFreeSpaces();
			int total = usedEvents+freeSpaces;
			WriteUserDataAsText("Network Event Usage: %d/%d\n", usedEvents, total);
		}

		const static int NUM_EVENT_TYPES_TO_LOG = 5;

		for(int i=0; i < NUM_EVENT_TYPES_TO_LOG; i++)
		{
			if (CNetworkEventsDumpInfo::ms_eventTypesCount[i].count > 0)
			{
				WriteUserDataAsText("Network Events Type: %s  Count %d\n", CNetworkEventsDumpInfo::ms_eventTypesCount[i].eventName, CNetworkEventsDumpInfo::ms_eventTypesCount[i].count);
			}
		}

		for (auto it = CNetworkEventsDumpInfo::ms_scriptedEvents.Begin(); it != CNetworkEventsDumpInfo::ms_scriptedEvents.End(); ++it)
		{
			WriteUserDataAsText("Network Scripted Events: %s  Count %d\n", (*it).eventName, (*it).count);
		}

		for(int i = 0; i < (int)NetworkObjectTypes::NUM_NET_OBJ_TYPES; i++)
		{
			WriteUserDataAsText("Sync Tree %d", i);
			CProjectSyncTree* tree = NetworkInterface::GetObjectManager().GetSyncTree((NetworkObjectType)i);

			if (tree)
			{
				const atFixedArray<netSyncDataNode*, netSyncDataNode::MAX_SYNC_DATA_NODES>& nodes = tree->GetDataNodes();

				for (int sn = 0; sn < nodes.GetCount(); sn++)
				{
					auto dataNode = nodes[sn];
					if (dataNode)
					{
						WriteUserDataAsText("\tNode %d Updated: %s\n", sn, nodes[sn]->GetWasUpdated() ? "TRUE" : "FALSE");
					}
					else
					{
						WriteUserDataAsText("\tNode %d is null\n", sn);
					}
				}
			}
			else
			{
				WriteUserDataAsText("\tTree is null\n");
			}
		}
	}
	else
	{
		WriteUserDataAsText("Game In Progress : No\n\n");
	}

	//-------
	WriteUserDataAsText("============ SCALEFORM INFO ============\n");
	WriteUserDataAsText("Last Movie : %s\n",                      CScaleformMgr::sm_LastMovieName);
	WriteUserDataAsText("Last ActionScript Method : %s\n",        CScaleformMgr::sm_LastMethodName);
	if (CScaleformMgr::sm_LastMethodParamName)
		WriteUserDataAsText("Last ActionScript Method Params : %s\n", CScaleformMgr::sm_LastMethodParamName);
	WriteUserDataAsText("Is Inside ActionScript : %s\n", CScaleformMgr::sm_bIsInsideActionScript?"yes":"no");
	WriteUserDataAsText("Active Movies\n");
	for(s32 movieIdx=0 ; movieIdx<CScaleformMgr::sm_ScaleformArray.GetCount(); ++movieIdx)
	{
		if (CScaleformMgr::IsMovieActive(movieIdx))
			WriteUserDataAsText(" - MovieId(%3d) Filename(%s)\n", CScaleformMgr::sm_ScaleformArray[movieIdx].iMovieId, CScaleformMgr::sm_ScaleformArray[movieIdx].cFilename);
	}

	//-------
	WriteUserDataAsText("\n============ SCRIPT THREAD INFO ============\n");
	WriteUserDataAsText("Number of scrThreads : %d\n", GtaThread::CountActiveThreads());
	// Not thread safe !
// 	WriteUserDataAsText("scrThreads stack traces :\n\n");
// 	for (int threadIdx = 0; threadIdx < scrThread::GetThreadCount(); ++threadIdx)
// 	{
// 		scrThread::GetThreadByIndex(threadIdx).PrintStackTrace(ChanneledOutputForStackTrace);
// 		if (s_ThreadlStack[0] != 0)
// 		{
// 			WriteUserDataAsText(s_ThreadlStack);
// 			s_ThreadlStack[0] = 0;
// 			WriteUserDataAsText("----\n");
// 		}
// 	}

	if (scrThread::GetActiveThreadGlobal())
	{
		GtaThread *pActiveThread = (GtaThread*) scrThread::GetActiveThreadGlobal();
		WriteUserDataAsText("Active scrThread %s PC=%d Handler=0x%p State=%d\n", pActiveThread->GetScriptName(), pActiveThread->GetProgramCounter(0), pActiveThread->m_Handler, (s32) pActiveThread->GetState());
		WriteUserDataAsText("Active scrThread StackTrace :\n");
		pActiveThread->PrintStackTrace(ChanneledOutputForStackTrace);
		if (s_ThreadlStack[0] != 0)
		{
			WriteUserDataAsText(s_ThreadlStack);
			s_ThreadlStack[0] = 0;
			WriteUserDataAsText("\n");
		}
	}
	else
	{
		WriteUserDataAsText("No Active scrThread\n");
	}

	//-------
	WriteUserDataAsText("============ MOVIE INFO ============\n");
	bwMovie::CoredumpHandler(BACKTRACE_ONLY(s_crashContextHandle));

	//-------
	WriteUserDataAsText("\n###### END OF DEBUG INFO ######");

	return 0;
}
#endif //RSG_ORBIS || BACKTRACE_ENABLED

static PMCPerfControl*	s_perfControl;

// This bool is set on the end of the FsmEnter. It is used by various systems involved in tight loops
// (Warning Screen, Game Session State machine) to drive some specific rendering behaviour. Please
// be careful of side effects if modiying g_gameRunning
bool g_GameRunning = false;

CApp* CApp::sm_Self = NULL;

// A dummy sync point to be able to perform tuner captures from the main thread's point of view
// and perform loading tuner captures.
#if RSG_PC
bool CApp::IsGameRunning()
{
	return (sm_Self && (sm_Self->GetState() == State_RunGame) && camInterface::IsFadedIn() && !fwTimer::IsGamePaused()) ? true : false;
}
#endif // RSG_PC

fwFsm::Return CApp::UpdateState(const s32 state, const s32, const fwFsm::Event event)
{
	fwFsmBegin

		fwFsmState(State_InitSystem)
			fwFsmOnUpdate
				return InitSystem();

		fwFsmState(State_InitGame)
			fwFsmOnUpdate
				return InitGame();

		fwFsmState(State_RunGame)
			fwFsmOnEnter
				return RunGame_OnEnter();
			fwFsmOnUpdate
				return RunGame();

		fwFsmState(State_ShutdownGame)
			fwFsmOnUpdate
				return ShutdownGame();

		fwFsmState(State_ShutdownSystem)
			fwFsmOnUpdate
				return ShutdownSystem();

	fwFsmEnd
}

#if __PPU
void CheckGameContent()
{
	int ret = 0;
	unsigned int type = 0;
	unsigned int attributes = 0;
	CellGameContentSize content_size;

	char directoryName[CELL_GAME_DIRNAME_SIZE];

	cellSysmoduleLoadModule(CELL_SYSMODULE_SYSUTIL_GAME);

	ret = cellGameBootCheck(&type, &attributes, &content_size, directoryName);

	if (CELL_GAME_RET_OK == ret)
	{
		char contentInfoPath[CELL_GAME_PATH_MAX];
		char usrdirPath[CELL_GAME_PATH_MAX];

		ret = cellGameContentPermit(contentInfoPath, usrdirPath);

		if (CELL_GAME_RET_OK == ret)
		{
			if (CELL_GAME_GAMETYPE_HDD == type)
			{
				Displayf("contentInfoPath = %s", contentInfoPath);
				Displayf("usrdirPath = %s", usrdirPath);
				Warningf("I AM TRYING TO BOOT FROM THE HARD DRIVE.  THIS MAY NOT BE WHAT YOU WANTED.");
				Warningf("In the XMB, Debug Settings > Game Type (Debugger) should be 'Disc Boot Game' and GameContentUtil Boot Path (Debugger) should be 'For Development'.");
				if (strlen(usrdirPath) > 0)
				{
					if (usrdirPath[strlen(usrdirPath)-1] != '/')
					{
						strncat(usrdirPath, "/", CELL_GAME_PATH_MAX);
					}
				}

				CFileMgr::SetGameHddBootPath(usrdirPath);
			}
		}
		else
		{
			Assertf(0, "CheckGameContent - cellGameContentPermit failed");
		}
	}
	else
	{
		Assertf(0, "CheckGameContent - cellGameBootCheck failed");
	}

	cellSysmoduleUnloadModule(CELL_SYSMODULE_SYSUTIL_GAME);

#if !__FINAL
	if (PARAM_forceboothdd.Get())
	{
		char buf[128] = {0};
		formatf(buf, "/dev_hdd0/game/%s/USRDIR/", XEX_TITLE_ID_HDD);
		CFileMgr::SetGameHddBootPath(buf);
	}
#endif // !__FINAL
}
#endif // __PPU

// PURPOSE: Initialise the basic systems.
fwFsm::Return CApp::InitSystem()
{
#if RSG_ORBIS
	s_ThreadlStack[0] = 0;
	s_debugInfoSummary[0] = 0;
	ASSERT_ONLY( int ret = ) sceCoredumpRegisterCoredumpHandler(CoredumpHandler, sCoredumpHandlerStackSize, (void*)0);
	Assert(ret == SCE_OK);
#endif //RSG_ORBIS

	//@@: location CAPP_INITSYSTEM
#if RSG_PC && __ASSERT
	// On PC, we need to enable queueing bugs for logging as soon as possible
	diagLogBugCallback = CDebug::OnLogBug;
#endif

#if RSG_PC
	diagTerminateCallback = CApp::WriteExitFile;
#endif

#if RSG_PC && __STEAM_BUILD
	rtry
	{
		wchar_t steamPath[MAX_PATH] = {0};
		rcheck(GetModuleFileNameW(NULL, steamPath, sizeof(steamPath)), catchall, );

		wchar_t* lastSlash = wcsrchr(steamPath, '\\');
		if (lastSlash == NULL)
		{
			lastSlash = wcsrchr(steamPath, '/');
		}

		if (lastSlash != NULL)
		{
			lastSlash[1] = L'\0';
		}

		safecat(steamPath, L"steam_api64.dll");

		rverify(CertificateVerify::Verify(steamPath, NULL), catchall, gnetError("Certification Verification failed for steam"));
		
		rcheck(gnetVerifyf(SteamAPI_Init(), "SteamAPI_Init failed - Is Steam running and are you signed in to a R* developer account?"), catchall, );
		rverify(SteamUser(), catchall, gnetError("SteamUser() was null"));
		g_rlPc.RefreshSteamAuthTicket(false);
	}
	rcatchall
	{
		Errorf("Could not initialize Steam API!! Make sure you have the Steam_api64.dll in the same directory as the game and you launched the game through Steam");
	}

#endif

//#if RSG_LAUNCHER_CHECK 
	//if (!CheckParentProcess())
	//{
		// We don't want crash dumps for launcher check
//		RAGE_PC_FATAL_SILENT(ERR_NO_LAUNCHER);
	//}
//#endif

#if RAGE_TIMEBARS
	float maxAllowedTime = 0.0f;

	// If the user wants to dump timebars if the main thread takes a long time...
	if (PARAM_dumptimebarsonlongmainthread.Get())
	{
		// See if the user also specified the amount of ms that we may take.
		PARAM_dumptimebarsonlongmainthread.Get(maxAllowedTime);

		// If not, default to 200ms.
		if (!maxAllowedTime)
		{
			maxAllowedTime = 200.0f;
		}
	}
#endif // RAGE_TIMEBARS

	PF_INIT_TIMEBARS("Main", MAIN_THREAD_MAX_TIMEBARS, maxAllowedTime);

	PF_INIT_STARTUPBAR("Startup", 1000);
	PF_BEGIN_STARTUPBAR();
	PF_START_STARTUPBAR("System Init");
 
	pm_Init();
	
	CDebug::SetPrinterFn();
	// need to call this before the drawlist mgr is initialised
	InitNYDrawListDebug();

#if !__FINAL || !__NO_OUTPUT
	netRemoteLog::InitClass("gta5");
#endif

#if __PPU
	// Copied from rage\base\src\rline\rlnp.cpp
	ASSERT_ONLY(int errorCode = ) sceNp2Init(rlNp::GetSceNpPoolSize(), rlNp::GetSceNpPoolMemory());
	Assertf(errorCode == 0, "CApp::InitGame - sceNp2Init failed - error code = %x", errorCode);

	CheckGameContent();
#endif // __PPU


#if !__FINAL || !RSG_PC
	if(!CSystem::Init("Fuzzy"))
#else
	if(!CSystem::Init("Grand Theft Auto V"))
#endif
	{
		SetState(State_ShutdownGame);
		return fwFsm::Continue;
	}

#if __STEAM_BUILD
	int steamAppId = CGameConfig::Get().GetConfigOnlineServices().m_SteamAppId;
	if (SteamAPI_RestartAppIfNecessary(steamAppId))
	{
		// We don't want crash dumps for steam check
		RAGE_PC_FATAL_SILENT(ERR_NO_STEAM);
	}
#endif

	// Dongle Validation
#if ENABLE_DONGLE
#if USE_DONGLE 
	CheckDongle();
#endif
	const char* cVersionNumber = CDebug::GetVersionNumber();
	if(cVersionNumber && !strncmp(cVersionNumber, "NM", 2))
		CheckDongle();
#endif

	InitNYDrawList();

	SetState(State_InitGame);
	return fwFsm::Continue;
}

XPARAM(maponly);
XPARAM(maponlyextra);
XPARAM(cheapmode);
XPARAM(nocars);
XPARAM(nopeds);
XPARAM(noambient);
XPARAM(marketing);
XPARAM(stealextramemory);

// PURPOSE: Initialise the game systems.
fwFsm::Return CApp::InitGame()
{
	//does this need to be called? doing so here seems to remove all the 
	//time stuff tracked from CApp::InitSystem() 
    //PF_BEGIN_STARTUPBAR();

	PF_START_STARTUPBAR("PreLoadingScreensInit");
#if RSG_PC
	CGame::InitCallbacks();
#endif
	CGame::PreLoadingScreensInit();

	// map the desired platform: device
#if __PS3 && !__FINAL
	sys_memory_info_t mem_info;
	sys_memory_get_user_memory_size( &mem_info );	
	bool runningOnDevKit = mem_info.total_user_memory > 256 << 20;
#else 
	bool runningOnDevKit = !__FINAL;
#endif // __PS3


	if ( runningOnDevKit && (PARAM_maponly.Get() || PARAM_maponlyextra.Get() || PARAM_cheapmode.Get())){
#if !__FINAL && !defined(NAVGEN_TOOL)
		// alloc 2x10Mb chunks
		CFileMgr::AllocateDummyResourceMem();
		PARAM_nocars.Set("true");
		PARAM_nopeds.Set("true");
		PARAM_noambient.Set("true");
		BANK_ONLY(g_maponlyHogEnabled = true);

		if (PARAM_cheapmode.Get())
		{
			BANK_ONLY(g_cheapmodeHogEnabled = true;)
		}	

		if (PARAM_maponlyextra.Get())
		{
			CFileMgr::AllocateExtraResourceMem((10 << 20) + (4 << 20));
			BANK_ONLY(g_maponlyExtraHogEnabled = true;)
		}
#endif //!__FINAL
	}

#if !__FINAL && !defined(NAVGEN_TOOL)
	int size = 0;
	if (PARAM_stealextramemory.Get(size) && size)
	{
		CFileMgr::AllocateExtraResourceMem(size);
		BANK_ONLY(g_stealExtraMemoryEnabled = true;)
	}
#endif //!__FINAL
#if(BUGSTAR_INTEGRATION_ACTIVE)
	CBugstarIntegration::Init();
#endif

#if __XENON && __INCLUDE_MEMORY_TEST_IN_XENON_BUILD
	// Perform startup memory test if LT+RT+LB+RB are held down on either pad
	CMemoryTest::StartTest();
#endif

#if TRACING_STATS
	gTracingStats.Init();
#endif

	PF_START_STARTUPBAR("CText init");
	CText::Init(INIT_CORE);			
	
	PF_START_STARTUPBAR("CLoadingScreens init");
	CLoadingScreens::Init( LOADINGSCREEN_CONTEXT_LEGALMAIN, 0);
	
	if (!StreamingInstall::HasInstallFinished())
	{
		CWarningScreen::Init(INIT_CORE);
		CGameStreamMgr::Init(INIT_CORE);

		PF_PUSH_STARTUPBAR("Streaming install");
        StreamingInstall::BlockUntilFinished();
        CFileMgr::CleanupAsyncInstall();
		PF_POP_STARTUPBAR();
	}
#if RSG_DURANGO
	else
	{
		// to shutdown the resume watcher and package transfer watcher started at init.
		// Durango only, to avoid messing with other platforms (B*2564598)
		StreamingInstall::Shutdown();
	}
#endif	// RSG_DURANGO

	PF_START_STARTUPBAR("Game Init");
	CGame::Init();

#if RSG_PC
	//Allow device resets to occur now that the game is fully initialized.  Request a devcie reset through the settings manager to ensure that all game properties are properly initialized.
//	GRCDEVICE.SetIgnoreDeviceReset(false);
//	CSettingsManager::GetInstance().RequestNewSettings(CSettingsManager::GetInstance().GetCopyOfSettings());
#endif

	SetState(State_RunGame);
	return fwFsm::Continue;
}


// PURPOSE: Game initialiation before game loop
fwFsm::Return CApp::RunGame_OnEnter()
{
	//////////////////////////////////////////////////////////////////////////
	// required for various Sony TRC bugs related to deleting of trophy data
	// and attempting to re-unlock trophies. This could all go away if we stop
	// caching trophy bits in the saved game profile.
#if RSG_NP
	// wait until trophy status is known (installed or not) before loading profile
	rlNpTrophy::WaitUntilTrophyStatusIsKnown();

	// write out profile if the trophy data has been re-installed on startup (due to being missing)
	if (rlNpTrophy::GetHasTrophyDataBeenReinstalled())
	{
		CLiveManager::GetAchMgr().ClearAchievements();
		CProfileSettings::GetInstance().WriteNow(true);
		rlNpTrophy::SetHasTrophyDataBeenReinstalled(false);
	}
#endif
	//////////////////////////////////////////////////////////////////////////

	// This shouldn't be here, we've already called this function much much sooner than this and we're throwing out nearly 30s of data.
	// PF_BEGIN_STARTUPBAR();

	CGame::UpdateSessionState();

#if !__FINAL && RAGE_TRACKING
	const char* trackerReport = NULL;
	if (PARAM_trackerreport.Get(trackerReport))
		RAGE_TRACK_REPORT_NAME(trackerReport);
#endif

	RAGETRACE_INIT();
	RAGETRACE_INITTHREAD("Main", 1024, 1);

	//gRenderThreadInterface.Init();		// create the render thread and let it take control of the d3d device

	PF_START_STARTUPBAR("Setup devices");
	CFileMgr::SetupDevicesAfterInit();

#if __PPU
	struct std::malloc_managed_size mms;
	std::malloc_stats(&mms);
	if (mms.current_system_size > 0) {
		Errorf("**** current system usage %dK; in use is %dk",mms.current_system_size>>10,mms.current_inuse_size>>10);
	}
#endif

	s_perfControl = rage_new PMCPerfControl();

#if !__FINAL
	Printf("Game init took %f seconds\n", initTimer.GetTime());
#endif

#if !__FINAL
	for(s32 bucket = 0; bucket < 16; ++bucket)
	{
		sysMemAllocator::SetBucketName(bucket, sysMemGetBucketName((MemoryBucketIds)bucket));
	}
#endif

	sysMemAllocator::GetMaster().GetAllocator(MEMTYPE_GAME_VIRTUAL)->UpdateMemorySnapshot();
	sysMemAllocator::GetMaster().GetAllocator(MEMTYPE_RESOURCE_VIRTUAL)->UpdateMemorySnapshot();
	sysMemAllocator::GetMaster().GetAllocator(MEMTYPE_GAME_PHYSICAL)->UpdateMemorySnapshot();
	sysMemAllocator::GetMaster().GetAllocator(MEMTYPE_RESOURCE_PHYSICAL)->UpdateMemorySnapshot();

	g_GameRunning = true;

	PF_FINISH_STARTUPBAR();

	SetInstance(this);
#if RSG_PC
	grcResourceCache::SetGameRunningCallback(IsGameRunning);

	GRCDEVICE.SetDisablePauseOnLostFocusSystemOverride(false);
	GRCDEVICE.LockDeviceResetAvailability();

	if (g_rlPc.GetGamepadManager())
	{
#if __FINAL
		g_rlPc.GetGamepadManager()->SetBackgroundInputEnabled(!GRCDEVICE.IsAllowPauseOnFocusLoss());
#else
		bool bGameWillPauseOnFocusLoss = GRCDEVICE.IsAllowPauseOnFocusLoss() && PARAM_BlockOnLostFocus.Get();
		g_rlPc.GetGamepadManager()->SetBackgroundInputEnabled(!bGameWillPauseOnFocusLoss);
#endif
	}
#endif // RSG_PC

	return fwFsm::Continue;
}


// PURPOSE: Main game loop.
fwFsm::Return CApp::RunGame()
	{
#if RAGE_TIMEBARS
		if (!PARAM_disableframemarkers.Get())
		{
			char buffer[32];
			formatf(buffer, "Frame %u", fwTimer::GetFrameCount());
			pfTimebarFuncs::g_PushMarkerCb(buffer, 0);
		}
#endif // RAGE_TIMEBARS

#if STALL_DETECTION
		if (!	(	CPauseMenu::IsActive()
			||	camInterface::IsFadedOut() 
			||	CLoadingScreens::AreActive()
			)
			)
		{
			m_StallDetection.BeginFrame();
		}
#endif // STALL_DETECTION

#if RAGE_TRACKING
		if ( PARAM_autoMemTrack.Get() )
		{	
			static int loopCount = 0;
			
			if( loopCount == 60 )
			{
				diagTracker* t = diagTracker::GetCurrent();
				
				if( t )
				{
					((diagTrackerRemote2*)t)->RequestDump();
				}
			}
			
			loopCount++;
		}
#endif


		PF_FRAMEINIT_TIMEBARS(gDrawListMgr->GetUpdateThreadFrameNumber());
		RAGETRACE_FRAMESTART();
		//@@: location CAPP_RUNGAME
		PF_PUSH_TIMEBAR_BUDGETED("System begin", 0.1f);
		s_perfControl->StartFrame();

#if RAGE_TIMEBARS
		// Indicate what kind of build this is in PIX/Tuner, so we now what's going on when looking at captures.
		{
			char buildType[256];
			char version[32];

			formatf(buildType, RSG_CONFIGURATION " %s %s", CDebug::GetPackageType(), CDebug::GetFullVersionString(version, sizeof(version)));
			pfAutoMarker versionMarker(buildType, 6);
		}
#endif // RAGE_TIMEBARS

#if GTA_REPLAY
		CReplayMgr::StartFrame();
#endif // GTA_REPLAY

#if __PPU && __DEV
		snSafeLoadPoint();
#endif

		RAGETRACE_PUSHNAME("CSystem::BeginUpdate");
		CSystem::BeginUpdate();
		RAGETRACE_POP();

#if (SECTOR_TOOLS_EXPORT)
		CSectorTools::Run( );
#endif // OBJECT_CHECKER_EXPORT
		PF_POP_TIMEBAR(); // "System begin"

		CGame::Update();

#if DEBUG_SCREENSHOTS
		PF_START_TIMEBAR_DETAIL("Screenshots");
		CDebug::HandleScreenShotRequests();
#endif

		pm_Display();

		//***RENDERTHREAD LOOP********************************************
		CTaskScheduler::StartBatchingTasks(5);

		gRenderThreadInterface.GPU_IdleSection();		// call stuff which needs done when GPU is idle
		gRenderThreadInterface.Synchronise();

		CControlMgr::StartUpdateThread();
		CGame::Render();		// it's called render, but it won't really - it just builds the draw lists & contexts
		CControlMgr::WaitForUpdateThread();
		//******************************************************

#if USE_TELEMETRY
		CTelemetry::Update();
		TelemetryUpdate();
#endif

		//@@: location CAPP_RUNGAME_AUDIO_WAIT
		PF_PUSH_TIMEBAR_BUDGETED("AudioWait", 1.0f);
		if (!CSavegameAutoload::IsPerformingAutoLoadAtStartOfGame()
			UI_LANDING_PAGE_ONLY(&& !CLandingPageArbiter::IsLandingPageActive())
			)
		{
			audNorthAudioEngine::FinishUpdate();
		}
		PF_POP_TIMEBAR();

#if GTA_REPLAY
		if(audDriver::GetMixer() && audDriver::GetMixer()->IsCapturing() && audDriver::GetMixer()->IsFrameRendering())
		{
			const float kfFrameStep = 1.0f / 31.0f;
			// Only trigger an extra engine update when exporting at 30fps or less to make sure enough audio is generated this frame
			if(audDriver::GetMixer()->GetTotalAudioTimeNs() < audDriver::GetMixer()->GetTotalVideoTimeNs() && CReplayMgr::GetExportFrameStep() >= kfFrameStep)
			{
				g_AudioEngine.TriggerUpdate();
				g_AudioEngine.WaitForAudioFrame();
			}

			CReplayPlaybackController const& c_replayPlaybackController = CReplayCoordinator::GetReplayPlaybackController();

			while (audDriver::GetMixer()->GetTotalAudioTimeNs() < audDriver::GetMixer()->GetTotalVideoTimeNs() && c_replayPlaybackController.IsExportingAudioFrameCaptureAllowed())
			{
				audDriver::GetMixer()->EnterReplaySwitchLock();
				audDriver::GetMixer()->TriggerUpdate();
				audDriver::GetMixer()->WaitOnMixBuffers();
				audDriver::GetMixer()->ExitReplaySwitchLock();
				//Displayf("Catch Up Audio: %LLu chasing (%LLu)", 
				//	NANOSECONDS_TO_ONE_H_NS_UNITS(audDriver::GetMixer()->GetTotalAudioTimeNs()), 
				//	NANOSECONDS_TO_ONE_H_NS_UNITS(audDriver::GetMixer()->GetTotalVideoTimeNs()));			
			}
		}

		if(audDriver::GetMixer()->m_ShouldStartFixedFrameRender)
		{
			audDriver::GetMixer()->m_ShouldStartFixedFrameRender = false;
			audDriver::GetMixer()->SetReplayMixerModeRendering();
		}
		
		if(audDriver::GetMixer()->m_ShouldStopFixedFrameRender)
		{
			audDriver::GetMixer()->m_ShouldStopFixedFrameRender = false;
			audDriver::GetMixer()->SetReplayMixerModeLoading();
		}
#endif

#if __DEV
		CGameWorld::AllowDelete(true);
#endif

#if GTA_REPLAY
		PF_PUSH_TIMEBAR_IDLE("ReplayMgrPostRender");
		CReplayMgr::PostRender();
		PF_POP_TIMEBAR();
#endif
		s_perfControl->EndFrame();

#if TRACING_STATS
		gTracingStats.Process();
#endif

#if RSG_PC
		CApp::CheckExit();
#else
		if (CApp::WantToExit())
		{
			SetState(State_ShutdownGame);
		}
#endif


#if __PS3 && !__FINAL
	pfDummySync();
#endif // __PS3 && !__FINAL

	RAGETRACE_PUSHNAME("CSystem::EndUpdate");
	CSystem::EndUpdate();
	RAGETRACE_POP();

	m_StallDetection.EndFrame();

#if RAGE_TIMEBARS
	if (!PARAM_disableframemarkers.Get())
	{
		pfTimebarFuncs::g_PopMarkerCb(0);
	}
#endif // RAGE_TIMEBARS

	return fwFsm::Continue;
}

#if RSG_PC

static bool sbDisabledScui = false;
static bool sbClosedScui = false;
static bool sbWarningAccept = false;
static bool sbWarningTriggered = false;

void GracefullyShutdownNetwork()
{
#if __STEAM_BUILD
	if (SteamUser())
	{
		CSteamID steamId = SteamUser()->GetSteamID();
		SteamUser()->EndAuthSession(steamId);
	}
#endif

	CNetwork::Bail(sBailParameters(BAIL_EXIT_GAME, BAIL_CTX_EXIT_GAME_FROM_APP));
	CNetwork::ShutdownConnectionManager();
	rlShutdown();
	netShutdown();
}

void ProcessScuiCloseLogic()
{
	if(g_rlPc.GetUiInterface())
	{
		if (!sbClosedScui && g_rlPc.GetUiInterface()->IsVisible())
		{
			Displayf("Closing SCUI for Quit Game message");
			g_rlPc.GetUiInterface()->CloseUi();
			sbClosedScui = true;
		}

		if (!sbDisabledScui)
		{
			Displayf("Disabling SCUI for Quit Game message");
			g_rlPc.GetUiInterface()->EnableHotkey(false);
			sbDisabledScui = true;
		}
	}
}

bool CApp::IsShutdownConfirmed()
{
	return sbWarningAccept || CPauseMenu::GetGameWantsToExitToWindows();
}

bool CApp::IsScuiDisabledForShutdown()
{
	return sbDisabledScui || sbClosedScui || sbWarningTriggered;
}

void CApp::CheckExit()
{
	// ===========================================
	//   PROCESSING THE STARTING OF THE EXIT FLOW 
	// ===========================================
	if (GRCDEVICE.QueryCloseWindow() || g_rlPc.HasUserRequestedShutdownViaScui() || CSystem::WantToRestart())
	{
		if(!NetworkInterface::GetNetworkExitFlow().IsExitFlowRunning())
		{
			// Network game is in progress, start the exit flow
			if(NetworkInterface::IsGameInProgress())
			{
				NetworkInterface::GetNetworkExitFlow().StartExitSaveFlow();
			}
			// If network game is not in progress, the user may have requested a restart. Thus, they have already
			// accepted the warning
			else if (CSystem::WantToRestart())
			{
				sbWarningTriggered = false;
				sbWarningAccept = true;
			}
			// Otherwise just go for straight to the warning message
			else
			{
				#if RSG_PC
				if (PARAM_benchmark.Get())
				{
					sbWarningTriggered = false;
					sbWarningAccept = true;
				}
				else
				#endif
				{
					sbWarningTriggered = true;
				}
			}
		}

		// If we have yet to close the SCUI, but the interface is valid and visible, close it and disable reopening.
		ProcessScuiCloseLogic();
	}

	// ===========================================
	// PROCESSING THE UI WARNINGS OF THE EXIT FLOW
	// ===========================================
	// If the exit flow is running and it hasn't finished, display the spinner warning
	if (NetworkInterface::GetNetworkExitFlow().IsSaveFlowRunning() && !NetworkInterface::GetNetworkExitFlow().IsSaveFlowFinished())
	{
		// Game Restart comes from the Pause Menu which the user has already accepted
		if (CSystem::WantToRestart())
		{
			CWarningScreen::SetControllerReconnectMessageWithHeader(WARNING_MESSAGE_STANDARD, "WARNING_EXIT_WINDOWS2", NetworkInterface::GetNetworkExitFlow().GetExitString(), FE_WARNING_SPINNER);
		}
		else
		{
			CWarningScreen::SetControllerReconnectMessageWithHeader(WARNING_MESSAGE_STANDARD, "WARNING_EXIT_WINDOWS2", "EXIT_SURE_2", FE_WARNING_SPINNER);
		}
	}
	else if (NetworkInterface::GetNetworkExitFlow().IsShutdownTasksRunning() && !NetworkInterface::GetNetworkExitFlow().IsReadyToShutdown())
	{
		CWarningScreen::SetControllerReconnectMessageWithHeader(WARNING_MESSAGE_STANDARD, "WARNING_EXIT_WINDOWS2", NetworkInterface::GetNetworkExitFlow().GetExitString(), FE_WARNING_SPINNER);
	}
	else if (NetworkInterface::GetNetworkExitFlow().IsReadyToShutdown())
	{
		CWarningScreen::SetControllerReconnectMessageWithHeader(WARNING_MESSAGE_STANDARD, "WARNING_EXIT_WINDOWS2", NetworkInterface::GetNetworkExitFlow().GetExitString(), FE_WARNING_SPINNER);
		sbWarningAccept = true;
	}

	// ===========================================
	//    PROCESSING THE INPUT OF THE EXIT FLOW
	// ===========================================
	if (NetworkInterface::GetNetworkExitFlow().IsSaveFlowFinished() || sbWarningTriggered)
	{
		// If we have yet to close the SCUI, but the interface is valid and visible, close it and disable reopening.
		ProcessScuiCloseLogic();

		if(sbWarningTriggered)
		{
			// Entitlement Manager worker thread is not safe, and must be allowed to finish.
#if RSG_ENTITLEMENT_ENABLED
			if (CEntitlementManager::IsReadyToShutdown())
			{
				CWarningScreen::SetControllerReconnectMessageWithHeader(WARNING_MESSAGE_STANDARD, "WARNING_EXIT_WINDOWS2", "EXIT_SURE_2", FE_WARNING_YES_NO);
			}
			else
#endif
			{
				CWarningScreen::SetControllerReconnectMessageWithHeader(WARNING_MESSAGE_STANDARD, "WARNING_EXIT_WINDOWS2", "EXIT_SURE_2", FE_WARNING_SPINNER);
			}
		}
		else if (CSystem::WantToRestart())
		{
			NetworkInterface::GetNetworkExitFlow().StartShutdownTasks();
		}
		else
		{
			// Warning messages for Network game, based on whether the save succeeded
			if(NetworkInterface::GetNetworkExitFlow().GetExitFlowState() == NetworkExitFlow::ETD_SAVE_SUCCESS)
			{
				CWarningScreen::SetControllerReconnectMessageWithHeader(WARNING_MESSAGE_STANDARD, "WARNING_EXIT_WINDOWS2", "EXIT_SURE_2", FE_WARNING_YES_NO);
			}
			else if(NetworkInterface::GetNetworkExitFlow().GetExitFlowState() == NetworkExitFlow::ETD_SAVE_FAILED)
			{
				CWarningScreen::SetControllerReconnectMessageWithHeader(WARNING_MESSAGE_STANDARD, "SG_HDNG", "SG_FAILED_ON_QUIT", FE_WARNING_YES_NO);
			}
			else
			{
				Assertf(0, "Exit State is incorrect");
			}
		}

		eWarningButtonFlags result = CWarningScreen::CheckAllInput(false);

		if (result == FE_WARNING_YES)
		{
			sbWarningTriggered = false;
			GRCDEVICE.ClearCloseWindow();

			NetworkInterface::GetNetworkExitFlow().StartShutdownTasks();
		}

		if (result == FE_WARNING_NO)
		{
			sbWarningTriggered = false;
			sbWarningAccept = false;
			GRCDEVICE.ClearCloseWindow();

			// Clear SCUI shutdown flags
			sbClosedScui = false;
			sbDisabledScui = false;
			g_rlPc.SetUserRequestedShutdownViaScui(false);

			// Re-Enable SCUI hotkey
			if(g_rlPc.GetUiInterface())
			{
				g_rlPc.GetUiInterface()->EnableHotkey(true);
			}

			// Reset Network shutdown
			if(NetworkInterface::GetNetworkExitFlow().IsExitFlowRunning())
			{
				NetworkInterface::GetNetworkExitFlow().ResetExitFlowState();
			}
		}
	}

	if (CSystem::WantToExit())
	{
		GRCDEVICE.ClearCloseWindow();
	}

	// ===========================================
	//    PROCESSING THE RESTART/SHUT DOWN
	// ===========================================
	if (IsShutdownConfirmed() && !NetworkInterface::GetNetworkExitFlow().IsShutdownTasksRunning())
	{
		bool bWantRestart = false;
		if (CSystem::WantToRestart())
		{
			if (g_rlPc.GetProfileManager() != NULL && g_rlPc.GetProfileManager()->IsSignedIn())
			{
				g_rlPc.GetProfileManager()->CreateSigninTransferProfile();
			}

			if (g_rlPc.GetDownloaderPipe() && g_rlPc.GetDownloaderPipe()->IsConnected())
			{
				g_rlPc.GetDownloaderPipe()->RestartGame();
			}
			else
			{
				bWantRestart = true;
			}
		}

#if USE_RAGESEC
		// By now the tasks and plugins should be canceled but there is a case where
		// not all are so this will help with that. Two updates for good measure
		if (rageSecPluginManager::CancelTasks())
		{
			netTask::Update();
			netTask::Update();
		}
#endif

		GracefullyShutdownNetwork();

#if USE_RAGESEC
		// Turns out on PC we just call Exit(), and I want to do proper memory 
		// sanitization / cleanup / DLL-handle-freeing, so I need to inject
		// myself and shutdown rageSecEngine
		rageSecEngine::Shutdown(rage::SHUTDOWN_CORE);
#endif

		CGenericGameStorage::FinishAllUnfinishedSaveGameOperations();

		WriteExitFile(NULL);

		if (bWantRestart)
		{
			Restart();
		}

		ExitProcess(0);
	}
}

void CApp::Restart()
{
#if RSG_PC
	const int MODULE_FILENAME_LENGTH = 1024;

	TCHAR strModuleFileName[MODULE_FILENAME_LENGTH];

	DWORD dwResult = GetModuleFileName(NULL, strModuleFileName, MODULE_FILENAME_LENGTH);

	if ((dwResult > 0) && (dwResult < MODULE_FILENAME_LENGTH))
	{
		LPTSTR lpCommandLine = GetCommandLine();
		LPTSTR lpCommandLineArg = strstr(lpCommandLine, ".exe");

		if (lpCommandLineArg == NULL)
		{
			lpCommandLineArg = strstr(lpCommandLine, ".EXE");
		}

		if (lpCommandLineArg)
		{
			lpCommandLineArg += 4;

			if (*lpCommandLineArg == '\"')
			{
				lpCommandLineArg++;
			}
		}

		SHELLEXECUTEINFO oShellExecuteInfo;

		memset(&oShellExecuteInfo, 0, sizeof(SHELLEXECUTEINFO));

		oShellExecuteInfo.cbSize = sizeof(SHELLEXECUTEINFO);
		oShellExecuteInfo.lpVerb = "open";
		oShellExecuteInfo.lpFile = strModuleFileName;
		oShellExecuteInfo.lpParameters = lpCommandLineArg;
		oShellExecuteInfo.nShow = SW_SHOW;

		ShellExecuteEx(&oShellExecuteInfo);
	}
#endif RSG_PC
}

void CApp::WriteExitFile(const char* error)
{
	static bool s_bOpenedExitFile = false;

	// If we've created the out-file already, and have no additional info, there's nothing to do
	if (s_bOpenedExitFile && !error)
	{
		return;
	}

	// Grab the current pid
	DWORD pid = GetCurrentProcessId();
	if (pid == 0)
	{
		return;
	}

	// Check for an in-file
	char path_buffer[MAX_PATH] = {0};
	int formattedLength = snprintf(path_buffer, MAX_PATH, "%s\\in-%u", CFileMgr::GetAppDataRootDirectory(), (unsigned int)pid);
	if (formattedLength >= MAX_PATH)
	{
		return;
	}

	if (!ASSET.Exists(path_buffer, ""))
	{
		// No in-file; we're probably not being run from the launcher
		return;
	}

	// Create the full file path
	formattedLength = snprintf(path_buffer, MAX_PATH, "%s\\out-%u", CFileMgr::GetAppDataRootDirectory(), (unsigned int)pid);

	if (formattedLength >= MAX_PATH)
	{
		return;
	}

	FileHandle handle = NULL;
	
	// If we've already written an out-file this session, append rather than re-create
	if (s_bOpenedExitFile)
	{
		handle = CFileMgr::OpenFileForAppending(path_buffer);
	}
	else
	{
		handle = CFileMgr::OpenFileForWriting(path_buffer);
	}

	if (handle != NULL)
	{
		s_bOpenedExitFile = true;

		// If we have an error, write it out to the out-file
		if (error != NULL)
		{
			CFileMgr::Write(handle, error, static_cast<s32>(strlen(error)));
		}

		CFileMgr::CloseFile(handle);
	}

}

#endif

// PURPOSE: Shutdown game systems.
fwFsm::Return CApp::ShutdownGame()
{
#if __PPU
	// Copied from rage\base\src\rline\rlnp.cpp
	sceNp2Term();
#endif // __PPU

	delete s_perfControl;
	s_perfControl = NULL;
	CGame::Shutdown();
	SetState(State_ShutdownSystem);

	return fwFsm::Continue;
}


// PURPOSE: Shutdown low level systems.
fwFsm::Return CApp::ShutdownSystem()
{
#if RSG_ORBIS
	ASSERT_ONLY( int ret = ) sceCoredumpUnregisterCoredumpHandler();
	Assert(ret == SCE_OK);
#endif //RSG_ORBIS

	CSystem::Shutdown();

#if !__FINAL || !__NO_OUTPUT
	netRemoteLog::ShutdownClass();
#endif

#if TRACING_STATS
	gTracingStats.Shutdown();
#endif

	Displayf("Goodbye");
	return fwFsm::Quit;
}



// PURPOSE: Check whether some system has indicated we need to shutdown.
bool CApp::WantToExit()
{
	return CSystem::WantToExit();
}

// PURPOSE: Check for a valid dongle.
void CApp::CheckDongle()
{
#if ENABLE_DONGLE
	char debugString [500];
	bool checkAuthenticity = true;

#if __PPU || RSG_ORBIS || RSG_DURANGO
	checkAuthenticity = CDongle::ValidateCodeFile(sEncodeString, debugString, sMCFileName);
#else
	checkAuthenticity = ioDongle::ReadCodeFile(sEncodeString, sMCFileName, debugString);
#endif // __PPU

	if(!checkAuthenticity)
	{
		Quitf("Auth fail!");
		while(1){} // Endless Loop
	}
#endif //ENABLE_DONGLE
}

//#if RSG_LAUNCHER_CHECK
//bool CApp::CheckParentProcess()
//{
//	PROCESSENTRY32	procentry;
//	HMODULE			hMod ;
//	char			szFileName[ MAX_PATH ] ;
//	DWORD			dwSize2 = 0, pid = 0, crtpid = 0, parentpid = 0;
//	BOOL			bContinue = FALSE;
//	bool			bConfirmed = false;
//	HANDLE			hSnapShot, hProcess;

//	atString		launcherProcess;
//	atString		bootstrapProcess;
//	atString		mtlProcess;
//	atString		mtlSteamHelper;

//	crtpid = GetCurrentProcessId();

//	if (crtpid == 0)
//	{
//		Errorf("Could not get process ID from the game.");
//		return false;
//	}

//	hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
//	if (hSnapShot == INVALID_HANDLE_VALUE)
//	{
//		Errorf("Could not get a valid snapshot of the process.");
//		return false;
//	}

//	procentry.dwSize = sizeof(PROCESSENTRY32);
//	bContinue = Process32First(hSnapShot, &procentry);
//	if (!bContinue)
//	{
//		Errorf("Could not get the process information from the snapshot.");
//		return false;
//	}

//	while (bContinue)
//	{
//		if (crtpid == procentry.th32ProcessID)
//			pid = procentry.th32ParentProcessID;

//		procentry.dwSize = sizeof(PROCESSENTRY32);
//		bContinue = !pid && Process32Next(hSnapShot, &procentry);
//	}

//	parentpid = pid;
//	hProcess = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, parentpid);

//	if (!hProcess)
//	{
//		Errorf("Could not do OpenProcess on parent process.");
//		return false;
//	}

//	launcherProcess = CHECK_PARENT_PROCESS;
//	launcherProcess.Lowercase();

//	bootstrapProcess = CHECK_BOOTSTRAP_PROCESS;
//	bootstrapProcess.Lowercase();
	
//	mtlProcess = CHECK_MTL_PROCESS;
//	mtlProcess.Lowercase();

//	mtlSteamHelper = CHECK_MTL_STEAMHELPER;
//	mtlSteamHelper.Lowercase();

//	bConfirmed = false;
//	if (EnumProcessModules(hProcess, &hMod, sizeof(hMod), &dwSize2))
//	{
//		if (GetModuleFileNameEx(hProcess, hMod, szFileName, sizeof(szFileName)))
//		{
//			const char* exeName = ASSET.FileName(szFileName);
//			atString currentName(exeName);
//			currentName.Lowercase();

//			if (currentName == launcherProcess ||
//				currentName == bootstrapProcess ||
//				currentName == mtlProcess ||
//				currentName == mtlSteamHelper)
//			{
//				bConfirmed = true;
//			}
//		}
//	}

//	CloseHandle(hProcess);

//	if (!bConfirmed)
//	{
//		Errorf("Parent process name does not match the one we are looking for: %s or %s", launcherProcess.c_str(), bootstrapProcess.c_str());
//	}

//	return bConfirmed;

//}

//#endif

#if BACKTRACE_ENABLED && (RSG_PC || RSG_DURANGO)
void CApp::WriteCrashContextLog(const wchar_t* path)
{
	s_crashContextHandle = CreateFileW(path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
	if (s_crashContextHandle != INVALID_HANDLE_VALUE)
	{
		CoredumpHandler(NULL);
		CloseHandle(s_crashContextHandle);
		s_crashContextHandle = INVALID_HANDLE_VALUE;
	}
}

void CApp::CollectAdditionalAttributes()
{
	u32 currentTime = fwTimer::GetSystemTimeInMilliseconds();

	// Game length
	sysException::SetAnnotation("game_lifetime", currentTime);
	// Last Launched Script
	rage::sysException::BacktraceScriptData lastLaunchedScript = sysException::BacktraceGetLastLaunchedScriptData();
	sysException::SetAnnotation("last_launched_script_id", lastLaunchedScript.m_LastLaunchedScriptProgramId);
	sysException::SetAnnotation("time_since_last_launched_script", currentTime - lastLaunchedScript.m_TimeSinceLastLaunchedScript);

	// Network info
	const char* networkState = "unknown";
	if (NetworkInterface::IsGameInProgress())
	{
		networkState = "game_in_progress";
	}
	else if (NetworkInterface::IsNetworkOpening())
	{
		networkState = "opening";
	}
	else if (NetworkInterface::IsNetworkOpen())
	{
		networkState = "open";
	}
	else if (NetworkInterface::IsNetworkClosing())
	{
		networkState = "closing";
	}
	else if (NetworkInterface::IsNetworkClosePending())
	{
		networkState = "close_pending";
	}
	sysException::SetAnnotation("network_state", networkState);

	// Number of players
	int activePlayers;
	if (!NetworkInterface::IsInAnyMultiplayer())
	{
		// We are not in a Multiplayer session so player count is either 0 or 1,
		// If the ped factory has not been init yet, then we are 0
		// Otherwise it depends if we can find a local player
		if(CPedFactory::GetFactory() == NULL)
		{
			activePlayers = 0;
		}
		else if(CGameWorld::FindLocalPlayer())
		{
			activePlayers = 1;
		}
		else
		{
			activePlayers = 0;
		}
	}
	else
	{
		activePlayers = NetworkInterface::GetNumActivePlayers();
	}
	sysException::SetAnnotation("active_players", activePlayers);

	// Ped Entity Count
	sysException::SetAnnotation("entity_count_peds", CPed::GetPool() ? CPed::GetPool()->GetNoOfUsedSpaces() : 0);

	// Vehicles Entity Count
	s32 totalVehicles = 0;
	CVehicle::Pool* pVehiclePool = CVehicle::GetPool();
	if (pVehiclePool)
	{
		s32 i = (s32)pVehiclePool->GetSize();
		while (i--)
		{
			if (pVehiclePool->GetSlot(i))
			{
				++totalVehicles;
			}
		}
	}
	sysException::SetAnnotation("entity_count_vehicles", totalVehicles);

	// Objects Entity Count
	sysException::SetAnnotation("entity_count_objects", CObject::GetPool() ? CObject::GetPool()->GetNoOfUsedSpaces() : 0);

#if RSG_SCARLETT
	const char* gfxModes[] = {
		"Performance",
		"Quality",
		"Performance RT 1440",
		"Performance RT",
	};
	sysException::SetAnnotation("rendering_mode", gfxModes[CSettingsManager::GetInstance().GetPriority()]);
#endif // RSG_SCARLETT
}
#endif //BACKTRACE_ENABLED && (RSG_PC || RSG_XBOX)