//----------------------------------------------------------------------------- // 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; }