Beispiel #1
0
STDMETHODIMP MFStream::GetLength(QWORD *pqwLength)
{
    if (!pqwLength)
        return E_INVALIDARG;
    QMutexLocker locker(&m_mutex);
    *pqwLength = QWORD(m_stream->size());
    return S_OK;
}
void StreamEventDemuxer::parseReferenceNPT( BYTE *section ) {
    BYTE descLen          = GET_SE_LEN(section);
    bool postDiscontinuityIndicator = GET_BYTE(section+2) & 0x80 ? true : false;
    BYTE contentID        = GET_BYTE(section+2) & 0x7F;
	QWORD stcReference    = ((QWORD(GET_BYTE(section+3) & 0x1)) << 32) | (0x00000000FFFFFFFFLLU & (GET_DWORD(section+4)));
    QWORD nptReference    = GET_QWORD(section+8) & 0x00000001FFFFFFFFLLU;
    WORD scaleNumerator   = GET_WORD(section+16);
    WORD scaleDenominator = GET_WORD(section+18);

    printf( "[DSMCCDemuxer] Reference NPT: len=%d, post=%d, contentID=%d, stc=%llx, npt=%llx, nominator=%x, denominator=%x\n",
        descLen, postDiscontinuityIndicator, contentID, stcReference, nptReference, scaleNumerator, scaleDenominator );
}
BOOL CUploadTransferED2K::DispatchNextChunk()
{
	ASSERT( m_nState == upsUploading );
	if ( !m_pDiskFile ) return FALSE;
	ASSERT( m_nLength < SIZE_UNKNOWN );
	ASSERT( m_nPosition < m_nLength );
	
	QWORD nChunk = m_nLength - m_nPosition;
	nChunk = min( nChunk, QWORD(Settings.eDonkey.FrameSize) );
	
#if 0
	// Use packet form
	
	CEDPacket* pPacket = CEDPacket::New( ED2K_C2C_SENDINGPART );
	pPacket->Write( &m_pED2K, sizeof(MD4) );
	pPacket->WriteLongLE( m_nOffset + m_nPosition );
	pPacket->WriteLongLE( m_nOffset + m_nPosition + nChunk );
	
	m_pDiskFile->Read( m_nFileBase + m_nOffset + m_nPosition, pPacket->GetWritePointer( nChunk ), nChunk, &nChunk );
	// SetFilePointer( hFile, m_nFileBase + m_nOffset + m_nPosition, NULL, FILE_BEGIN );
	// ReadFile( hFile, pPacket->WriteGetPointer( nChunk ), nChunk, &nChunk, NULL );
	
	if ( nChunk == 0 )
	{
		pPacket->Release();
		return FALSE;
	}
	
	pPacket->m_nLength = sizeof(MD4) + 8 + nChunk;
	
	Send( pPacket );
	
#else
	// Raw write
	
	CBuffer* pBuffer = m_pClient->m_pOutput;
	pBuffer->EnsureBuffer( sizeof(ED2K_PART_HEADER) + (DWORD)nChunk );
	
	ED2K_PART_HEADER* pHeader = (ED2K_PART_HEADER*)( pBuffer->m_pBuffer + pBuffer->m_nLength );
	
	if ( ! m_pDiskFile->Read( m_nFileBase + m_nOffset + m_nPosition, &pHeader[1], nChunk, &nChunk ) ) return FALSE;
	// SetFilePointer( hFile, m_nFileBase + m_nOffset + m_nPosition, NULL, FILE_BEGIN );
	// ReadFile( hFile, &pHeader[1], nChunk, &nChunk, NULL );
	if ( nChunk == 0 ) return FALSE;
	
	pHeader->nProtocol	= ED2K_PROTOCOL_EDONKEY;
	pHeader->nType		= ED2K_C2C_SENDINGPART;
	pHeader->nLength	= 1 + sizeof(MD4) + 8 + (DWORD)nChunk;
	pHeader->pMD4		= m_pED2K;
	pHeader->nOffset1	= (DWORD)( m_nOffset + m_nPosition );
	pHeader->nOffset2	= (DWORD)( m_nOffset + m_nPosition + nChunk );
	
	pBuffer->m_nLength += sizeof(ED2K_PART_HEADER) + (DWORD)nChunk;
	m_pClient->Send( NULL );
	
#endif
	
	m_nPosition += nChunk;
	m_nUploaded += nChunk;
	Statistics.Current.Uploads.Volume += ( nChunk / 1024 );
	
	return TRUE;
}
bool SyncCommand::HandleRegistryKey(const CString& regname, CSimpleIni& iniFile, bool bCloudIsNewer)
{
    CAutoRegKey hKey;
    CAutoRegKey hKeyKey;
    DWORD regtype = 0;
    DWORD regsize = 0;
    CString sKeyPath = L"Software";
    CString sValuePath = regname;
    CString sIniKeyName = regname;
    CString sRegname = regname;
    CString sValue;
    bool bHaveChanges = false;
    if (regname.Find('\\') >= 0)
    {
        // handle values in sub-keys
        sKeyPath = L"Software\\" + regname.Left(regname.ReverseFind('\\'));
        sValuePath = regname.Mid(regname.ReverseFind('\\') + 1);
    }
    if (RegOpenKeyEx(HKEY_CURRENT_USER, sKeyPath, 0, KEY_READ, hKey.GetPointer()) == ERROR_SUCCESS)
    {
        bool bEnum = false;
        bool bEnumKeys = false;
        int index = 0;
        int keyindex = 0;
        // an asterisk means: use all values inside the specified key
        if (sValuePath == L"*")
            bEnum = true;
        if (sValuePath == L"**")
        {
            bEnumKeys = true;
            bEnum = true;
            RegOpenKeyEx(HKEY_CURRENT_USER, sKeyPath, 0, KEY_READ, hKeyKey.GetPointer());
        }
        do
        {
            if (bEnumKeys)
            {
                bEnum = true;
                index = 0;
                wchar_t cKeyName[MAX_PATH] = { 0 };
                DWORD cLen = _countof(cKeyName);
                if (RegEnumKeyEx(hKeyKey, keyindex, cKeyName, &cLen, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
                {
                    bEnumKeys = false;
                    break;
                }
                ++keyindex;
                sKeyPath = L"Software\\" + regname.Left(regname.ReverseFind('\\')) + L"\\" + cKeyName + L"\\";
                sRegname = regname.Left(regname.ReverseFind('\\')) + L"\\" + cKeyName + L"\\";
                hKey.CloseHandle();
                if (RegOpenKeyEx(HKEY_CURRENT_USER, sKeyPath, 0, KEY_READ, hKey.GetPointer()) != ERROR_SUCCESS)
                {
                    bEnumKeys = false;
                    break;
                }
            }
            do
            {
                if (bEnum)
                {
                    // start enumerating all values
                    wchar_t cValueName[MAX_PATH] = { 0 };
                    DWORD cLen = _countof(cValueName);
                    if (RegEnumValue(hKey, index, cValueName, &cLen, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
                    {
                        bEnum = false;
                        break;
                    }
                    ++index;
                    sValuePath = cValueName;
                    CString sValueLower = sValuePath;
                    sValueLower.MakeLower();
                    bool bIgnore = false;
                    for (const auto& ignore : regBlockArray)
                    {
                        if (wcswildcmp(ignore, sValueLower))
                        {
                            bIgnore = true;
                            break;
                        }
                    }
                    if (bIgnore)
                        continue;
                    sIniKeyName = sRegname.Left(sRegname.ReverseFind('\\'));
                    if (sIniKeyName.IsEmpty())
                        sIniKeyName = sValuePath;
                    else
                        sIniKeyName += L"\\" + sValuePath;
                }
                if (RegQueryValueEx(hKey, sValuePath, NULL, &regtype, NULL, &regsize) == ERROR_SUCCESS)
                {
                    if (regtype != 0)
                    {
                        auto regbuf = std::make_unique<BYTE[]>(regsize);
                        if (RegQueryValueEx(hKey, sValuePath, NULL, &regtype, regbuf.get(), &regsize) == ERROR_SUCCESS)
                        {
                            switch (regtype)
                            {
                                case REG_DWORD:
                                {
                                    DWORD value = *(DWORD*)regbuf.get();
                                    sValue = iniFile.GetValue(L"registry_dword", sIniKeyName);
                                    DWORD nValue = DWORD(_wtol(sValue));
                                    if (nValue != value)
                                    {
                                        if (bCloudIsNewer)
                                        {
                                            RegSetValueEx(hKey, sValuePath, NULL, regtype, (BYTE *)&nValue, sizeof(nValue));
                                        }
                                        else
                                        {
                                            bHaveChanges = true;
                                            sValue.Format(L"%lu", value);
                                            iniFile.SetValue(L"registry_dword", sIniKeyName, sValue);
                                        }
                                    }
                                    if (bCloudIsNewer)
                                        iniFile.Delete(L"registry_dword", sIniKeyName);
                                }
                                    break;
                                case REG_QWORD:
                                {
                                    QWORD value = *(QWORD*)regbuf.get();
                                    sValue = iniFile.GetValue(L"registry_qword", sIniKeyName);
                                    QWORD nValue = QWORD(_wtoi64(sValue));
                                    if (nValue != value)
                                    {
                                        if (bCloudIsNewer)
                                        {
                                            RegSetValueEx(hKey, sValuePath, NULL, regtype, (BYTE *)&nValue, sizeof(nValue));
                                        }
                                        else
                                        {
                                            bHaveChanges = true;
                                            sValue.Format(L"%I64d", value);
                                            iniFile.SetValue(L"registry_qword", sIniKeyName, sValue);
                                        }
                                    }
                                    if (bCloudIsNewer)
                                        iniFile.Delete(L"registry_qword", sIniKeyName);
                                }
                                    break;
                                case REG_EXPAND_SZ:
                                case REG_MULTI_SZ:
                                case REG_SZ:
                                {
                                    sValue = (LPCWSTR)regbuf.get();
                                    CString iniValue = iniFile.GetValue(L"registry_string", sIniKeyName);
                                    if (iniValue != sValue)
                                    {
                                        if (bCloudIsNewer)
                                        {
                                            RegSetValueEx(hKey, sValuePath, NULL, regtype, (BYTE *)(LPCWSTR)iniValue, (iniValue.GetLength() + 1)*sizeof(WCHAR));
                                        }
                                        else
                                        {
                                            bHaveChanges = true;
                                            iniFile.SetValue(L"registry_string", sIniKeyName, sValue);
                                        }
                                    }
                                    if (bCloudIsNewer)
                                        iniFile.Delete(L"registry_string", sIniKeyName);
                                }
                                    break;
                            }
                        }
                    }
                }
            } while (bEnum);
        } while (bEnumKeys);
    }
    return bHaveChanges;
}
BOOL CLocalSearch::AddHitG1(CLibraryFile* pFile, int nIndex)
{
	// Check that the file is actually available. (We must not return ghost hits to G1!)
	if ( ! pFile->IsAvailable() ) return FALSE;

	// Check that a queue that can upload this file exists, and isn't insanely long.
	if ( UploadQueues.QueueRank( PROTOCOL_HTTP, pFile ) > Settings.Gnutella1.HitQueueLimit ) return FALSE;
	// Normally this isn't a problem- the default queue length is 8 to 10, so this check (50) will
	// never be activated. However, sometimes users configure bad settings, such as a 2000 user HTTP
	// queue. Although the remote client could/should handle this by itself, we really should give
	// Gnutella some protection against 'extreme' settings (if only to reduce un-necessary traffic.)

	m_pPacket->WriteLongLE( pFile->m_nIndex );
	m_pPacket->WriteLongLE( (DWORD)min( pFile->GetSize(), QWORD(0xFFFFFFFF) ) );
	if ( Settings.Gnutella1.QueryHitUTF8 ) //Support UTF-8 Query
	{
		m_pPacket->WriteStringUTF8( pFile->m_sName );
	}
	else
	{
		m_pPacket->WriteString( pFile->m_sName );
	}

	if ( pFile->m_bSHA1 )
	{
		CString strHash = CSHA::HashToString( &pFile->m_pSHA1, TRUE );
		m_pPacket->WriteString( strHash );

		/*
		CGGEPBlock pBlock;

		CGGEPItem* pItem = pBlock.Add( _T("H") );
		pItem->WriteByte( 1 );
		pItem->Write( &pFile->m_pSHA1, 20 );

		pBlock.Write( m_pPacket );
		m_pPacket->WriteByte( 0 );
		*/
	}
	else if ( pFile->m_bTiger )
	{
		CString strHash = CTigerNode::HashToString( &pFile->m_pTiger, TRUE );
		m_pPacket->WriteString( strHash );
	}
	else if ( pFile->m_bED2K )
	{
		CString strHash = CED2K::HashToString( &pFile->m_pED2K, TRUE );
		m_pPacket->WriteString( strHash );
	}
	else
	{
		m_pPacket->WriteByte( 0 );
	}

	if ( pFile->m_pSchema != NULL && pFile->m_pMetadata != NULL && ( m_pSearch == NULL || m_pSearch->m_bWantXML ) )
	{
		AddMetadata( pFile->m_pSchema, pFile->m_pMetadata, nIndex );
	}

	return TRUE;
}
bool CDownloadTransferED2K::SendFragmentRequests()
{
	ASSUME_LOCK( Transfers.m_pSection );

	ASSERT( m_pClient != NULL );

	if ( m_nState != dtsDownloading ) return TRUE;

	if ( m_oRequested.size() >= (int)Settings.eDonkey.RequestPipe ) return TRUE;

	Fragments::List oPossible( m_pDownload->GetEmptyFragmentList() );

	if ( ! m_pClient->m_bEmLargeFile && ( m_pDownload->m_nSize & 0xffffffff00000000 ) )
	{
		Fragments::Fragment Selected( 0x100000000, m_pDownload->m_nSize - 1 );
		oPossible.erase( Selected );
	}

	if ( ! m_pDownload->m_bTorrentEndgame )
	{
		for ( CDownloadTransfer* pTransfer = m_pDownload->GetFirstTransfer() ;
			pTransfer && ! oPossible.empty() ; pTransfer = pTransfer->m_pDlNext )
		{
			pTransfer->SubtractRequested( oPossible );
		}
	}

	typedef std::map< QWORD, Fragments::Fragment > _TRequest;
	typedef _TRequest::iterator _TRequestIndex;
	_TRequest oRequesting;
	while ( m_oRequested.size() < (int)Settings.eDonkey.RequestPipe )
	{
		QWORD nOffset, nLength;

		if ( SelectFragment( oPossible, nOffset, nLength, m_pDownload->m_bTorrentEndgame ) )
		{
			ChunkifyRequest( &nOffset, &nLength, Settings.eDonkey.RequestSize, FALSE );

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

			m_oRequested.push_back( Selected );

			oRequesting.insert( _TRequest::value_type(nOffset, Selected) );
		}
		else
		{
			break;
		}
	}

	while ( ! oRequesting.empty() )
	{
		DWORD nCount = 0;
		QWORD nOffsetBegin[3] = {0,0,0}, nOffsetEnd[3] = {0,0,0};
		bool  bI64Offset = false;

		while ( nCount < 3 && ! oRequesting.empty() )
		{
			_TRequestIndex iIndex = oRequesting.begin();
			nOffsetBegin[nCount] = QWORD((*iIndex).second.begin());
			nOffsetEnd[nCount] = QWORD((*iIndex).second.end());
			bI64Offset |= ( ( ( nOffsetBegin[nCount] & 0xffffffff00000000 ) ) ||
							( ( nOffsetEnd[nCount] & 0xffffffff00000000 ) ) );
			oRequesting.erase(iIndex);
			nCount++;
		}

		if ( bI64Offset )
		{
			CEDPacket* pPacket = CEDPacket::New( ED2K_C2C_REQUESTPARTS, ED2K_PROTOCOL_EMULE );
			pPacket->Write( m_pDownload->m_oED2K );

			// This commented-out code is for BigEndian, only needed when ported to different platform.
			//pPacket->WriteLongLE( (DWORD)( nOffsetBegin[0] & 0x00000000ffffffff ) );
			//pPacket->WriteLongLE( (DWORD)( ( nOffsetBegin[0] & 0xffffffff00000000 ) >> 32 ) );
			//pPacket->WriteLongLE( (DWORD)( nOffsetBegin[1] & 0x00000000ffffffff ) );
			//pPacket->WriteLongLE( (DWORD)( ( nOffsetBegin[1] & 0xffffffff00000000 ) >> 32 ) );
			//pPacket->WriteLongLE( (DWORD)( nOffsetBegin[2] & 0x00000000ffffffff ) );
			//pPacket->WriteLongLE( (DWORD)( ( nOffsetBegin[2] & 0xffffffff00000000 ) >> 32 ) );
			//pPacket->WriteLongLE( (DWORD)( nOffsetEnd[0] & 0x00000000ffffffff ) );
			//pPacket->WriteLongLE( (DWORD)( ( nOffsetEnd[0] & 0xffffffff00000000 ) >> 32 ) );
			//pPacket->WriteLongLE( (DWORD)( nOffsetEnd[1] & 0x00000000ffffffff ) );
			//pPacket->WriteLongLE( (DWORD)( ( nOffsetEnd[1] & 0xffffffff00000000 ) >> 32 ) );
			//pPacket->WriteLongLE( (DWORD)( nOffsetEnd[2] & 0x00000000ffffffff ) );
			//pPacket->WriteLongLE( (DWORD)( ( nOffsetEnd[2] & 0xffffffff00000000 ) >> 32 ) );

			// If little Endian, no need to use above code
			pPacket->Write( &nOffsetBegin[0], 8 );
			pPacket->Write( &nOffsetBegin[1], 8 );
			pPacket->Write( &nOffsetBegin[2], 8 );
			pPacket->Write( &nOffsetEnd[0], 8 );
			pPacket->Write( &nOffsetEnd[1], 8 );
			pPacket->Write( &nOffsetEnd[2], 8 );
			Send( pPacket );
		}
		else
		{
			CEDPacket* pPacket = CEDPacket::New( ED2K_C2C_REQUESTPARTS );
			pPacket->Write( m_pDownload->m_oED2K );

			// This commented-out code is for BigEndian, only needed when ported to different platform.
			//pPacket->WriteLongLE( (DWORD)( nOffsetBegin[0] & 0x00000000ffffffff ) );
			//pPacket->WriteLongLE( (DWORD)( nOffsetBegin[1] & 0x00000000ffffffff ) );
			//pPacket->WriteLongLE( (DWORD)( nOffsetBegin[2] & 0x00000000ffffffff ) );
			//pPacket->WriteLongLE( (DWORD)( nOffsetEnd[0] & 0x00000000ffffffff ) );
			//pPacket->WriteLongLE( (DWORD)( nOffsetEnd[1] & 0x00000000ffffffff ) );
			//pPacket->WriteLongLE( (DWORD)( nOffsetEnd[2] & 0x00000000ffffffff ) );

			pPacket->Write( &nOffsetBegin[0], 4 );
			pPacket->Write( &nOffsetBegin[1], 4 );
			pPacket->Write( &nOffsetBegin[2], 4 );
			pPacket->Write( &nOffsetEnd[0], 4 );
			pPacket->Write( &nOffsetEnd[1], 4 );
			pPacket->Write( &nOffsetEnd[2], 4 );
			Send( pPacket );
		}

		while ( nCount-- )
		{
			int nType = ( m_nDownloaded == 0 || ( nOffsetBegin[nCount] % ED2K_PART_SIZE ) == 0 )
				? MSG_INFO : MSG_DEBUG;

			theApp.Message( (WORD)nType, IDS_DOWNLOAD_FRAGMENT_REQUEST,
				nOffsetBegin[nCount], nOffsetEnd[nCount], (LPCTSTR)m_pDownload->GetDisplayName(), (LPCTSTR)m_sAddress );
		}
	}

	// If there are no more possible chunks to request, and endgame is available but not active
	if ( oPossible.empty() && Settings.eDonkey.Endgame && ! m_pDownload->m_bTorrentEndgame )
	{
		// And the file is at least 100MB, with less than 1MB to go
		if ( ( m_pDownload->GetVolumeComplete() > 100*1024*1024 ) &&
			 ( m_pDownload->GetVolumeRemaining() <  1*1024*1024 ) )
		{
			// Then activate endgame
			m_pDownload->m_bTorrentEndgame = true;
			theApp.Message( MSG_DEBUG, _T("Activating endgame for ed2k transfer %s"), m_pDownload->m_sName );
		}
	}

	if ( ! m_oRequested.empty() )
		return true;

	Send( CEDPacket::New( ED2K_C2C_QUEUERELEASE ) );

	theApp.Message( MSG_INFO, IDS_DOWNLOAD_FRAGMENT_END, (LPCTSTR)m_sAddress );
	Close( TRI_TRUE );

	return false;
}
UINT MMDeviceAudioSource::GetNextBuffer(float curVolume)
{
    UINT captureSize = 0;
    HRESULT err = mmCapture->GetNextPacketSize(&captureSize);
    if(FAILED(err))
    {
        RUNONCE AppWarning(TEXT("MMDeviceAudioSource::GetBuffer: GetNextPacketSize failed"));
        return NoAudioAvailable;
    }

    float *outputBuffer = NULL;

    if(captureSize)
    {
        LPBYTE captureBuffer;
        DWORD dwFlags = 0;
        UINT numAudioFrames = 0;

        UINT64 devPosition;
        UINT64 qpcTimestamp;
        err = mmCapture->GetBuffer(&captureBuffer, &numAudioFrames, &dwFlags, &devPosition, &qpcTimestamp);
        if(FAILED(err))
        {
            RUNONCE AppWarning(TEXT("MMDeviceAudioSource::GetBuffer: GetBuffer failed"));
            return NoAudioAvailable;
        }

        QWORD newTimestamp;

        if(dwFlags & AUDCLNT_BUFFERFLAGS_TIMESTAMP_ERROR)
        {
            RUNONCE AppWarning(TEXT("MMDeviceAudioSource::GetBuffer: woa woa woa, getting timestamp errors from the audio subsystem.  device = %s"), GetDeviceName().Array());
            if(!bBrokenTimestamp)
                newTimestamp = lastUsedTimestamp + numAudioFrames*1000/inputSamplesPerSec;
        }
        else
        {
            if(!bBrokenTimestamp)
                newTimestamp = qpcTimestamp/10000;

            /*UINT64 freq;
            mmClock->GetFrequency(&freq);
            Log(TEXT("position: %llu, numAudioFrames: %u, freq: %llu, newTimestamp: %llu, test: %llu"), devPosition, numAudioFrames, freq, newTimestamp, devPosition*8000/freq);*/
        }

        //have to do this crap to account for broken devices or device drivers.  absolutely unbelievable.
        if(!bFirstFrameReceived)
        {
            LARGE_INTEGER clockFreq;
            QueryPerformanceFrequency(&clockFreq);
            QWORD curTime = GetQPCTimeMS(clockFreq.QuadPart);

            if(newTimestamp < (curTime-1000) || newTimestamp > (curTime+1000))
            {
                bBrokenTimestamp = true;

                Log(TEXT("MMDeviceAudioSource::GetNextBuffer: Got bad audio timestamp offset %lld from device: '%s', timestamps for this device will be calculated.  curTime: %llu, newTimestamp: %llu"), (LONGLONG)(newTimestamp - curTime), GetDeviceName().Array(), curTime, newTimestamp);
                lastUsedTimestamp = newTimestamp = curTime;
            }
            else
                lastUsedTimestamp = newTimestamp;

            bFirstFrameReceived = true;
        }

        if(tempBuffer.Num() < numAudioFrames*2)
            tempBuffer.SetSize(numAudioFrames*2);

        outputBuffer = tempBuffer.Array();
        float *tempOut = outputBuffer;

        //------------------------------------------------------------
        // channel upmix/downmix

        if(inputChannels == 1)
        {
            UINT  numFloats   = numAudioFrames;
            float *inputTemp  = (float*)captureBuffer;
            float *outputTemp = outputBuffer;

            if(App->SSE2Available() && (UPARAM(inputTemp) & 0xF) == 0 && (UPARAM(outputTemp) & 0xF) == 0)
            {
                UINT alignedFloats = numFloats & 0xFFFFFFFC;
                for(UINT i=0; i<alignedFloats; i += 4)
                {
                    __m128 inVal   = _mm_load_ps(inputTemp+i);

                    __m128 outVal1 = _mm_unpacklo_ps(inVal, inVal);
                    __m128 outVal2 = _mm_unpackhi_ps(inVal, inVal);

                    _mm_store_ps(outputTemp+(i*2),   outVal1);
                    _mm_store_ps(outputTemp+(i*2)+4, outVal2);
                }

                numFloats  -= alignedFloats;
                inputTemp  += alignedFloats;
                outputTemp += alignedFloats*2;
            }

            while(numFloats--)
            {
                float inputVal = *inputTemp;
                *(outputTemp++) = inputVal;
                *(outputTemp++) = inputVal;

                inputTemp++;
            }
        }
        else if(inputChannels == 2) //straight up copy
        {
            if(App->SSE2Available())
                SSECopy(outputBuffer, captureBuffer, numAudioFrames*2*sizeof(float));
            else
                mcpy(outputBuffer, captureBuffer, numAudioFrames*2*sizeof(float));
        }
        else
        {
            //todo: downmix optimization, also support for other speaker configurations than ones I can merely "think" of.  ugh.
            float *inputTemp  = (float*)captureBuffer;
            float *outputTemp = outputBuffer;

            if(inputChannelMask == KSAUDIO_SPEAKER_QUAD)
            {
                UINT numFloats = numAudioFrames*4;
                float *endTemp = inputTemp+numFloats;

                while(inputTemp < endTemp)
                {
                    float left      = inputTemp[0];
                    float right     = inputTemp[1];
                    float rear      = (inputTemp[2]+inputTemp[3])*surroundMix;

                    *(outputTemp++) = left  - rear;
                    *(outputTemp++) = right + rear;

                    inputTemp  += 4;
                }
            }
            else if(inputChannelMask == KSAUDIO_SPEAKER_2POINT1)
            {
                UINT numFloats = numAudioFrames*3;
                float *endTemp = inputTemp+numFloats;

                while(inputTemp < endTemp)
                {
                    float left      = inputTemp[0];
                    float right     = inputTemp[1];
                    float lfe       = inputTemp[2]*lowFreqMix;

                    *(outputTemp++) = left  + lfe;
                    *(outputTemp++) = right + lfe;

                    inputTemp  += 3;
                }
            }
            else if(inputChannelMask == KSAUDIO_SPEAKER_4POINT1)
            {
                UINT numFloats = numAudioFrames*5;
                float *endTemp = inputTemp+numFloats;

                while(inputTemp < endTemp)
                {
                    float left      = inputTemp[0];
                    float right     = inputTemp[1];
                    float lfe       = inputTemp[2]*lowFreqMix;
                    float rear      = (inputTemp[3]+inputTemp[4])*surroundMix;

                    *(outputTemp++) = left  + lfe - rear;
                    *(outputTemp++) = right + lfe + rear;

                    inputTemp  += 5;
                }
            }
            else if(inputChannelMask == KSAUDIO_SPEAKER_SURROUND)
            {
                UINT numFloats = numAudioFrames*4;
                float *endTemp = inputTemp+numFloats;

                while(inputTemp < endTemp)
                {
                    float left      = inputTemp[0];
                    float right     = inputTemp[1];
                    float center    = inputTemp[2]*centerMix;
                    float rear      = inputTemp[3]*(surroundMix*dbMinus3);

                    *(outputTemp++) = left  + center - rear;
                    *(outputTemp++) = right + center + rear;

                    inputTemp  += 4;
                }
            }
            //don't think this will work for both
            else if(inputChannelMask == KSAUDIO_SPEAKER_5POINT1)
            {
                UINT numFloats = numAudioFrames*6;
                float *endTemp = inputTemp+numFloats;

                while(inputTemp < endTemp)
                {
                    float left      = inputTemp[0];
                    float right     = inputTemp[1];
                    float center    = inputTemp[2]*centerMix;
                    float lowFreq   = inputTemp[3]*lowFreqMix;
                    float rear      = (inputTemp[4]+inputTemp[5])*surroundMix;

                    *(outputTemp++) = left  + center + lowFreq - rear;
                    *(outputTemp++) = right + center + lowFreq + rear;

                    inputTemp  += 6;
                }
            }
            //todo ------------------
            //not sure if my 5.1/7.1 downmixes are correct
            else if(inputChannelMask == KSAUDIO_SPEAKER_5POINT1_SURROUND)
            {
                UINT numFloats = numAudioFrames*6;
                float *endTemp = inputTemp+numFloats;

                while(inputTemp < endTemp)
                {
                    float left      = inputTemp[0];
                    float right     = inputTemp[1];
                    float center    = inputTemp[2]*centerMix;
                    float lowFreq   = inputTemp[3]*lowFreqMix;
                    float sideLeft  = inputTemp[4]*dbMinus3;
                    float sideRight = inputTemp[5]*dbMinus3;

                    *(outputTemp++) = left  + center + sideLeft  + lowFreq;
                    *(outputTemp++) = right + center + sideRight + lowFreq;

                    inputTemp  += 6;
                }
            }
            else if(inputChannelMask == KSAUDIO_SPEAKER_7POINT1)
            {
                UINT numFloats = numAudioFrames*8;
                float *endTemp = inputTemp+numFloats;

                while(inputTemp < endTemp)
                {
                    float left          = inputTemp[0];
                    float right         = inputTemp[1];
                    float center        = inputTemp[2]*(centerMix*dbMinus3);
                    float lowFreq       = inputTemp[3]*lowFreqMix;
                    float rear          = (inputTemp[4]+inputTemp[5])*surroundMix;
                    float centerLeft    = inputTemp[6]*dbMinus6;
                    float centerRight   = inputTemp[7]*dbMinus6;

                    *(outputTemp++) = left  + centerLeft  + center + lowFreq - rear;
                    *(outputTemp++) = right + centerRight + center + lowFreq + rear;

                    inputTemp  += 8;
                }
            }
            else if(inputChannelMask == KSAUDIO_SPEAKER_7POINT1_SURROUND)
            {
                UINT numFloats = numAudioFrames*8;
                float *endTemp = inputTemp+numFloats;

                while(inputTemp < endTemp)
                {
                    float left      = inputTemp[0];
                    float right     = inputTemp[1];
                    float center    = inputTemp[2]*centerMix;
                    float lowFreq   = inputTemp[3]*lowFreqMix;
                    float rear      = (inputTemp[4]+inputTemp[5])*(surroundMix*dbMinus3);
                    float sideLeft  = inputTemp[6]*dbMinus6;
                    float sideRight = inputTemp[7]*dbMinus6;

                    *(outputTemp++) = left  + sideLeft + center + lowFreq - rear;
                    *(outputTemp++) = right + sideLeft + center + lowFreq + rear;

                    inputTemp  += 8;
                }
            }
        }

        mmCapture->ReleaseBuffer(numAudioFrames);

        //------------------------------------------------------------
        // resample

        if(bResample)
        {
            UINT frameAdjust = UINT((double(numAudioFrames) * resampleRatio) + 1.0);
            UINT newFrameSize = frameAdjust*2;

            if(tempResampleBuffer.Num() < newFrameSize)
                tempResampleBuffer.SetSize(newFrameSize);

            SRC_DATA data;
            data.src_ratio = resampleRatio;

            data.data_in = tempBuffer.Array();
            data.input_frames = numAudioFrames;

            data.data_out = tempResampleBuffer.Array();
            data.output_frames = frameAdjust;

            data.end_of_input = 0;

            int err = src_process(resampler, &data);
            if(err)
            {
                RUNONCE AppWarning(TEXT("Was unable to resample audio"));
                return NoAudioAvailable;
            }

            if(data.input_frames_used != numAudioFrames)
            {
                RUNONCE AppWarning(TEXT("Failed to downsample buffer completely, which shouldn't actually happen because it should be using 10ms of samples"));
                return NoAudioAvailable;
            }

            numAudioFrames = data.output_frames_gen;
        }

        //-----------------------------------------------------------------------------
        // sort all audio frames into 10 millisecond increments (done because not all devices output in 10ms increments)
        // NOTE: 0.457+ - instead of using the timestamps from windows, just compare and make sure it stays within a 100ms of their timestamps

        float *newBuffer = (bResample) ? tempResampleBuffer.Array() : tempBuffer.Array();

        if(storageBuffer.Num() == 0 && numAudioFrames == 441)
        {
            lastUsedTimestamp += 10;
            if(!bBrokenTimestamp) 
            {
                QWORD difVal = GetQWDif(newTimestamp, lastUsedTimestamp);
                if(difVal > 70)
                    lastUsedTimestamp = newTimestamp;
            }

            if(lastUsedTimestamp > lastSentTimestamp)
            {
                QWORD adjustVal = (lastUsedTimestamp-lastSentTimestamp);
                if(adjustVal < 10)
                    lastUsedTimestamp += 10-adjustVal;

                AudioSegment &newSegment = *audioSegments.CreateNew();
                newSegment.audioData.CopyArray(newBuffer, numAudioFrames*2);
                newSegment.timestamp = lastUsedTimestamp;
                MultiplyAudioBuffer(newSegment.audioData.Array(), numAudioFrames*2, curVolume);

                lastSentTimestamp = lastUsedTimestamp;
            }
        }
        else
        {
            UINT storedFrames = storageBuffer.Num();

            storageBuffer.AppendArray(newBuffer, numAudioFrames*2);
            if(storageBuffer.Num() >= (441*2))
            {
                lastUsedTimestamp += 10;
                if(!bBrokenTimestamp)
                {
                    QWORD difVal = GetQWDif(newTimestamp, lastUsedTimestamp);
                    if(difVal > 70)
                        lastUsedTimestamp = newTimestamp - (QWORD(storedFrames)/2*1000/44100);
                }

                //------------------------
                // add new data

                if(lastUsedTimestamp > lastSentTimestamp)
                {
                    QWORD adjustVal = (lastUsedTimestamp-lastSentTimestamp);
                    if(adjustVal < 10)
                        lastUsedTimestamp += 10-adjustVal;

                    AudioSegment &newSegment = *audioSegments.CreateNew();
                    newSegment.audioData.CopyArray(storageBuffer.Array(), (441*2));
                    newSegment.timestamp = lastUsedTimestamp;
                    MultiplyAudioBuffer(newSegment.audioData.Array(), 441*2, curVolume);

                    storageBuffer.RemoveRange(0, (441*2));
                }

                //------------------------
                // if still data pending (can happen)

                while(storageBuffer.Num() >= (441*2))
                {
                    lastUsedTimestamp += 10;

                    if(lastUsedTimestamp > lastSentTimestamp)
                    {
                        QWORD adjustVal = (lastUsedTimestamp-lastSentTimestamp);
                        if(adjustVal < 10)
                            lastUsedTimestamp += 10-adjustVal;

                        AudioSegment &newSegment = *audioSegments.CreateNew();
                        newSegment.audioData.CopyArray(storageBuffer.Array(), (441*2));
                        storageBuffer.RemoveRange(0, (441*2));
                        MultiplyAudioBuffer(newSegment.audioData.Array(), 441*2, curVolume);

                        newSegment.timestamp = lastUsedTimestamp;

                        lastSentTimestamp = lastUsedTimestamp;
                    }
                }
            }
        }

        //-----------------------------------------------------------------------------

        return ContinueAudioRequest;
    }

    return NoAudioAvailable;
}
void CDownloadWithTiger::FinishValidation()
{
    FF::SimpleFragmentList oCorrupted( m_nSize );

	if ( m_nVerifyHash == HASH_TIGERTREE )
	{
		if ( m_pTigerTree.FinishBlockTest( m_nVerifyBlock ) )
		{
			m_pTigerBlock[ m_nVerifyBlock ] = TS_TRUE;
			m_nTigerSuccess ++;
		}
		else
		{
			m_pTigerBlock[ m_nVerifyBlock ] = TS_FALSE;

            QWORD nOffset = QWORD(m_nVerifyBlock) * QWORD(m_nTigerSize);
            oCorrupted.insert( oCorrupted.end(), FF::SimpleFragment( nOffset,
                ::std::min( nOffset + m_nTigerSize, m_nSize ) ) );
		}
	}
	else if ( m_nVerifyHash == HASH_ED2K )
	{
		if ( m_pHashset.FinishBlockTest( m_nVerifyBlock ) )
		{
			m_pHashsetBlock[ m_nVerifyBlock ] = TS_TRUE;
			m_nHashsetSuccess ++;
		}
		else
		{
			m_pHashsetBlock[ m_nVerifyBlock ] = TS_FALSE;

            QWORD nOffset = QWORD(m_nVerifyBlock) * QWORD(ED2K_PART_SIZE);
            oCorrupted.insert( oCorrupted.end(), FF::SimpleFragment( nOffset,
                ::std::min( nOffset + ED2K_PART_SIZE, m_nSize ) ) );
		}
	}
	else if ( m_nVerifyHash == HASH_TORRENT )
	{
		if ( m_pTorrent.FinishBlockTest( m_nVerifyBlock ) )
		{
			m_pTorrentBlock[ m_nVerifyBlock ] = TS_TRUE;
			m_nTorrentSuccess ++;

			OnFinishedTorrentBlock( m_nVerifyBlock );
		}
		else
		{
			m_pTorrentBlock[ m_nVerifyBlock ] = TS_FALSE;

            QWORD nOffset = QWORD(m_nVerifyBlock) * QWORD(m_nTorrentSize);
            oCorrupted.insert( oCorrupted.end(), FF::SimpleFragment( nOffset,
                ::std::min( nOffset + m_nTorrentSize, m_nSize ) ) );
		}
	}

	if ( !oCorrupted.empty() && m_pFile != NULL )
	{
		if ( m_pTigerBlock != NULL )
			SubtractHelper( oCorrupted, m_pTigerBlock, m_nTigerBlock, m_nTigerSize );
		if ( m_pHashsetBlock != NULL )
			SubtractHelper( oCorrupted, m_pHashsetBlock, m_nHashsetBlock, ED2K_PART_SIZE );
		if ( m_pTorrentBlock != NULL )
			SubtractHelper( oCorrupted, m_pTorrentBlock, m_nTorrentBlock, m_nTorrentSize );

        for ( FF::SimpleFragmentList::ConstIterator pRange = oCorrupted.begin();
            pRange != oCorrupted.end(); ++pRange )
		{
			m_pFile->InvalidateRange( pRange->begin(), pRange->length() );
			RemoveOverlappingSources( pRange->begin(), pRange->length() );
		}

    }
	m_nVerifyHash	= HASH_NULL;
	m_nVerifyBlock	= 0xFFFFFFFF;
	m_nVerifyCookie++;

	SetModified();
}