void CDownloadTask::RunMergeFile(CDownload* pDownload, LPCTSTR szFilename, BOOL bMergeValidation, const Fragments::List& oMissedGaps, float fProgress) { // HANDLE hSource = CreateFile( szFilename, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL ); QWORD nSourceSize = 0; // qwSourceLength QWORD nSourceOffset = 0; // qwSourceOffset CAtlFile oSource; oSource.Create( szFilename, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, OPEN_EXISTING ); VERIFY_FILE_ACCESS( oSource, szFilename ) if ( ! oSource ) { // Source file open error theApp.Message( MSG_ERROR, IDS_DOWNLOAD_FILE_OPEN_ERROR, szFilename ); return; } oSource.GetSize( nSourceSize ); if ( ! nSourceSize ) return; // Empty source file CSingleLock pLock( &Transfers.m_pSection, TRUE ); if ( ! Downloads.Check( pDownload ) || pDownload->IsCompleted() || pDownload->IsMoving() ) return; // Moot download almost completed if ( m_bMergeValidation && ! pDownload->IsTorrent() && pDownload->NeedTigerTree() && pDownload->NeedHashset() ) { // pLock.Unlock(); // MsgBox( IDS_DOWNLOAD_EDIT_COMPLETE_NOHASH, MB_ICONEXCLAMATION ); DEBUG_ONLY( theApp.Message( MSG_DEBUG, IDS_DOWNLOAD_EDIT_COMPLETE_NOHASH ) ); return; // No hashsets } if ( ! pDownload->PrepareFile() ) return; // Destination file open error Fragments::List oList( pDownload->GetEmptyFragmentList() ); if ( ! oMissedGaps.empty() ) { Fragments::List::const_iterator pItr = oMissedGaps.begin(); const Fragments::List::const_iterator pEnd = oMissedGaps.end(); for ( ; pItr != pEnd ; ++pItr ) oList.erase( *pItr ); } if ( ! oList.size() ) return; // No available fragments // Determine offset if needed if ( pDownload->IsMultiFileTorrent() ) { QWORD nOffset = 0; // qwOffset BOOL bFound = FALSE; CBTInfo::CBTFile* pFile; CString strTargetName; const CString strSourceName = PathFindFileName( szFilename ); if ( GetAsyncKeyState( VK_SHIFT ) & 0x8000 ) // Forced selection dialog { int nIndex = pDownload->SelectFile( &pLock ); if ( nIndex == 0 ) { bFound = TRUE; } else if ( nIndex > 0 ) { CBTInfo::CBTFile* pSelectFile = pDownload->m_pTorrent.m_pFiles.GetAt( pDownload->m_pTorrent.m_pFiles.FindIndex( nIndex ) ); for ( POSITION pos = pDownload->m_pTorrent.m_pFiles.GetHeadPosition() ; pos ; ) { pFile = pDownload->m_pTorrent.m_pFiles.GetNext( pos ); if ( pFile->m_sPath == pSelectFile->m_sPath ) { DEBUG_ONLY( theApp.Message( MSG_DEBUG, _T("Merge Selected File: ") + pFile->m_sPath ) ); nSourceOffset = nOffset; bFound = TRUE; // Avoid checks below break; } nOffset += pFile->m_nSize; } } } if ( ! bFound ) // No forced match, try filename { for ( POSITION pos = pDownload->m_pTorrent.m_pFiles.GetHeadPosition() ; pos ; ) { pFile = pDownload->m_pTorrent.m_pFiles.GetNext( pos ); strTargetName = PathFindFileName( pFile->m_sPath ); if ( strTargetName.CompareNoCase( strSourceName ) == 0 ) { DEBUG_ONLY( theApp.Message( MSG_DEBUG, _T("Merge Filename: ") + pFile->m_sPath ) ); nSourceOffset = nOffset; bFound = TRUE; // Avoid fallback check below break; } nOffset += pFile->m_nSize; } } if ( ! bFound ) // No filename match, try exact size { nOffset = 0; // const CString strExt = PathFindExtension( strSourceName ); for ( POSITION pos = pDownload->m_pTorrent.m_pFiles.GetHeadPosition() ; pos ; ) { pFile = pDownload->m_pTorrent.m_pFiles.GetNext( pos ); if ( pFile->m_nSize == nSourceSize ) // && strExt == PathFindExtension( pFile->m_sPath ) { DEBUG_ONLY( theApp.Message( MSG_DEBUG, _T("Merge Filesize Fallback") ) ); nSourceOffset = nOffset; // bFound = TRUE; break; } nOffset += pFile->m_nSize; } } } pLock.Unlock(); const float fIncrement = fProgress / oList.size(); const DWORD nBufferLength = 256 * 1024; // Was 65536? // Read missing file fragments from selected file auto_array< BYTE > Buf( new BYTE [nBufferLength] ); Fragments::List::const_iterator pItr = oList.begin(); const Fragments::List::const_iterator pEnd = oList.end(); for ( ; ! m_pEvent && pItr != pEnd ; ++pItr ) { m_fProgress += fIncrement; // Update tooltip QWORD qwLength = pItr->end() - pItr->begin(); QWORD qwOffset = pItr->begin(); // Check for overlapped fragments if ( qwOffset + qwLength <= nSourceOffset || nSourceOffset + nSourceSize <= qwOffset ) continue; // No overlaps // Calculate overlapped range end offset QWORD qwEnd = min( qwOffset + qwLength, nSourceOffset + nSourceSize ); // Calculate overlapped range start offset qwOffset = max( qwOffset, nSourceOffset ); // Calculate overlapped range length qwLength = qwEnd - qwOffset; // Calculate file offset if any QWORD qwFileOffset = ( qwOffset > nSourceOffset ) ? qwOffset - nSourceOffset : 0; if ( FAILED( oSource.Seek( qwFileOffset, FILE_BEGIN ) ) ) continue; DWORD dwToRead; while ( ( dwToRead = (DWORD)min( qwLength, (QWORD)nBufferLength ) ) != 0 && ! m_pEvent ) { DWORD dwReaded = 0; if ( SUCCEEDED( oSource.Read( Buf.get(), dwToRead, dwReaded ) ) && dwReaded ) { pLock.Lock(); if ( ! Downloads.Check( pDownload ) || pDownload->IsCompleted() || pDownload->IsMoving() ) return; pDownload->SubmitData( qwOffset, Buf.get(), (QWORD)dwReaded ); pLock.Unlock(); qwOffset += (QWORD)dwReaded; qwLength -= (QWORD)dwReaded; } else { // File error or end of file. Non-Fatal break; } } pLock.Lock(); if ( ! Downloads.Check( pDownload ) || pDownload->IsCompleted() || pDownload->IsMoving() ) return; if ( bMergeValidation ) pDownload->RunValidation(); pDownload->SetModified(); pLock.Unlock(); } // m_bSuccess = true; }
blockPair CDownloadTransfer::SelectBlock(const Fragments::List& oPossible, const std::vector< bool >& pAvailable, bool bEndGame) const { ASSUME_LOCK( Transfers.m_pSection ); if ( oPossible.empty() ) return std::make_pair( 0ull, 0ull ); Fragments::List::const_iterator pItr = oPossible.begin(); const Fragments::List::const_iterator pEnd = oPossible.end(); if ( bEndGame ) { std::vector< blockPair > oPartials; for ( ; pItr != pEnd && oPartials.size() < oPartials.max_size(); ++pItr ) { oPartials.push_back( std::make_pair( pItr->begin(), pItr->end() - pItr->begin() ) ); } return oPartials[ GetRandomNum< size_t >( 0u, oPartials.size() - 1u ) ]; } if ( pItr->begin() < Settings.Downloads.ChunkStrap ) { return std::make_pair( pItr->begin(), min( pItr->end() - pItr->begin(), (QWORD)Settings.Downloads.ChunkStrap ) ); } DWORD nBlockSize = m_pDownload->GetVerifyLength( m_nProtocol ); if ( ! nBlockSize ) return std::make_pair( pItr->begin(), pItr->end() - pItr->begin() ); std::vector< QWORD > oBlocks; QWORD nRangeBlock = 0ull; QWORD nRange[3] = { 0ull, 0ull, 0ull }; QWORD nBestRange[3] = { 0ull, 0ull, 0ull }; for ( ; pItr != pEnd; ++pItr ) { QWORD nPart[2] = { pItr->begin(), 0ull }; QWORD nBlockBegin = nPart[0] / nBlockSize; const QWORD nBlockEnd = ( pItr->end() - 1ull ) / nBlockSize; // The start of a block is complete, but part is missing if ( nPart[0] % nBlockSize && ( nBlockBegin >= pAvailable.size() || pAvailable[ (DWORD)nBlockBegin ] ) ) { nPart[1] = min( pItr->end(), nBlockSize * ( nBlockBegin + 1ull ) ); nPart[1] -= nPart[0]; CheckPart( nPart, nBlockBegin, nRange, nRangeBlock, nBestRange ); } // The end of a block is complete, but part is missing if ( ( ! nPart[1] || nBlockBegin != nBlockEnd ) && pItr->end() % nBlockSize && ( nBlockEnd >= pAvailable.size() || pAvailable[ (DWORD)nBlockEnd ] ) ) { nPart[0] = nBlockEnd * nBlockSize; nPart[1] = pItr->end() - nPart[0]; CheckPart( nPart, nBlockEnd, nRange, nRangeBlock, nBestRange ); } // This fragment contains one or more aligned empty blocks if ( ! nRange[2] ) { for ( ; ( nBlockBegin <= nBlockEnd && oBlocks.size() < oBlocks.max_size() ); ++nBlockBegin ) { if ( nBlockBegin >= pAvailable.size() || pAvailable[ (DWORD)nBlockBegin ] ) oBlocks.push_back( nBlockBegin ); } } } CheckRange( nRange, nBestRange ); if ( ! nBestRange[2] ) { if ( oBlocks.empty() ) return std::make_pair( 0ull, 0ull ); nRange[0] = oBlocks[ GetRandomNum< size_t >( 0u, oBlocks.size() - 1u ) ]; nRange[0] *= nBlockSize; return std::make_pair( nRange[0], nBlockSize ); } return std::make_pair( nBestRange[0], nBestRange[1] ); }