BOOL CDownloadTransferBT::OnPiece(CBTPacket* pPacket)
{
	ASSUME_LOCK( Transfers.m_pSection );

	ASSERT( m_pClient != NULL );
	if ( pPacket->GetRemaining() < 8 ) return TRUE;
	if ( m_nState != dtsRequesting && m_nState != dtsDownloading ) return TRUE;
	SetState( dtsDownloading );
	DWORD nBlock	= pPacket->ReadLongBE();
	QWORD nOffset	= pPacket->ReadLongBE();
	QWORD nLength	= pPacket->GetRemaining();
	nOffset += (QWORD)nBlock * m_pDownload->m_pTorrent.m_nBlockSize;
	m_nDownloaded += nLength;
	m_pDownload->m_nTorrentDownloaded += nLength;
	m_pDownload->m_pTorrent.m_nTotalDownload += nLength;
	m_pSource->AddFragment( nOffset, nLength );
	m_pSource->SetValid();
	m_oRequested.erase( Fragments::Fragment( nOffset, nOffset + nLength ) );

	BOOL bSuccess = m_pDownload->SubmitData( nOffset,
		pPacket->m_pBuffer + pPacket->m_nPosition, nLength );
	if ( ! bSuccess )
		TRACE( _T("[BT] Failed to submit data %I64u-%I64u to \"%s\".\n"), nOffset, nOffset + nLength, m_pDownload->m_sPath );

	// TODO: SendRequests and ShowInterest could be combined.. SendRequests
	// is probably going to tell us if we are interested or not
	ShowInterest();
	return SendFragmentRequests();
}
BOOL CDownloadTransferED2K::OnSendingPart64(CEDPacket* pPacket)
{
	//if ( m_nState != dtsDownloading ) return TRUE;

	if ( pPacket->GetRemaining() <= Hashes::Ed2kHash::byteCount + 16 )
	{
		theApp.Message( MSG_ERROR, IDS_ED2K_CLIENT_BAD_PACKET, (LPCTSTR)m_sAddress, pPacket->m_nType );
		Close( TRI_FALSE );
		return FALSE;
	}

	Hashes::Ed2kHash oED2K;
	pPacket->Read( oED2K );

	if ( oED2K != m_pDownload->m_oED2K )
	{
		theApp.Message( MSG_ERROR, IDS_DOWNLOAD_WRONG_HASH,
			(LPCTSTR)m_sAddress, (LPCTSTR)m_pDownload->GetDisplayName() );
		// Close( TRI_FALSE );
		// return FALSE;
		return TRUE;
	}

	QWORD	nOffset = pPacket->ReadLongLE();
			nOffset = ( (QWORD)pPacket->ReadLongLE() << 32 ) | nOffset;
	
	QWORD	nLength = pPacket->ReadLongLE();
			nLength = ( (QWORD)pPacket->ReadLongLE() << 32 ) | nLength;

	if ( nLength <= nOffset )
	{
		if ( nLength == nOffset ) return TRUE;
		theApp.Message( MSG_ERROR, IDS_ED2K_CLIENT_BAD_PACKET, (LPCTSTR)m_sAddress, pPacket->m_nType );
		Close( TRI_FALSE );
		return FALSE;
	}

	nLength -= nOffset;

	if ( nLength > (QWORD)pPacket->GetRemaining() )
	{
		theApp.Message( MSG_ERROR, IDS_ED2K_CLIENT_BAD_PACKET, (LPCTSTR)m_sAddress, pPacket->m_nType );
		Close( TRI_FALSE );
		return FALSE;
	}

	/*BOOL bUseful =*/ m_pDownload->SubmitData( nOffset, pPacket->m_pBuffer + pPacket->m_nPosition, nLength );

	m_oRequested.erase( Fragments::Fragment( nOffset, nOffset + nLength ) );

	m_pSource->AddFragment( nOffset, nLength, ( nOffset % ED2K_PART_SIZE ) ? TRUE : FALSE );

	m_nDownloaded += nLength;

	m_pSource->SetValid();

	return SendFragmentRequests();
}
BOOL CDownloadTransferED2K::OnStartUpload(CEDPacket* /*pPacket*/)
{
	SetState( dtsDownloading );
	m_pClient->m_mInput.tLast = GetTickCount();

	ClearRequests();

	return SendFragmentRequests();
}
BOOL CDownloadTransferBT::OnUnchoked(CBTPacket* /*pPacket*/)
{
	ASSUME_LOCK( Transfers.m_pSection );

	m_bChoked = FALSE;
	SetState( dtsTorrent );
	m_oRequested.clear();
	
	theApp.Message( MSG_DEBUG, _T("Download from %s was Unchoked."), (LPCTSTR)m_sAddress );
	
	return SendFragmentRequests();
}
BOOL CDownloadTransferED2K::OnCompressedPart(CEDPacket* pPacket)
{
	if ( pPacket->GetRemaining() <= Hashes::Ed2kHash::byteCount + 8 )
	{
		theApp.Message( MSG_ERROR, IDS_ED2K_CLIENT_BAD_PACKET, (LPCTSTR)m_sAddress, pPacket->m_nType );
		Close( TRI_FALSE );
		return FALSE;
	}

	Hashes::Ed2kHash oED2K;
	pPacket->Read( oED2K );

	if ( validAndUnequal( oED2K, m_pDownload->m_oED2K ) )
	{
		theApp.Message( MSG_ERROR, IDS_DOWNLOAD_WRONG_HASH, (LPCTSTR)m_sAddress, (LPCTSTR)m_pDownload->GetDisplayName() );
		// Close( TRI_FALSE );
		// return FALSE;
		return TRUE;
	}

	QWORD nBaseOffset = pPacket->ReadLongLE();
	QWORD nBaseLength = pPacket->ReadLongLE();

	z_streamp pStream = (z_streamp)m_pInflatePtr;

	if ( m_pInflatePtr == NULL || m_nInflateOffset != nBaseOffset || m_nInflateLength != nBaseLength )
	{
		if ( pStream != NULL )
		{
			inflateEnd( pStream );
			delete pStream;
		}

		m_nInflateOffset	= nBaseOffset;
		m_nInflateLength	= nBaseLength;
		m_nInflateRead		= 0;
		m_nInflateWritten	= 0;
		m_pInflateBuffer->Clear();

		m_pInflatePtr = new z_stream;
		pStream = (z_streamp)m_pInflatePtr;
		ZeroMemory( pStream, sizeof( z_stream ) );

		if ( inflateInit( pStream ) != Z_OK )
		{
			delete pStream;
			m_pInflatePtr = NULL;

			theApp.Message( MSG_ERROR, IDS_DOWNLOAD_INFLATE_ERROR, (LPCTSTR)m_pDownload->GetDisplayName() );

			Close( TRI_FALSE );
			return FALSE;
		}
	}

	m_pInflateBuffer->Add( pPacket->m_pBuffer + pPacket->m_nPosition, pPacket->GetRemaining() );

	auto_array< BYTE > pBuffer( new BYTE[ BUFFER_SIZE ] );

	if ( m_pInflateBuffer->m_nLength > 0 && m_nInflateRead < m_nInflateLength )
	{
		pStream->next_in  = m_pInflateBuffer->m_pBuffer;
		pStream->avail_in = m_pInflateBuffer->m_nLength;

		do
		{
			pStream->next_out  = pBuffer.get();
			pStream->avail_out = BUFFER_SIZE;

			inflate( pStream, Z_SYNC_FLUSH );

			if ( pStream->avail_out < BUFFER_SIZE )
			{
				QWORD nOffset = m_nInflateOffset + m_nInflateWritten;
				QWORD nLength = BUFFER_SIZE - pStream->avail_out;

				m_pDownload->SubmitData( nOffset, pBuffer.get(), nLength );

				m_oRequested.erase( Fragments::Fragment( nOffset, nOffset + nLength ) );

				m_pSource->AddFragment( nOffset, nLength, ( nOffset % ED2K_PART_SIZE ) ? TRUE : FALSE );

				m_nDownloaded += nLength;
				m_nInflateWritten += nLength;
			}
		}
		while ( pStream->avail_out == 0 );

		if ( pStream->avail_in < m_pInflateBuffer->m_nLength )
		{
			m_nInflateRead += ( m_pInflateBuffer->m_nLength - pStream->avail_in );
			m_pInflateBuffer->Remove( m_pInflateBuffer->m_nLength - pStream->avail_in );
		}
	}

	if ( m_nInflateRead >= m_nInflateLength )
	{
		inflateEnd( pStream );
		delete pStream;
		m_pInflatePtr = NULL;
		m_pInflateBuffer->Clear();
	}

	m_pSource->SetValid();

	return SendFragmentRequests();
}
BOOL CDownloadTransferBT::OnRun()
{
	DWORD tNow = GetTickCount();
	BOOL bShowInterest	= ( tNow - m_tRunThrottle >= 2000 );

	QWORD nBlockSize	= m_pDownload->m_pTorrent.m_nBlockSize;
	DWORD nBlockCount	= m_pDownload->m_pTorrent.m_nBlockCount;

	if ( ! m_bAvailable && nBlockSize && nBlockCount && m_nAvailable )
	{
		m_bAvailable = TRUE;

		if ( m_nAvailable != nBlockCount )
		{
			BYTE* pAvailable = m_pAvailable;
			DWORD nAvailable = m_nAvailable;
			
			m_nAvailable = nBlockCount;
			m_pAvailable = new BYTE[ nBlockCount ];
			ZeroMemory( m_pAvailable, nBlockCount );
			if ( pAvailable && nAvailable )
			{
				if ( nAvailable > nBlockCount )
					nAvailable = nBlockCount;
				memcpy( m_pAvailable, pAvailable, nAvailable );	
			}
			delete [] pAvailable;
		}

		for ( DWORD nBlock = 0; nBlock < nBlockCount; nBlock++ )
		{
			if ( m_pAvailable[ nBlock ] )
			{
				QWORD nOffset = nBlockSize * nBlock;
				QWORD nLength = min( nBlockSize, m_pDownload->m_nSize - nOffset );
				m_pSource->m_oAvailable.insert( m_pSource->m_oAvailable.end(),
					Fragments::Fragment( nOffset, nOffset + nLength ) );
			}
		}
		bShowInterest = TRUE;
	}

	if ( bShowInterest )
	{
		m_tRunThrottle = tNow;
		ShowInterest();
		if ( m_nState == dtsTorrent || m_nState == dtsRequesting || m_nState == dtsDownloading )
		{
			SendFragmentRequests();
		}
	}

	if ( ( m_pDownload->GetSourceCount() < Settings.Downloads.SourcesWanted ) &&
		 ( tNow >= m_tSourceRequest + Settings.BitTorrent.SourceExchangePeriod * 60 * 1000 ) )
	{
		m_tSourceRequest = tNow;

		m_pClient->SendSourceRequest();
	}

	return CDownloadTransfer::OnRun();
}