//---------------------------------------------------------------------------------- // LoadInfoTextFromFile: Load the info box from the downloaded file. //---------------------------------------------------------------------------------- void PatchDetailsCtrl::LoadInfoTextFromFile(const std::string& sFile) { FILE* pFile = fopen(sFile.c_str(), "rt+"); if (pFile) { ResourceManager* pResMgr = GetCustomInfo()->GetResourceManager(); const int MAX_INFO_FILE_SIZE = 100000; char sRawContents[MAX_INFO_FILE_SIZE] = ""; memset(sRawContents, 0, MAX_INFO_FILE_SIZE); UINT nRead = static_cast<UINT>(fread(sRawContents, 1, sizeof(sRawContents) - 1, pFile)); if (nRead == sizeof(sRawContents) - 1) { char sRead[32]; wsprintf(sRead, "%d", nRead); GUIString sFormat = pResMgr->GetFinishedString(PatchDetails_FileTooBig_String); MessageBox(GetWindow(), sFormat, pResMgr->GetFinishedString(PatchDetails_FileTooBigTtl_String), MD_OK); } else sRawContents[nRead] = 0; if (nRead > 0) { m_pInfoText->Clear(); m_pInfoText->AddFormatedText(sRawContents); m_pInfoText->SetVertOffset(0); // Scroll to the top. m_pInfoText->Invalidate(); } fclose(pFile); } }
//---------------------------------------------------------------------------------- // HandleAlreadyHaveUpdateButton: Process a click on the Already Have Update button. //---------------------------------------------------------------------------------- bool SelectHostCtrl::HandleAlreadyHaveUpdateButton(ComponentEvent* pEvent) { if (pEvent->mType != ComponentEvent_ButtonPressed) return false; ResourceManager* pResMgr = GetCustomInfo()->GetResourceManager(); MSFileDialog Dlg; Dlg.SetFileMustExist(true); Dlg.AddFilter("*.exe", pResMgr->GetFinishedString(SelectHost_PatchFilter_String)); Dlg.SetTitle(pResMgr->GetFinishedString(SelectHost_FileOpenDlgTitle_String)); //Dlg.SetOkTitle("Open"); // No reason to change the default. Dlg.SetFileMustExist(true); Dlg.SetNoChangeDir(true); Dlg.SetInitialDir(GetCustomInfo()->GetPatchFolder()); if (Dlg.DoDialog(GetWindow())) { m_sLocalPatch = Dlg.GetFile(); if (GetMainControl()->GetPatchCore()->MatchLocalPatch(GetWindow(), m_sLocalPatch)) FireNextButton(); else m_sLocalPatch = ""; } return true; }
//---------------------------------------------------------------------------------- // TimerEvent: Handle a timer event. //---------------------------------------------------------------------------------- bool DownloadCtrl::TimerEvent(int tDelta) { Container::TimerEvent(tDelta); m_tDownloading += tDelta; if (! m_bShownTimeoutWarning) { CustomInfo* pCustInfo = GetCustomInfo(); ResourceManager* pResMgr = pCustInfo->GetResourceManager(); if (m_tDownloading > pCustInfo->GetPatchTimeout()) { m_bShownTimeoutWarning = true; MessageBox(GetWindow(), pResMgr->GetFinishedString(Download_NoData_String), pResMgr->GetFinishedString(Global_Warning_String), MD_OK); } } return true; }
//---------------------------------------------------------------------------------- // HandleVisitHostButton: Process a click on the Visit Host button. //---------------------------------------------------------------------------------- bool DownloadCtrl::HandleVisitHostButton(ComponentEvent* pEvent) { if (pEvent->mType != ComponentEvent_ButtonPressed) return false; ResourceManager* pResMgr = GetCustomInfo()->GetResourceManager(); CPatchData* pPatch = GetCustomInfo()->GetSelectedPatch(); // Launch the user's browser (send them to the host's site). if (pPatch) { if (pPatch->GetHostUrl().length()) Browse(pPatch->GetHostUrl()); else MessageBox(GetWindow(), pResMgr->GetFinishedString(Download_NoHostUrl_String), pResMgr->GetFinishedString(Global_Error_String), MD_OK); } return true; }
//---------------------------------------------------------------------------------- // Create the Restartter appliaction by extracting it from the embedded resource. //---------------------------------------------------------------------------------- bool ExtractRestarter(void) { ResourceManager* pResMgr = GetCustomInfo()->GetResourceManager(); DebugLog("Preparing to extract restarter\n"); if (! ExtractResourceFile(IDF_RESTARTER_EXE, sRestartExe)) { MessageBox(g_pWindow, pResMgr->GetFinishedString(Global_NoWriteRestarter_String), pResMgr->GetFinishedString(Global_Error_String), MB_OK); return false; } if (! ExtractResourceFile(IDF_PSAPI_DLL, sPsapiDll)) { MessageBox(g_pWindow, pResMgr->GetFinishedString(Global_NoWritePsapi_String), pResMgr->GetFinishedString(Global_Error_String), MB_OK); return false; } return true; }
//---------------------------------------------------------------------------------- // CanClose: Is it okay to close this window? In other words, ask the user if they // really want to abort the download (if its still in progress). //---------------------------------------------------------------------------------- bool DownloadCtrl::CanClose(void) { if (m_bPatchDownloadComplete) return true; if (m_bValidatingPatch) return false; // The MessageBox does not block access to this call so make sure we don't trap // ourselves. if (m_bShowingAbortDlg) return false; m_bShowingAbortDlg = true; ResourceManager* pResMgr = GetCustomInfo()->GetResourceManager(); int nResult = MessageBox(GetWindow(), pResMgr->GetFinishedString(Download_AbandonDownload_String), pResMgr->GetFinishedString(Download_Confirm_String), MD_YESNO); m_bShowingAbortDlg = false; // Did the download complete while we were waiting? if (nResult == MDR_NO && m_bPatchDownloadSucceeded) { ValidatePatch(); EnableControls(); } if (nResult == MDR_YES) { // Tag the patch as canceled. CPatchData* pPatch = GetCustomInfo()->GetSelectedPatch(); pPatch->SetDownloadAborts(pPatch->GetDownloadAborts() + 1); } return nResult == MDR_YES; }
//---------------------------------------------------------------------------------- // UpdateInfoText: Update the contents of the info text box. //---------------------------------------------------------------------------------- void SelectHostCtrl::UpdateInfoText(void) { // Some of the text changes dynamically based on patch selection, update it. ResourceManager* pResMgr = GetCustomInfo()->GetResourceManager(); GUIString sInfo = pResMgr->BuildInfoString(SelectHost_WaitInfo1_String_Id); if (GetCustomInfo()->GetPatchList()->size()) { sInfo = pResMgr->BuildInfoString(SelectHost_Info1_String_Id); if (m_bMixedPatches) { sInfo.append("\n\n"); sInfo.append(pResMgr->GetFinishedString(SelectHost_InfoManual_String)); } } m_pInfoText->Clear(); m_pInfoText->AddFormatedText(sInfo); m_pInfoText->SetVertOffset(0); // Scroll to the top. }
//---------------------------------------------------------------------------------- // FtpDownloadComplete: The ftp transfer has completed for the patch. Check the // status to see if it worked. //---------------------------------------------------------------------------------- void PatchDetailsCtrl::FtpDownloadComplete(FTPGetOp* pGetOp) { CustomInfo* pCustInfo = GetCustomInfo(); ResourceManager* pResMgr = pCustInfo->GetResourceManager(); WONStatus aStatus = pGetOp->GetStatus(); if (aStatus == WS_Success) { if (! m_bAborted) LoadInfoTextFromFile(m_sInfoFile); m_bDownloadSucceeded = true; } else { if (! m_bAborted) // If the user aborted don't pop up an error telling them they aborted... { // Clear the file name (so no one tries to use it), but don't deleted it, // we can resume the download later. /// pCustInfo->SetPatchFile(""); GUIString sTitle = pResMgr->GetFinishedString(Download_DownloadError_String); GUIString sMsg; switch (aStatus) { case WS_FTP_StatusError: switch (pGetOp->GetFTPStatus()) { case 500: MessageBox(GetWindow(), pResMgr->GetFinishedString(Download_InvalidFtpUser_String), sTitle, MD_OK); break; case 530: MessageBox(GetWindow(), pResMgr->GetFinishedString(Download_InvalidFtpPassword_String), sTitle, MD_OK); break; case 550: MessageBox(GetWindow(), pResMgr->GetFinishedString(Download_InvalidFtpFile_String), sTitle, MD_OK); break; default: sMsg = pResMgr->GetFinishedString(Download_FtpError_String); ReplaceSubInt(sMsg, "%ERROR%", pGetOp->GetFTPStatus()); MessageBox(GetWindow(), sMsg, sTitle, MD_OK); } //lint !e788 break; case WS_FTP_InvalidResponse: MessageBox(GetWindow(), pResMgr->GetFinishedString(Download_InvalidFtpResp_String), sTitle, MD_OK); break; case WS_FTP_InvalidPasvResponse: MessageBox(GetWindow(), pResMgr->GetFinishedString(Download_InvalidFtpPasvResp_String), sTitle, MD_OK); break; case WS_WSAECONNREFUSED: MessageBox(GetWindow(), pResMgr->GetFinishedString(Download_ConnectionRefused_String), sTitle, MD_OK); break; default: sMsg = pResMgr->GetFinishedString(Download_UnknownFtpError_String); ReplaceSubString(sMsg, "%ERROR%", WONStatusToString(aStatus)); MessageBox(GetWindow(), sMsg, sTitle, MD_OK); } //lint !e788 } } m_bDownloadComplete = true; EnableControls(); }
//---------------------------------------------------------------------------------- // FtpPatchDownloadComplete: The ftp transfer has completed for the patch. Check // the status to see if it worked. //---------------------------------------------------------------------------------- void DownloadCtrl::FtpPatchDownloadComplete(FTPGetOp* pGetOp) { CustomInfo* pCustInfo = GetCustomInfo(); ResourceManager* pResMgr = pCustInfo->GetResourceManager(); // Turn off the timer. RequestTimer(false); WONStatus aStatus = pGetOp->GetStatus(); CPatchData* pPatch = pCustInfo->GetSelectedPatch(); if (aStatus == WS_Success) { if (pPatch) pCustInfo->SaveDefaultSelection(pPatch->GetHostName()); m_bPatchDownloadSucceeded = true; if (! m_bAborted) { // Set the progress bar to 100%. int nLower, nUpper; m_pProgressBar->GetRange(nLower, nUpper); UpdateProgressBar(nUpper, nUpper); ValidatePatch(); } } else { if (! m_bAborted) // If the user aborted don't pop up an error telling them they aborted... { // Clear the file name (so no one tries to use it), but don't deleted it, // we can resume the download later. pCustInfo->SetPatchFile(""); GUIString sTitle = pResMgr->GetFinishedString(Download_DownloadError_String); GUIString sMsg; if (pPatch) pPatch->SetDownloadFailures(pPatch->GetDownloadFailures() + 1); switch (aStatus) { case WS_FTP_StatusError: switch (pGetOp->GetFTPStatus()) { case 500: NamePasswordDialog(pGetOp, sTitle, pResMgr->GetFinishedString(Download_InvalidFtpUser_String)); return; case 530: NamePasswordDialog(pGetOp, sTitle, pResMgr->GetFinishedString(Download_InvalidFtpPassword_String)); return; case 550: MessageBox(GetWindow(), pResMgr->GetFinishedString(Download_InvalidFtpFile_String), sTitle, MD_OK); break; default: sMsg = pResMgr->GetFinishedString(Download_FtpError_String); ReplaceSubInt(sMsg, "%ERROR%", pGetOp->GetFTPStatus()); MessageBox(GetWindow(), sMsg, sTitle, MD_OK); } //lint !e788 break; case WS_FTP_InvalidResponse: MessageBox(GetWindow(), pResMgr->GetFinishedString(Download_InvalidFtpResp_String), sTitle, MD_OK); break; case WS_FTP_InvalidPasvResponse: MessageBox(GetWindow(), pResMgr->GetFinishedString(Download_InvalidFtpPasvResp_String), sTitle, MD_OK); break; case WS_WSAECONNREFUSED: MessageBox(GetWindow(), pResMgr->GetFinishedString(Download_ConnectionRefused_String), sTitle, MD_OK); break; default: sMsg = pResMgr->GetFinishedString(Download_UnknownFtpError_String); ReplaceSubString(sMsg, "%ERROR%", WONStatusToString(aStatus)); MessageBox(GetWindow(), sMsg, sTitle, MD_OK); } //lint !e788 FireBackButton(); } } m_bPatchDownloadComplete = true; SendReport(); EnableControls(); }
//---------------------------------------------------------------------------------- // Show: Make this dialog visible (or invisible), and start (or stop) any threads // that are needed. //---------------------------------------------------------------------------------- void DownloadCtrl::Show(bool bShow) { SetVisible(bShow); if (bShow) { m_bPatchIsValid = false; m_bShownTimeoutWarning = false; // m_nReturnResult = SUR_UNKNOWN; GetMainControl()->SetReturnResult(SUR_UNKNOWN); CustomInfo* pCustInfo = GetCustomInfo(); ResourceManager* pResMgr = pCustInfo->GetResourceManager(); // Some of the text changes dynamically based on patch selection, update it. // Informational (Main) text. GUIString sInfo = pResMgr->BuildInfoString(Download_Info1_String_Id); m_pInfoText->Clear(); m_pInfoText->AddFormatedText(sInfo); m_pInfoText->SetVertOffset(0); // Scroll to the top. // Visit Host prompt. m_pVisitHostLabel->SetText(pResMgr->GetFinishedString(Download_Host_String)); m_pVisitHostLabel->SetDesiredSize(); // Visit Host button. m_pVisitHostButton->SetText(pResMgr->GetFinishedString(Download_VisitHost_String)); ResetProgressBar(); // Make sure the Next button is the default button. m_pNextButton->RequestFocus(); // Start the download clock. m_tDownloading = 0; m_bDownloadStarted = false; m_bPatchDownloadComplete = false; m_bPatchDownloadSucceeded = false; m_bBitmapDownloadComplete = false; m_bBitmapDownloadSucceeded = false; m_bAborted = false; RequestTimer(true); m_pVisitHostButton->SetVisible(true); m_pVisitHostImageButton->SetVisible(false); EnableControls(); // Check to see if we are using a previously downloaded patch. if (pCustInfo->GetPatchFile() != "") { m_bPatchDownloadComplete = true; int nLower, nUpper; m_pProgressBar->GetRange(nLower, nUpper); UpdateProgressBar(nUpper, nUpper); ValidatePatch(); // Check it (we are paranoid). EnableControls(); } else StartDownloads(); // Start the download itself. } else { // Turn off the timer. RequestTimer(false); // If the download is still in progress, kill it. KillThreads(); } }
//---------------------------------------------------------------------------------- // UpdateProgressBar: Update the status bar (and related text) to reflect the // supplied position of the download. //---------------------------------------------------------------------------------- void DownloadCtrl::UpdateProgressBar(int nBytesRcvd, int nTotalSize) { ResourceManager* pResMgr = GetCustomInfo()->GetResourceManager(); if (nBytesRcvd > 0) RequestTimer(false); if (! m_bAborted) { // Update the Progress Bar. m_pProgressBar->SetRange(0, nTotalSize); m_pProgressBar->SetPosition(nBytesRcvd); // Time Remaining text. DWORD tElapsed = m_tDownloading; if (nTotalSize == 0) nTotalSize = nBytesRcvd; // Sanity Check (avoid a rare divide by zero). int nPercentDone = nBytesRcvd ? (int)((100.0 * (double)nBytesRcvd) / (double)nTotalSize): 0; int aTimeElapsed = time(NULL) - mStartTime; if(aTimeElapsed < 0) aTimeElapsed = 0; mNumberCallbacks++; int nBytesLeft = nTotalSize - nBytesRcvd; float fSecsPerByte = (float)aTimeElapsed / (float)nBytesRcvd; int nTimeLeft = (int)((float)nBytesLeft * fSecsPerByte); if(nTimeLeft < 1) nTimeLeft = 1; GUIString sOutput; if (nTotalSize == nBytesRcvd) // Are we done? sOutput = ""; else if (mNumberCallbacks >= 3 && nTimeLeft && nTimeLeft < (60 * 60 * 24)) // Make sure its less than a day. { int nHours = nTimeLeft / (60 * 60); int nMinutes = (nTimeLeft / 60) % 60; int nSeconds = nTimeLeft % 60; if (nHours > 1) sOutput = pResMgr->GetFinishedString(Download_TimeHoursMinutes_String); else if (nHours) sOutput = pResMgr->GetFinishedString(Download_TimeHourMinutes_String); else if (nMinutes > 1) sOutput = pResMgr->GetFinishedString(Download_TimeMinutesSeconds_String); else if (nMinutes) sOutput = pResMgr->GetFinishedString(Download_TimeMinuteSeconds_String); else sOutput = pResMgr->GetFinishedString(Download_TimeSeconds_String); TCHAR sTime[32]; wsprintf(sTime, "%d", nHours); GUIString sHours = sTime; wsprintf(sTime, "%d", nMinutes); GUIString sMinutes = sTime; wsprintf(sTime, "%d", nSeconds); GUIString sSeconds = sTime; ReplaceSubString(sOutput, sHoursKey, sHours); ReplaceSubString(sOutput, sMinutesKey, sMinutes); ReplaceSubString(sOutput, sSecondsKey, sSeconds); } else sOutput = pResMgr->GetFinishedString(Download_Estimating_String); m_pTimeLeftLabel->SetText(sOutput); m_pTimeLeftLabel->Invalidate(); // Download Progress text. sOutput = pResMgr->GetFinishedString(Download_BytesTransferred_String); TCHAR sPercent[32]; wsprintf(sPercent, "%d", nPercentDone); ReplaceSubString(sOutput, sPercentDoneKey, sPercent); ReplaceSubString(sOutput, sReceivedKey, NumToStrWithCommas(nBytesRcvd)); ReplaceSubString(sOutput, sTotalSizeKey, NumToStrWithCommas(nTotalSize)); m_pProgressStatusLabel->SetText(sOutput); m_pProgressStatusLabel->Invalidate(); } }
//---------------------------------------------------------------------------------- // Start the restarter. Or is that restart the starter? No, that's not right. // // Start the application that waits for SierraUp to shutdown. The restarter will // then apply the SierraUp patch, and restart Sierraup. //---------------------------------------------------------------------------------- bool LaunchRestarter(const std::string& sPatchExe) { ResourceManager* pResMgr = GetCustomInfo()->GetResourceManager(); DebugLog("Preparing to launch restarter (applying update file: %s)\n", sPatchExe.c_str()); char sSelf[MAX_PATH]; GetModuleFileName(0, sSelf, MAX_PATH); // Fetch the current working folder. TCHAR sWorkingDir[MAX_PATH]; GetCurrentDirectory(sizeof(sWorkingDir), sWorkingDir); if (sWorkingDir[0] && sWorkingDir[strlen(sWorkingDir) - 1] != '\\') strcat(sWorkingDir, "\\"); // Fetch the folder SierraUp is installed in. std::string sSelfDir = sSelf; int nPos = sSelfDir.rfind('\\'); sSelfDir.erase(nPos + 1, sSelfDir.length() - (nPos + 1)); // Leave the last backslash. DebugLog("SierraUpdate folder: %s\n", sSelfDir.c_str()); // What is our executable name (just in case we change it). std::string sOurExeName = sSelf; nPos = sOurExeName.rfind('\\'); // Redundant, but just in case I move this block of code. sOurExeName.erase(0, nPos + 1); std::string sLowerExeName = StringToLowerCase(sOurExeName); DebugLog("SierraUpdate executable: %s\n", sOurExeName.c_str()); // Fetch the original Command Line (we are going to re-use it). std::string sOrigCmdLine = GetCommandLine(); std::string sLowerCmdLine = StringToLowerCase(sOrigCmdLine); std::string sNewCmdLine = sOrigCmdLine; // We will remove 'ourself' in a bit. DebugLog("Original Command Line = '%s'\n", sOrigCmdLine.c_str()); // Find and remove ourself from the command line. nPos = sLowerCmdLine.find(sLowerExeName); if (nPos != -1) { UINT nDelta = nPos + sLowerExeName.length(); sNewCmdLine.erase(0, nDelta); if (sNewCmdLine.length() && sNewCmdLine[0] == '\"') sNewCmdLine.erase(0, 1); // Bypass the ending quote. if (sNewCmdLine.length()) sNewCmdLine.erase(0, 1); // Bypass the space. } DebugLog("New Command Line (for restart) = '%s'\n", sNewCmdLine.c_str()); // We need the full path to the patch exe (replace the '.\' with our folder). std::string sFullPatchPath = CorrectPath(sPatchExe); DebugLog("Full update path = '%s'\n", sFullPatchPath.c_str()); // Write the configuration file the the restarter will use. WritePrivateProfileString(sRestartSec, sWaitTimeKey, sRestartWaitTime, sRestartIniFile); WritePrivateProfileString(sRestartSec, sWaitAppKey, sSelf, sRestartIniFile); WritePrivateProfileString(sRestartSec, sRestartParamsKey, sNewCmdLine.c_str(), sRestartIniFile); WritePrivateProfileString(sRestartSec, sUpdateAppKey, sFullPatchPath.c_str(), sRestartIniFile); WritePrivateProfileString(sRestartSec, sUpdateParamsKey, sSelfDir.c_str(), sRestartIniFile); WritePrivateProfileString(sRestartSec, sDebugKey, GetCustomInfo()->GetDebug() ? "1" : "0", sRestartIniFile); std::string sCmdLine = "\""; sCmdLine += sRestartExe; sCmdLine += TEXT("\" "); sCmdLine += sRestartIniFile; STARTUPINFO SI; ZeroMemory(&SI, sizeof(SI)); SI.cb = sizeof(STARTUPINFO); PROCESS_INFORMATION PI; DebugLog("Launching SierraUpRestarter\n"); BOOL bRes = CreateProcess(NULL, (LPTSTR)(LPCTSTR)sCmdLine.c_str(), NULL, NULL, FALSE, DETACHED_PROCESS, NULL, NULL, &SI, &PI); if (bRes) { // Clean up. CloseHandle(PI.hThread); CloseHandle(PI.hProcess); return true; } else { GUIString sMsg; if (GetLastError() == 2) sMsg = pResMgr->GetFinishedString(Global_NoFindRestarter_String); else { sMsg = pResMgr->GetFinishedString(Global_NoRunRestarter_String); ReplaceSubInt(sMsg, "%ERROR%", GetLastError()); } DebugLog("Error Starting SierraUpRestarter: %s\n", std::string(sMsg).c_str()); MessageBox(g_pWindow, sMsg, pResMgr->GetFinishedString(Global_RestarterFailed_String), MB_OK); return false; } }
//---------------------------------------------------------------------------------- // UpdatePatchList: Update the list of patches (and results). //---------------------------------------------------------------------------------- void SelectHostCtrl::UpdatePatchList(void) { CustomInfo* pCustInfo = GetCustomInfo(); ResourceManager* pResMgr = pCustInfo->GetResourceManager(); GUIString sError; int nManualPatches = 0; int nAutoPatches = 0; // Clear the list box of old refuse. m_pHostList->Clear(); m_pHostList->BeginMultiChange(); GUIString sDefault = pCustInfo->LoadDefaultSelection(); sDefault.toLowerCase(); CPatchDataList* pPatches = pCustInfo->GetPatchList(); // Go through the list and decide if we have to mark manual downloads. CPatchDataList::iterator Itr = pPatches->begin(); while (Itr != pPatches->end()) { CPatchData* pData = *Itr; if (pData) { if (pData->GetMustVisitHost()) nManualPatches++; else nAutoPatches++; } ++Itr; } m_bMixedPatches = nManualPatches != 0 && nAutoPatches != 0; // Now go through the list and add all of the items to the list box. Itr = pPatches->begin(); int nIndex = 0; int nSelectIndex = -1; while (Itr != pPatches->end()) { CPatchData* pData = *Itr; //pData->MessageBox(GetWindow()); // Debugging Aide. if (pData) { // Start with the Patch Name (we will display this). std::string sLine = pData->GetPatchName(); // Add the Manual flag (if needed). if (pData->GetMustVisitHost() && m_bMixedPatches) sLine += " *"; // Now add the results of previous download atempts. int nFailures = pData->GetDownloadFailures(); int nAborts = pData->GetDownloadAborts(); if (nFailures) { sError = pResMgr->GetFinishedString(Global_DownloadFailures_String); ReplaceSubInt(sError, "%NUM_FAILURES%", nFailures); } else if (nAborts) { sError = pResMgr->GetFinishedString(Global_DownloadAborts_String); ReplaceSubInt(sError, "%NUM_ABORTS%", nAborts); } else sError = ""; PatchDataItem* pItem = static_cast<PatchDataItem*>(m_pHostList->InsertItem(new PatchDataItem)); m_pHostList->SetString(nIndex, 0, sLine); m_pHostList->SetString(nIndex, 1, sError); pItem->m_pPatchData = pData; // Check to see if this was the last 'successful download site', if so, select it. GUIString sHost = pData->GetHostName(); sHost.toLowerCase(); if (sDefault == sHost) nSelectIndex = nIndex; ++nIndex; } ++Itr; } // If no site was selected, choose one at random. if (nSelectIndex == -1 && nIndex > 0) { srand(GetTickCount()); nSelectIndex = rand() % nIndex; } // Actually make the selection. if (nSelectIndex != -1) m_pHostList->SetSelItem(nSelectIndex); m_pHostList->EndMultiChange(); // Changing the patch list can change the Info Text, so update it. UpdateInfoText(); EnableControls(); }