void Dlg_GameLibrary::RefreshList() { std::map<std::string, std::string>::iterator iter = Results.begin(); while (iter != Results.end()) { const std::string& filepath = iter->first; const std::string& md5 = iter->second; if (VisibleResults.find(filepath) == VisibleResults.end()) { // Not yet added, if (m_GameHashLibrary.find(md5) != m_GameHashLibrary.end()) { // Found in our hash library! const GameID nGameID = m_GameHashLibrary[md5]; RA_LOG("Found one! Game ID %d (%s)", nGameID, m_GameTitlesLibrary[nGameID].c_str()); const std::string& sGameTitle = m_GameTitlesLibrary[nGameID]; AddTitle(sGameTitle, filepath, nGameID); SetDlgItemText(m_hDialogBox, IDC_RA_SCANNERFOUNDINFO, NativeStr(sGameTitle).c_str()); VisibleResults[filepath] = md5; // Copy to VisibleResults } } iter++; } }
void Dlg_GameLibrary::KillThread() { Dlg_GameLibrary::ThreadProcessingAllowed = false; while (Dlg_GameLibrary::ThreadProcessingActive) { RA_LOG("Waiting for background scanner..."); Sleep(200); } }
BOOL AchievementSet::Unlock( AchievementID nAchID ) { for( size_t i = 0; i < NumAchievements(); ++i ) { if( m_Achievements[ i ].ID() == nAchID ) { m_Achievements[ i ].SetActive( FALSE ); return TRUE; // Update Dlg? //TBD } } RA_LOG( "Attempted to unlock achievement %d but failed!\n", nAchID ); return FALSE;//?? }
HBITMAP LoadLocalPNG( const std::string& sPath, const RASize& sz ) { SetCurrentDirectory( Widen( g_sHomeDir ).c_str() ); ASSERT( _FileExists( sPath ) ); if( !_FileExists( sPath ) ) { RA_LOG( "File could not be found: %s\n", sPath.c_str() ); return nullptr; } HBITMAP hRetVal = nullptr; // Step 2: Decode the source image to IWICBitmapSource IWICBitmapDecoder* pDecoder = nullptr; HRESULT hr = g_UserImageFactoryInst.m_pIWICFactory->CreateDecoderFromFilename( Widen( sPath ).c_str(), // Image to be decoded nullptr, // Do not prefer a particular vendor GENERIC_READ, // Desired read access to the file WICDecodeMetadataCacheOnDemand, // Cache metadata when needed &pDecoder ); // Pointer to the decoder // Retrieve the first frame of the image from the decoder IWICBitmapFrameDecode* pFrame = nullptr; if( SUCCEEDED( hr ) ) hr = pDecoder->GetFrame( 0, &pFrame ); // Retrieve IWICBitmapSource from the frame if( SUCCEEDED( hr ) ) { SAFE_RELEASE( g_UserImageFactoryInst.m_pOriginalBitmapSource ); //##SD ??? pFrame->QueryInterface( IID_IWICBitmapSource, reinterpret_cast<void**>( &g_UserImageFactoryInst.m_pOriginalBitmapSource ) ); } // Step 3: Scale the original IWICBitmapSource to the client rect size // and convert the pixel format IWICBitmapSource* pToRenderBitmapSource = nullptr; if( SUCCEEDED( hr ) ) hr = ConvertBitmapSource( { 0, 0, sz.Width(), sz.Height() }, pToRenderBitmapSource ); // Step 4: Create a DIB from the converted IWICBitmapSource if( SUCCEEDED( hr ) ) hr = UserImageFactory_CreateDIBSectionFromBitmapSource( pToRenderBitmapSource, hRetVal ); SAFE_RELEASE( pToRenderBitmapSource ); SAFE_RELEASE( pDecoder ); SAFE_RELEASE( pFrame ); SAFE_RELEASE( g_UserImageFactoryInst.m_pOriginalBitmapSource ); return hRetVal; }
INT_PTR CALLBACK Dlg_GameLibrary::GameLibraryProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_INITDIALOG: { HWND hList = GetDlgItem(hDlg, IDC_RA_LBX_GAMELIST); SetupColumns(hList); ListView_SetExtendedListViewStyle(hList, LVS_EX_FULLROWSELECT | LVS_EX_HEADERDRAGDROP); SetDlgItemText(hDlg, IDC_RA_ROMDIR, NativeStr(g_sROMDirLocation).c_str()); SetDlgItemText(hDlg, IDC_RA_GLIB_NAME, TEXT("")); m_GameHashLibrary.clear(); m_GameTitlesLibrary.clear(); m_ProgressLibrary.clear(); ParseGameHashLibraryFromFile(m_GameHashLibrary); ParseGameTitlesFromFile(m_GameTitlesLibrary); ParseMyProgressFromFile(m_ProgressLibrary); //int msBetweenRefresh = 1000; // auto? //SetTimer( hDlg, 1, msBetweenRefresh, (TIMERPROC)g_GameLibrary.s_GameLibraryProc ); RefreshList(); return FALSE; } case WM_TIMER: if ((g_GameLibrary.GetHWND() != NULL) && (IsWindowVisible(g_GameLibrary.GetHWND()))) RefreshList(); //ReloadGameListData(); return FALSE; case WM_NOTIFY: switch (LOWORD(wParam)) { case IDC_RA_LBX_GAMELIST: { switch (((LPNMHDR)lParam)->code) { case LVN_ITEMCHANGED: { //RA_LOG( "Item Changed\n" ); HWND hList = GetDlgItem(hDlg, IDC_RA_LBX_GAMELIST); const int nSel = ListView_GetSelectionMark(hList); if (nSel != -1) { TCHAR buffer[1024]; ListView_GetItemText(hList, nSel, 1, buffer, 1024); SetWindowText(GetDlgItem(hDlg, IDC_RA_GLIB_NAME), buffer); } } break; case NM_CLICK: //RA_LOG( "Click\n" ); break; case NM_DBLCLK: if (LaunchSelected()) { EndDialog(hDlg, TRUE); return TRUE; } break; default: break; } } return FALSE; default: RA_LOG("%08x, %08x\n", wParam, lParam); return FALSE; } case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: if (LaunchSelected()) { EndDialog(hDlg, TRUE); return TRUE; } else { return FALSE; } case IDC_RA_RESCAN: ReloadGameListData(); mtx.lock(); //? SetDlgItemText(m_hDialogBox, IDC_RA_SCANNERFOUNDINFO, TEXT("Scanning...")); mtx.unlock(); return FALSE; case IDC_RA_PICKROMDIR: g_sROMDirLocation = GetFolderFromDialog(); RA_LOG("Selected Folder: %s\n", g_sROMDirLocation.c_str()); SetDlgItemText(hDlg, IDC_RA_ROMDIR, NativeStr(g_sROMDirLocation).c_str()); return FALSE; case IDC_RA_LBX_GAMELIST: { HWND hList = GetDlgItem(hDlg, IDC_RA_LBX_GAMELIST); const int nSel = ListView_GetSelectionMark(hList); if (nSel != -1) { TCHAR sGameTitle[1024]; ListView_GetItemText(hList, nSel, 1, sGameTitle, 1024); SetWindowText(GetDlgItem(hDlg, IDC_RA_GLIB_NAME), sGameTitle); } } return FALSE; case IDC_RA_REFRESH: RefreshList(); return FALSE; default: return FALSE; } case WM_PAINT: if (nNumParsed != Results.size()) nNumParsed = Results.size(); return FALSE; case WM_CLOSE: EndDialog(hDlg, FALSE); return TRUE; case WM_USER: return FALSE; default: return FALSE; } }
void Dlg_GameLibrary::ScanAndAddRomsRecursive(const std::string& sBaseDir) { char sSearchDir[2048]; sprintf_s(sSearchDir, 2048, "%s\\*.*", sBaseDir.c_str()); WIN32_FIND_DATA ffd; HANDLE hFind = FindFirstFile(NativeStr(sSearchDir).c_str(), &ffd); if (hFind != INVALID_HANDLE_VALUE) { unsigned int ROM_MAX_SIZE = 6 * 1024 * 1024; unsigned char* sROMRawData = new unsigned char[ROM_MAX_SIZE]; do { if (KEYDOWN(VK_ESCAPE)) break; memset(sROMRawData, 0, ROM_MAX_SIZE); //?!?? const std::string sFilename = Narrow(ffd.cFileName); if (strcmp(sFilename.c_str(), ".") == 0 || strcmp(sFilename.c_str(), "..") == 0) { // Ignore 'this' } else if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { RA_LOG("Directory found: %s\n", ffd.cFileName); std::string sRecurseDir = sBaseDir + "\\" + sFilename.c_str(); ScanAndAddRomsRecursive(sRecurseDir); } else { LARGE_INTEGER filesize; filesize.LowPart = ffd.nFileSizeLow; filesize.HighPart = ffd.nFileSizeHigh; if (filesize.QuadPart < 2048 || filesize.QuadPart > ROM_MAX_SIZE) { // Ignore: wrong size RA_LOG("Ignoring %s, wrong size\n", sFilename.c_str()); } else { // Parse as ROM! RA_LOG("%s looks good: parsing!\n", sFilename.c_str()); char sAbsFileDir[2048]; sprintf_s(sAbsFileDir, 2048, "%s\\%s", sBaseDir.c_str(), sFilename.c_str()); HANDLE hROMReader = CreateFile(NativeStr(sAbsFileDir).c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hROMReader != INVALID_HANDLE_VALUE) { BY_HANDLE_FILE_INFORMATION File_Inf; int nSize = 0; if (GetFileInformationByHandle(hROMReader, &File_Inf)) nSize = (File_Inf.nFileSizeHigh << 16) + File_Inf.nFileSizeLow; DWORD nBytes = 0; BOOL bResult = ReadFile(hROMReader, sROMRawData, nSize, &nBytes, NULL); const std::string sHashOut = RAGenerateMD5(sROMRawData, nSize); if (m_GameHashLibrary.find(sHashOut) != m_GameHashLibrary.end()) { const unsigned int nGameID = m_GameHashLibrary[std::string(sHashOut)]; RA_LOG("Found one! Game ID %d (%s)", nGameID, m_GameTitlesLibrary[nGameID].c_str()); const std::string& sGameTitle = m_GameTitlesLibrary[nGameID]; AddTitle(sGameTitle, sAbsFileDir, nGameID); SetDlgItemText(m_hDialogBox, IDC_RA_GLIB_NAME, NativeStr(sGameTitle).c_str()); InvalidateRect(m_hDialogBox, nullptr, TRUE); } CloseHandle(hROMReader); } } } } while (FindNextFile(hFind, &ffd) != 0); delete[](sROMRawData); sROMRawData = NULL; FindClose(hFind); } SetDlgItemText(m_hDialogBox, IDC_RA_SCANNERFOUNDINFO, TEXT("Scanning complete")); }
void RA_RichPresenceInterpretter::ParseRichPresenceFile( const char* sFilename ) { m_formats.clear(); m_lookups.clear(); m_sDisplay.clear(); const char EndLine = '\n'; const char* LookupStr = "Lookup:"; const char* FormatStr = "Format:"; const char* FormatTypeStr = "FormatType="; const char* DisplayableStr = "Display:"; FILE* pFile = NULL; fopen_s( &pFile, sFilename, "r" ); if( pFile != NULL ) { DWORD nCharsRead = 0; char buffer[4096]; _ReadTil( EndLine, buffer, 4096, &nCharsRead, pFile ); while( nCharsRead != 0 ) { if( strncmp( LookupStr, buffer, strlen( LookupStr ) ) == 0 ) { // Lookup type char* pBuf = buffer + ( strlen( LookupStr ) ); RA_Lookup newLookup( _ReadStringTil( EndLine, pBuf, TRUE ) ); while( nCharsRead != 0 && ( buffer[0] != EndLine ) ) { _ReadTil( EndLine, buffer, 4096, &nCharsRead, pFile ); if( nCharsRead > 2 ) { char* pBuf = &buffer[0]; const char* pValue = _ReadStringTil( '=', pBuf, TRUE ); const char* pName = _ReadStringTil( EndLine, pBuf, TRUE ); int nBase = 10; if( pValue[0] == '0' && pValue[1] == 'x' ) nBase = 16; DataPos nVal = static_cast<DataPos>( strtol( pValue, NULL, nBase ) ); newLookup.AddLookupData( nVal, pName ); } } RA_LOG( "RP: Adding Lookup %s\n", newLookup.Description().c_str() ); m_lookups.push_back( newLookup ); } else if( strncmp( FormatStr, buffer, strlen( FormatStr ) ) == 0 ) { // char* pBuf = &buffer[0]; const char* pUnused = _ReadStringTil( ':', pBuf, TRUE ); std::string sDesc = _ReadStringTil( EndLine, pBuf, TRUE ); _ReadTil( EndLine, buffer, 4096, &nCharsRead, pFile ); if( nCharsRead > 0 && strncmp( FormatTypeStr, buffer, strlen( FormatTypeStr ) ) == 0 ) { char* pBuf = &buffer[0]; const char* pUnused = _ReadStringTil( '=', pBuf, TRUE ); const char* pType = _ReadStringTil( EndLine, pBuf, TRUE ); RA_Leaderboard::FormatType nType; if( strcmp( pType, "SCORE" ) == 0 || strcmp( pType, "POINTS" ) == 0 ) { nType = RA_Leaderboard::Format_Score; } else if( strcmp( pType, "TIME" ) == 0 || strcmp( pType, "FRAMES" ) == 0 ) { nType = RA_Leaderboard::Format_TimeFrames; } else if( strcmp( pType, "SECS" ) == 0 ) { nType = RA_Leaderboard::Format_TimeSecs; } else if( strcmp( pType, "MILLISECS" ) == 0 ) { nType = RA_Leaderboard::Format_TimeMillisecs; } else if( strcmp( pType, "VALUE" ) == 0 ) { nType = RA_Leaderboard::Format_Value; } else { nType = RA_Leaderboard::Format_Other; } RA_LOG( "RP: Adding Formatter %s (%s)\n", sDesc.c_str(), pType ); m_formats.push_back( RA_Formattable( sDesc, nType ) ); } } else if( strncmp( DisplayableStr, buffer, strlen( DisplayableStr ) ) == 0 ) { _ReadTil( EndLine, buffer, 4096, &nCharsRead, pFile ); char* pBuf = &buffer[0]; m_sDisplay = _ReadStringTil( '\n', pBuf, TRUE ); // Terminates \n instead } _ReadTil( EndLine, buffer, 4096, &nCharsRead, pFile ); } fclose( pFile ); } }
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; } }
// static BOOL AchievementSet::LoadFromFile( GameID nGameID ) { // Is this safe? CoreAchievements->Clear(); UnofficialAchievements->Clear(); LocalAchievements->Clear(); //?!?!? if( nGameID == 0 ) return TRUE; const std::string sFilename = GetAchievementSetFilename( nGameID ); SetCurrentDirectory( Widen( g_sHomeDir ).c_str() ); FILE* pFile = NULL; fopen_s( &pFile, sFilename.c_str(), "rb" ); if( pFile != NULL ) { // Store this: we are now assuming this is the correct checksum if we have a file for it CoreAchievements->SetGameID( nGameID ); UnofficialAchievements->SetGameID( nGameID ); LocalAchievements->SetGameID( nGameID ); Document doc; doc.ParseStream( FileStream( pFile ) ); if( !doc.HasParseError() ) { //ASSERT( doc["Success"].GetBool() ); //const Value& PatchData = doc["PatchData"]; const GameID nGameIDFetched = doc["ID"].GetUint(); ASSERT( nGameIDFetched == nGameID ); const std::string& sGameTitle = doc["Title"].GetString(); const unsigned int nConsoleID = doc["ConsoleID"].GetUint(); const std::string& sConsoleName = doc["ConsoleName"].GetString(); const unsigned int nForumTopicID = doc["ForumTopicID"].GetUint(); const unsigned int nGameFlags = doc["Flags"].GetUint(); const std::string& sImageIcon = doc["ImageIcon"].GetString(); const std::string& sImageTitle = doc["ImageTitle"].GetString(); const std::string& sImageIngame = doc["ImageIngame"].GetString(); const std::string& sImageBoxArt = doc["ImageBoxArt"].GetString(); const std::string& sPublisher = doc["Publisher"].IsNull() ? "Unknown" : doc["Publisher"].GetString(); const std::string& sDeveloper = doc["Developer"].IsNull() ? "Unknown" : doc["Developer"].GetString(); const std::string& sGenre = doc["Genre"].IsNull() ? "Unknown" : doc["Genre"].GetString(); const std::string& sReleased = doc["Released"].IsNull() ? "Unknown" : doc["Released"].GetString(); const bool bIsFinal = doc["IsFinal"].GetBool(); const std::string& sRichPresencePatch = doc["RichPresencePatch"].IsNull() ? "" : doc["RichPresencePatch"].GetString(); //##SD store all this data somewhere? Do we want it? m_sPreferredGameTitle = sGameTitle; RA_RichPresenceInterpretter::PersistAndParseScript( nGameIDFetched, sRichPresencePatch ); const Value& AchievementsData = doc["Achievements"]; for( SizeType i = 0; i < AchievementsData.Size(); ++i ) { // Parse into correct boxes unsigned int nFlags = AchievementsData[i]["Flags"].GetUint(); if( nFlags == 3 ) { Achievement& newAch = CoreAchievements->AddAchievement(); newAch.Parse( AchievementsData[i] ); } else if( nFlags == 5 ) { Achievement& newAch = UnofficialAchievements->AddAchievement(); newAch.Parse( AchievementsData[i] ); } else { RA_LOG( "Cannot deal with achievement with flags: %d\n", nFlags ); } } const Value& LeaderboardsData = doc["Leaderboards"]; for( SizeType i = 0; i < LeaderboardsData.Size(); ++i ) { //"Leaderboards":[{"ID":"2","Mem":"STA:0xfe10=h0000_0xhf601=h0c_d0xhf601!=h0c_0xfff0=0_0xfffb=0::CAN:0xhfe13<d0xhfe13::SUB:0xf7cc!=0_d0xf7cc=0::VAL:0xhfe24*1_0xhfe25*60_0xhfe22*3600","Format":"TIME","Title":"Green Hill Act 1","Description":"Complete this act in the fastest time!"}, RA_Leaderboard lb( LeaderboardsData[i]["ID"].GetUint() ); lb.LoadFromJSON( LeaderboardsData[i] ); g_LeaderboardManager.AddLeaderboard( lb ); } } else { fclose( pFile ); ASSERT( !"Could not parse file?!" ); return FALSE; } fclose( pFile ); unsigned int nTotalPoints = 0; for( size_t i = 0; i < CoreAchievements->NumAchievements(); ++i ) nTotalPoints += CoreAchievements->GetAchievement( i ).Points(); if( RAUsers::LocalUser().IsLoggedIn() ) { // Loaded OK: post a request for unlocks PostArgs args; args['u'] = RAUsers::LocalUser().Username(); args['t'] = RAUsers::LocalUser().Token(); args['g'] = std::to_string( nGameID ); args['h'] = g_bHardcoreModeActive ? "1" : "0"; RAWeb::CreateThreadedHTTPRequest( RequestUnlocks, args ); std::string sNumCoreAch = std::to_string( CoreAchievements->NumAchievements() ); g_PopupWindows.AchievementPopups().AddMessage( MessagePopup( "Loaded " + sNumCoreAch + " achievements, Total Score " + std::to_string( nTotalPoints ), "", PopupInfo ) ); // TBD } return TRUE; } else { // Cannot open file RA_LOG( "Cannot open file %s\n", sFilename.c_str() ); return FALSE; } }
BOOL AchievementSet::FetchFromWebBlocking( GameID nGameID ) { // Can't open file: attempt to find it on SQL server! PostArgs args; args['u'] = RAUsers::LocalUser().Username(); args['t'] = RAUsers::LocalUser().Token(); args['g'] = std::to_string( nGameID ); args['h'] = g_bHardcoreModeActive ? "1" : "0"; Document doc; if( RAWeb::DoBlockingRequest( RequestPatch, args, doc ) && doc.HasMember( "Success" ) && doc[ "Success" ].GetBool() && doc.HasMember( "PatchData" ) ) { const Value& PatchData = doc[ "PatchData" ]; //const std::string& sMinVer = PatchData["MinVer"].GetString(); //const long nMinVerRequired = strtol( sMinVer.substr( 2 ).c_str(), NULL, 10 ); //const long CURRENT_VER = strtol( std::string( g_sClientVersion ).substr( 2 ).c_str(), nullptr, 10 ); //if( CURRENT_VER < nMinVerRequired ) //{ // // Client version too old! // char buffer[4096]; // sprintf_s( buffer, 4096, // "Client version of 0.%03d is too old for the latest patch format.\r\n" // "Version 0.%03d or greater required.\r\n" // "Visit " RA_HOST " for a more recent version? ", // CURRENT_VER, // nMinVerRequired ); // if( MessageBox( nullptr, buffer, "Client out of date!", MB_YESNO ) == IDYES ) // { // sprintf_s( buffer, 4096, "http://" RA_HOST "/download.php" ); // ShellExecute( NULL, // "open", // buffer, // NULL, // NULL, // SW_SHOWNORMAL ); // } // else // { // //MessageBox( nullptr, "Cannot load achievements for this game.", "Error", MB_OK ); // } // return FALSE; //} //else { SetCurrentDirectory( Widen( g_sHomeDir ).c_str() ); FILE* pf = nullptr; fopen_s( &pf, std::string( RA_DIR_DATA + std::to_string( nGameID ) + ".txt" ).c_str(), "wb" ); if( pf != nullptr ) { FileStream fs( pf ); Writer<FileStream> writer( fs ); PatchData.Accept( writer ); fclose( pf ); return TRUE; } else { ASSERT( !"Could not open patch file for writing?" ); RA_LOG( "Could not open patch file for writing?" ); return FALSE; } } } else { // Could not connect... PopupWindows::AchievementPopups().AddMessage( MessagePopup( "Could not connect to " RA_HOST_URL "...", "Working offline...", PopupInfo ) ); //? return FALSE; } }