//-----------------------------------------------------------------------------
//	Purpose: Open the user settings container for the current mod
//-----------------------------------------------------------------------------
uint CXboxSystem::CreateUserSettingsContainer( uint nCreationFlags )
{
	if ( XBX_GetStorageDeviceId() == XBX_INVALID_STORAGE_ID || XBX_GetStorageDeviceId() == XBX_STORAGE_DECLINED )
		return ERROR_INVALID_HANDLE;

	// Don't allow any of our saves or user data to be transferred to another user
	nCreationFlags |= XCONTENTFLAG_NOPROFILE_TRANSFER;

	XCONTENT_DATA contentData;
	contentData.DeviceID = XBX_GetStorageDeviceId();
	contentData.dwContentType = XCONTENTTYPE_SAVEDGAME;
	Q_wcsncpy( contentData.szDisplayName, g_pVGuiLocalize->Find( "#GameUI_Console_UserSettings" ), sizeof( contentData.szDisplayName ) );
	Q_snprintf( contentData.szFileName, sizeof( contentData.szFileName ), "UserSettings" );

	SIZE_T dwFileCacheSize = 0; // Use the smallest size (default)
	ULARGE_INTEGER ulSize;
	ulSize.QuadPart = XBX_USER_SETTINGS_BYTES;

	int nRet = XContentCreateEx( XBX_GetPrimaryUserId(), XBX_USER_SETTINGS_CONTAINER_DRIVE, &contentData, nCreationFlags, NULL, NULL, dwFileCacheSize, ulSize, NULL );
	if ( nRet == ERROR_SUCCESS )
	{
		BOOL bUserIsCreator = false;
		XContentGetCreator( XBX_GetPrimaryUserId(), &contentData, &bUserIsCreator, NULL, NULL );
		if( bUserIsCreator == false )
		{
			XContentClose( XBX_USER_SETTINGS_CONTAINER_DRIVE, NULL );
			return ERROR_ACCESS_DENIED;
		}
	}

	return nRet;
}
//-----------------------------------------------------------------------------
// Purpose: Space free on the current device
//-----------------------------------------------------------------------------
uint CXboxSystem::GetContainerRemainingSpace( void )
{
	XDEVICE_DATA deviceData;
	if ( XContentGetDeviceData( XBX_GetStorageDeviceId(), &deviceData ) != ERROR_SUCCESS )
		return 0;

	return deviceData.ulDeviceFreeBytes;
}
//-----------------------------------------------------------------------------
// Purpose: Returns the filename to count map loads in
//-----------------------------------------------------------------------------
bool UTIL_GetMapLoadCountFileName( const char *pszFilePrependName, char *pszBuffer, int iBuflen )
{
	if ( IsX360() )
	{
#ifdef _X360
		if ( XBX_GetStorageDeviceId() == XBX_INVALID_STORAGE_ID || XBX_GetStorageDeviceId() == XBX_STORAGE_DECLINED )
			return false;
#endif
	}

	if ( IsX360() )
	{
		Q_snprintf( pszBuffer, iBuflen, "%s:/%s", MAP_KEY_FILE_DIR, pszFilePrependName );
	}
	else
	{
		Q_snprintf( pszBuffer, iBuflen, "%s/%s", MAP_KEY_FILE_DIR, pszFilePrependName );
	}

	return true;
}
//-----------------------------------------------------------------------------
//	Purpose: Open the save game container for the current mod
//-----------------------------------------------------------------------------
uint CXboxSystem::CreateSavegameContainer( uint nCreationFlags )
{
	if ( XBX_GetStorageDeviceId() == XBX_INVALID_STORAGE_ID || XBX_GetStorageDeviceId() == XBX_STORAGE_DECLINED )
		return ERROR_INVALID_HANDLE;

	// Don't allow any of our saves or user data to be transferred to another user
	nCreationFlags |= XCONTENTFLAG_NOPROFILE_TRANSFER;

	const wchar_t *pchContainerDisplayName;
	const char *pchContainerName;
	g_pXboxSystem->GetModSaveContainerNames( GetCurrentMod(), &pchContainerDisplayName, &pchContainerName );

	XCONTENT_DATA contentData;
	contentData.DeviceID = XBX_GetStorageDeviceId();
	contentData.dwContentType = XCONTENTTYPE_SAVEDGAME;
	Q_wcsncpy( contentData.szDisplayName, pchContainerDisplayName, sizeof ( contentData.szDisplayName ) );
	Q_snprintf( contentData.szFileName, sizeof( contentData.szFileName ), pchContainerName );

	SIZE_T dwFileCacheSize = 0; // Use the smallest size (default)
	ULARGE_INTEGER ulSize;
	ulSize.QuadPart = XBX_PERSISTENT_BYTES_NEEDED;

	int nRet = XContentCreateEx( XBX_GetPrimaryUserId(), GetCurrentMod(), &contentData, nCreationFlags, NULL, NULL, dwFileCacheSize, ulSize, NULL );
	if ( nRet == ERROR_SUCCESS )
	{
		BOOL bUserIsCreator = false;
		XContentGetCreator( XBX_GetPrimaryUserId(), &contentData, &bUserIsCreator, NULL, NULL );
		if( bUserIsCreator == false )
		{
			XContentClose( GetCurrentMod(), NULL );
			return ERROR_ACCESS_DENIED;
		}
	}

	return nRet;
}
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
bool CTFStatPanel::ReadStats( void )
{
	CDmxElement *pPlayerStats;

	DECLARE_DMX_CONTEXT();

	if ( IsX360() )
	{
#ifdef _X360
		if ( XBX_GetStorageDeviceId() == XBX_INVALID_STORAGE_ID || XBX_GetStorageDeviceId() == XBX_STORAGE_DECLINED )
			return false;
#endif
	}

	char	szFilename[_MAX_PATH];

	if ( IsX360() )
	{
		Q_snprintf( szFilename, sizeof( szFilename ), "cfg:/tf2_playerstats.dmx" );
	}
	else
	{
		Q_snprintf( szFilename, sizeof( szFilename ), "tf2_playerstats.dmx" );
	}

	MEM_ALLOC_CREDIT();

	bool bOk = UnserializeDMX( szFilename, "MOD", true, &pPlayerStats );

	if ( !bOk )
		return false;

	int iVersion = pPlayerStats->GetValue< int >( "iVersion" );
	if ( iVersion > PLAYERSTATS_FILE_VERSION )
	{
		// file is beyond our comprehension
		return false;
	}

	int iSteamID = pPlayerStats->GetValue<int>( "SteamID" );
	int iCRCFile = pPlayerStats->GetValue<int>( "iTimestamp" );	
	
	const CUtlVector< CDmxElement* > &aClassStatsList = pPlayerStats->GetArray< CDmxElement * >( "aClassStats" );
	int iCount = aClassStatsList.Count();
	m_aClassStats.SetCount( iCount );
	for( int i = 0; i < m_aClassStats.Count(); i++ )
	{
		CDmxElement *pClass = aClassStatsList[ i ];
		ClassStats_t &stat = m_aClassStats[ i ];

		pClass->UnpackIntoStructure( &stat, s_ClassStatsUnpack );

		CDmxElement *pAccumulated = pClass->GetValue< CDmxElement * >( "accumulated" );
		pAccumulated->UnpackIntoStructure( &stat.accumulated, s_RoundStatsUnpack );

		CDmxElement *pMax = pClass->GetValue< CDmxElement * >( "max" );
		pMax->UnpackIntoStructure( &stat.max, s_RoundStatsUnpack );
	}

	CleanupDMX( pPlayerStats );

	UpdateStatSummaryPanel();

	// check file CRC and steam ID to see if we think this file has not been tampered with
	int iCRC = CalcCRC( iSteamID );
	// does file CRC match CRC generated from file data, and is there a Steam ID in the file
	if ( ( iCRC == iCRCFile ) && ( iSteamID > 0 ) && SteamUser() ) 
	{		
		// does the file Steam ID match current Steam ID (so you can't hand around files)
		CSteamID steamID = SteamUser()->GetSteamID();
		if ( steamID.GetAccountID() == (uint32) iSteamID )
		{
			m_bLocalFileTrusted = true;
		}
	}

	m_bStatsChanged = false;

	return true;
}
//-----------------------------------------------------------------------------
// Purpose: Writes stat file.  Used as primary storage for X360.  For PC,
//			Steam is authoritative but we write stat file for debugging (although
//			we never read it).
//-----------------------------------------------------------------------------
void CTFStatPanel::WriteStats( void )
{
	if ( !m_bStatsChanged )
		return;

	MEM_ALLOC_CREDIT();

	DECLARE_DMX_CONTEXT();
	CDmxElement *pPlayerStats = CreateDmxElement( "PlayerStats" );
	CDmxElementModifyScope modify( pPlayerStats );

	// get Steam ID.  If not logged into Steam, use 0
	int iSteamID = 0;
	if ( SteamUser() )
	{
		CSteamID steamID = SteamUser()->GetSteamID();
		iSteamID = steamID.GetAccountID();
	}	
	// Calc CRC of all data to make the local data file somewhat tamper-resistant
	int iCRC = CalcCRC( iSteamID );

	pPlayerStats->SetValue( "iVersion", static_cast<int>( PLAYERSTATS_FILE_VERSION ) );
	pPlayerStats->SetValue( "SteamID", iSteamID );	
	pPlayerStats->SetValue( "iTimestamp", iCRC );	// store the CRC with a non-obvious name

	CDmxAttribute *pClassStatsList = pPlayerStats->AddAttribute( "aClassStats" );
	CUtlVector< CDmxElement* >& classStats = pClassStatsList->GetArrayForEdit<CDmxElement*>();

	modify.Release();

	for( int i = 0; i < m_aClassStats.Count(); i++ )
	{
		const ClassStats_t &stat = m_aClassStats[ i ];

		// strip out any garbage class data
		if ( ( stat.iPlayerClass > TF_LAST_NORMAL_CLASS ) || ( stat.iPlayerClass < TF_FIRST_NORMAL_CLASS ) )
			continue;

		CDmxElement *pClass = CreateDmxElement( "ClassStats_t" );
		classStats.AddToTail( pClass );

		CDmxElementModifyScope modifyClass( pClass );

		pClass->SetValue( "comment: classname", g_aPlayerClassNames_NonLocalized[ stat.iPlayerClass ] );
		pClass->AddAttributesFromStructure( &stat, s_ClassStatsUnpack );
		
		CDmxElement *pAccumulated = CreateDmxElement( "RoundStats_t" );
		pAccumulated->AddAttributesFromStructure( &stat.accumulated, s_RoundStatsUnpack );
		pClass->SetValue( "accumulated", pAccumulated );

		CDmxElement *pMax = CreateDmxElement( "RoundStats_t" );
		pMax->AddAttributesFromStructure( &stat.max, s_RoundStatsUnpack );
		pClass->SetValue( "max", pMax );
	}

	if ( IsX360() )
	{
#ifdef _X360
		if ( XBX_GetStorageDeviceId() == XBX_INVALID_STORAGE_ID || XBX_GetStorageDeviceId() == XBX_STORAGE_DECLINED )
			return;
#endif
	}

	char szFilename[_MAX_PATH];

	if ( IsX360() )
		Q_snprintf( szFilename, sizeof( szFilename ), "cfg:/tf2_playerstats.dmx" );
	else
		Q_snprintf( szFilename, sizeof( szFilename ), "tf2_playerstats.dmx" );

	{
		MEM_ALLOC_CREDIT();
		CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
		if ( SerializeDMX( buf, pPlayerStats, szFilename ) )
		{
			filesystem->WriteFile( szFilename, "MOD", buf );
		}
	}

	CleanupDMX( pPlayerStats );

	if ( IsX360() )
	{
		xboxsystem->FinishContainerWrites();
	}

	m_bStatsChanged = false;
}
//=========================================================
//=========================================================
bool C_GameInstructor::WriteSaveData()
{
	if ( engine->IsPlayingDemo() )
		return false;
 
	if ( !m_bDirtySaveData )
		return true;
 
#ifdef _X360
	float flPlatTime = Plat_FloatTime();
 
	static ConVarRef host_write_last_time( "host_write_last_time" );
	if ( host_write_last_time.IsValid() )
	{
		float flTimeSinceLastWrite = flPlatTime - host_write_last_time.GetFloat();
		if ( flTimeSinceLastWrite < 3.5f )
		{
			// Prevent writing to the same storage device twice in less than 3 second succession for TCR success!
			// This happens after leaving a game in splitscreen.
			//DevMsg( "Waiting to write Game Instructor for splitscreen slot %d... (%.1f seconds remain)\n", m_nSplitScreenSlot, 3.5f - flTimeSinceLastWrite );
			return false;
		}
	}
#endif
 
	// Always mark as clean state to avoid re-entry on
	// subsequent frames when storage device might be
	// in a yet-unmounted state.
	m_bDirtySaveData = false;
 
#ifdef _X360
	DevMsg( "Write Game Instructor for splitscreen slot %d at time: %.1f\n", m_nSplitScreenSlot, flPlatTime );
 
	if ( m_nSplitScreenSlot < 0 )
		return false;
 
	if ( m_nSplitScreenSlot >= (int) XBX_GetNumGameUsers() )
		return false;
 
	int iController = XBX_GetUserId( m_nSplitScreenSlot );
 
	if ( iController < 0 || XBX_GetUserIsGuest( iController ) )
	{
		// Can't save data for guests
		return false;
	}
 
	DWORD nStorageDevice = XBX_GetStorageDeviceId( iController );
	if ( !XBX_DescribeStorageDevice( nStorageDevice ) )
		return false;
#endif
 
	// Build key value data to save
	KeyValues *data = new KeyValues( "Game Instructor Counts" );
	KeyValues::AutoDelete autoDelete(data);
 
	for ( int i = 0; i < m_Lessons.Count(); ++i )
	{
		CBaseLesson *pLesson = m_Lessons[i];
 
		int iDisplayCount = pLesson->GetDisplayCount();
		int iSuccessCount = pLesson->GetSuccessCount();
 
		if ( iDisplayCount || iSuccessCount )
		{
			// We've got some data worth saving
			KeyValues *pKVData = new KeyValues( pLesson->GetName() );
 
			if ( iDisplayCount )
				pKVData->SetInt( "display", iDisplayCount );
 
			if ( iSuccessCount )
				pKVData->SetInt( "success", iSuccessCount );
 
			data->AddSubKey( pKVData );
		}
	}
 
	// Save it!
	CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
 
	data->RecursiveSaveToFile( buf, 0 );
 
	char	szFilename[_MAX_PATH];
 
#ifdef _X360
	if ( IsX360() )
	{
		XBX_MakeStorageContainerRoot( iController, XBX_USER_SETTINGS_CONTAINER_DRIVE, szFilename, sizeof( szFilename ) );
		int nLen = strlen( szFilename );
		Q_snprintf( szFilename + nLen, sizeof( szFilename ) - nLen, ":\\game_instructor_counts.txt" );
	}
	else
#endif
	{
		Q_snprintf( szFilename, sizeof( szFilename ), "save/game_instructor_counts.txt" );
		filesystem->CreateDirHierarchy( "save", "MOD" );
	}
 
	bool bWriteSuccess = filesystem->WriteFile( szFilename, MOD_DIR, buf );
 
#ifdef _X360
	if ( xboxsystem )
	{
		xboxsystem->FinishContainerWrites( iController );
	}
#endif
 
	return bWriteSuccess;
}
//=========================================================
//=========================================================
bool C_GameInstructor::ReadSaveData()
{
	// for external playtests, don't ever read in persisted instructor state, always start fresh
	if ( CommandLine()->FindParm( "-playtest" ) )
		return true;
 
	if ( m_bHasLoadedSaveData )
		return true;
 
	// Always reset state first in case storage device
	// was declined or ends up in faulty state
	ResetDisplaysAndSuccesses();
 
	m_bHasLoadedSaveData = true;
 
#ifdef _X360
	DevMsg( "Read Game Instructor for splitscreen slot %d\n", m_nSplitScreenSlot );
 
	if ( m_nSplitScreenSlot < 0 )
		return false;
 
	if ( m_nSplitScreenSlot >= (int) XBX_GetNumGameUsers() )
		return false;
 
	int iController = XBX_GetUserId( m_nSplitScreenSlot );
 
	if ( iController < 0 || XBX_GetUserIsGuest( iController ) )
	{
		// Can't read data for guests
		return false;
	}
 
	DWORD nStorageDevice = XBX_GetStorageDeviceId( iController );
	if ( !XBX_DescribeStorageDevice( nStorageDevice ) )
		return false;
#endif
 
	char szFilename[_MAX_PATH];
 
#ifdef _X360
	if ( IsX360() )
	{
		XBX_MakeStorageContainerRoot( iController, XBX_USER_SETTINGS_CONTAINER_DRIVE, szFilename, sizeof( szFilename ) );
		int nLen = strlen( szFilename );
		Q_snprintf( szFilename + nLen, sizeof( szFilename ) - nLen, ":\\game_instructor_counts.txt" );
	}
	else
#endif
	{
		Q_snprintf( szFilename, sizeof( szFilename ), "save/game_instructor_counts.txt" );
	}
 
	KeyValues *data = new KeyValues( "Game Instructor Counts" );
	KeyValues::AutoDelete autoDelete(data);
 
	if ( data->LoadFromFile( g_pFullFileSystem, szFilename, NULL ) )
	{
		int nVersion = 0;
 
		for ( KeyValues *pKey = data->GetFirstSubKey(); pKey; pKey = pKey->GetNextTrueSubKey() )
		{
			CBaseLesson *pLesson = GetLesson_Internal( pKey->GetName() );
 
			if ( pLesson )
			{
				pLesson->SetDisplayCount( pKey->GetInt( "display", 0 ) );
				pLesson->SetSuccessCount( pKey->GetInt( "success", 0 ) );
 
				if ( Q_strcmp( pKey->GetName(), "version number" ) == 0 )
				{
					nVersion = pLesson->GetSuccessCount();
				}
			}
		}
 
		CBaseLesson *pLessonVersionNumber = GetLesson_Internal( "version number" );
		if ( pLessonVersionNumber && !pLessonVersionNumber->IsLearned() )
		{
			ResetDisplaysAndSuccesses();
			pLessonVersionNumber->SetSuccessCount( pLessonVersionNumber->GetSuccessLimit() );
			m_bDirtySaveData = true;
		}
 
 
		return true;
	}
 
	// Couldn't read from the file
	return false;
}