void AchievementSet::LoadProgress( const char* sLoadStateFilename ) { char buffer[4096]; long nFileSize = 0; unsigned int CondNumHits[50]; // 50 conditions per achievement unsigned int CondSourceVal[50]; unsigned int CondSourceLastVal[50]; unsigned int CondTargetVal[50]; unsigned int CondTargetLastVal[50]; unsigned int nID = 0; unsigned int nNumCond = 0; char cheevoProgressString[4096]; unsigned int i = 0; unsigned int j = 0; char* pGivenProgressMD5 = NULL; char* pGivenCheevoMD5 = NULL; char cheevoMD5TestMangled[4096]; int nMemStringLen = 0; if( !RAUsers::LocalUser().IsLoggedIn() ) return; if( sLoadStateFilename == NULL ) return; sprintf_s( buffer, 4096, "%s%s.rap", RA_DIR_DATA, sLoadStateFilename ); char* pRawFile = _MallocAndBulkReadFileToBuffer( buffer, nFileSize ); if( pRawFile != NULL ) { unsigned int nOffs = 0; while( nOffs < (unsigned int)(nFileSize-2) && pRawFile[nOffs] != '\0' ) { char* pIter = &pRawFile[nOffs]; // Parse achievement id and num conditions nID = strtol( pIter, &pIter, 10 ); pIter++; nNumCond = strtol( pIter, &pIter, 10 ); pIter++; // Concurrently build the md5 checkstring sprintf_s( cheevoProgressString, 4096, "%d:%d:", nID, nNumCond ); ZeroMemory( CondNumHits, 50*sizeof(unsigned int) ); ZeroMemory( CondSourceVal, 50*sizeof(unsigned int) ); ZeroMemory( CondSourceLastVal, 50*sizeof(unsigned int) ); ZeroMemory( CondTargetVal, 50*sizeof(unsigned int) ); ZeroMemory( CondTargetLastVal, 50*sizeof(unsigned int) ); for( i = 0; i < nNumCond && i < 50; ++i ) { // Parse next condition state CondNumHits[i] = strtol( pIter, &pIter, 10 ); pIter++; CondSourceVal[i] = strtol( pIter, &pIter, 10 ); pIter++; CondSourceLastVal[i] = strtol( pIter, &pIter, 10 ); pIter++; CondTargetVal[i] = strtol( pIter, &pIter, 10 ); pIter++; CondTargetLastVal[i] = strtol( pIter, &pIter, 10 ); pIter++; // Concurrently build the md5 checkstring sprintf_s( buffer, 4096, "%d:%d:%d:%d:%d:", CondNumHits[i], CondSourceVal[i], CondSourceLastVal[i], CondTargetVal[i], CondTargetLastVal[i] ); strcat_s( cheevoProgressString, 4096, buffer ); } // Read the given md5: pGivenProgressMD5 = strtok_s( pIter, ":", &pIter ); pGivenCheevoMD5 = strtok_s( pIter, ":", &pIter ); // Regenerate the md5 and see if it sticks: sprintf_s( cheevoMD5TestMangled, 4096, "%s%s%s%d", RAUsers::LocalUser().Username().c_str(), cheevoProgressString, RAUsers::LocalUser().Username().c_str(), nID ); std::string sRecalculatedProgressMD5 = RAGenerateMD5( cheevoMD5TestMangled ); if( sRecalculatedProgressMD5.compare( pGivenProgressMD5 ) == 0 ) { // Embed in achievement: Achievement* pAch = Find( nID ); if( pAch != NULL ) { std::string sMemStr = pAch->CreateMemString(); // Recalculate the current achievement to see if it's compatible: std::string sMemMD5 = RAGenerateMD5( sMemStr ); if( sMemMD5.compare( 0, 32, pGivenCheevoMD5 ) == 0 ) { for( size_t nGrp = 0; nGrp < pAch->NumConditionGroups(); ++nGrp ) { for( j = 0; j < pAch->NumConditions( nGrp ); ++j ) { Condition& cond = pAch->GetCondition( nGrp, j ); cond.OverrideCurrentHits( CondNumHits[ j ] ); cond.CompSource().SetValues( CondSourceVal[ j ], CondSourceLastVal[ j ] ); cond.CompTarget().SetValues( CondTargetVal[ j ], CondTargetLastVal[ j ] ); pAch->SetDirtyFlag( Dirty_Conditions ); } } } else { ASSERT( !"Achievement progress savestate incompatible (achievement has changed?)" ); RA_LOG( "Achievement progress savestate incompatible (achievement has changed?)" ); } } else { ASSERT( !"Achievement doesn't exist!" ); RA_LOG( "Achievement doesn't exist!" ); } } else { //assert(!"MD5 invalid... what to do... maybe they're trying to hack achievements?"); } nOffs = (pIter - pRawFile); } free( pRawFile ); pRawFile = NULL; } }
void AchievementSet::SaveProgress( const char* sSaveStateFilename ) { if( !RAUsers::LocalUser().IsLoggedIn() ) return; if( sSaveStateFilename == NULL ) return; SetCurrentDirectory( Widen( g_sHomeDir ).c_str() ); char buffer[ 4096 ]; sprintf_s( buffer, 4096, "%s.rap", sSaveStateFilename ); FILE* pf = NULL; fopen_s( &pf, buffer, "w" ); if( pf == NULL ) { ASSERT( !"Could not save progress!" ); return; } for( size_t i = 0; i < NumAchievements(); ++i ) { Achievement* pAch = &m_Achievements[i]; if( !pAch->Active() ) continue; // Write ID of achievement and num conditions: char cheevoProgressString[4096]; memset( cheevoProgressString, '\0', 4096 ); for( unsigned int nGrp = 0; nGrp < pAch->NumConditionGroups(); ++nGrp ) { sprintf_s( buffer, "%d:%d:", pAch->ID(), pAch->NumConditions( nGrp ) ); strcat_s( cheevoProgressString, 4096, buffer ); for( unsigned int j = 0; j < pAch->NumConditions( nGrp ); ++j ) { Condition& cond = pAch->GetCondition( nGrp, j ); sprintf_s( buffer, 4096, "%d:%d:%d:%d:%d:", cond.CurrentHits(), cond.CompSource().RawValue(), cond.CompSource().RawPreviousValue(), cond.CompTarget().RawValue(), cond.CompTarget().RawPreviousValue() ); strcat_s( cheevoProgressString, 4096, buffer ); } } // Generate a slightly different key to md5ify: char sCheevoProgressMangled[4096]; sprintf_s( sCheevoProgressMangled, 4096, "%s%s%s%d", RAUsers::LocalUser().Username().c_str(), cheevoProgressString, RAUsers::LocalUser().Username().c_str(), pAch->ID() ); std::string sMD5Progress = RAGenerateMD5( std::string( sCheevoProgressMangled ) ); std::string sMD5Achievement = RAGenerateMD5( pAch->CreateMemString() ); fwrite( cheevoProgressString, sizeof(char), strlen(cheevoProgressString), pf ); fwrite( sMD5Progress.c_str(), sizeof(char), sMD5Progress.length(), pf ); fwrite( ":", sizeof(char), 1, pf ); fwrite( sMD5Achievement.c_str(), sizeof(char), sMD5Achievement.length(), pf ); fwrite( ":", sizeof(char), 1, pf ); // Check! } fclose( pf ); }