Make your own free website on Tripod.com

Difference Analysis Generated by HtmlDiff on 01.03.2003 16:52  

Base file: eMule0.26d.Maella.v2.0.beta6\src\DownloadClient.cpp

Modified file: vampirev1esrc\src\DownloadClient.cpp

//this file is part of eMule
//Copyright (C)2002 Merkur ( merkur-@users.sourceforge.net / http://www.emule-project.net )
//
//This program is free software; you can redistribute it and/or
//modify it under the terms of the GNU General Public License
//as published by the Free Software Foundation; either
//version 2 of the License, or (at your option) any later version.
//
//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.
//
//
//You should have received a copy of the GNU General Public License
//along with this program; if not, write to the Free Software
//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

#include "StdAfx.h"
#include "zlib/zlib.h"
#include "updownclient.h"
#include "partfile.h"
#include "opcodes.h"
#include "packets.h"
#include "emule.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif


//  members of CUpDownClient
//  which are mainly used for downloading functions 
CBarShader CUpDownClient::s_StatusBar(16);
void CUpDownClient::DrawStatusBar(CDC* dc, RECT* rect, bool onlygreyrect, bool  bFlat){ 
    COLORREF crBoth; 
    COLORREF crNeither; 
    COLORREF crClientOnly; 
    COLORREF crPending;
    COLORREF crNextPending;

    if(bFlat) { 
        crBoth = RGB(0, 150, 0);
        crNeither = RGB(224, 224, 224);
        crClientOnly = RGB(0, 0, 0);
        crPending = RGB(255,208,0);
        crNextPending = RGB(255,255,100);
    } else { 
        crBoth = RGB(0, 192, 0);
        crNeither = RGB(240, 240, 240);
        crClientOnly = RGB(104, 104, 104);
        crPending = RGB(255, 208, 0);
        crNextPending = RGB(255,255,100);
    } 

    s_StatusBar.SetFileSize(reqfile->GetFileSize()); 
    s_StatusBar.SetHeight(rect->bottom - rect->top); 
    s_StatusBar.SetWidth(rect->right - rect->left); 
    s_StatusBar.Fill(crNeither); 

    // Barry - was only showing one part from client, even when reserved bits from 2 partspartss
    CString gettingParts;
    ShowDownloadingParts(&gettingParts);

    if (!onlygreyrect && reqfile && m_abyPartStatus) { 
        for (uint32 i = 0;i != m_nPartCount;i++){ 
            if (m_abyPartStatus[i]){ 
                uint32 uEnd; 
                if (PARTSIZE*(i+1) > reqfile->GetFileSize()) 
                    uEnd = reqfile->GetFileSize(); 
                else 
                    uEnd = PARTSIZE*(i+1); 

                if (reqfile->IsComplete(PARTSIZE*i,PARTSIZE*(i+1)-1)) 
                    s_StatusBar.FillRange(PARTSIZE*i, uEnd, crBoth);
                else if (m_nDownloadState == DS_DOWNLOADING && m_nLastBlockOffset < uEnd &&
                         m_nLastBlockOffset >= PARTSIZE*i)
                    s_StatusBar.FillRange(PARTSIZE*i, uEnd, crPending);
                else if (gettingParts.GetAt(i) == 'Y')
                    s_StatusBar.FillRange(PARTSIZE*i, uEnd, crNextPending);
                else
                    s_StatusBar.FillRange(PARTSIZE*i, uEnd, crClientOnly);
            } 
        } 
    } 
    s_StatusBar.Draw(dc, rect->left, rect->top, bFlat); 
} 


bool CUpDownClient::Compare(CUpDownClient* tocomp){
    if(HasValidHash() && tocomp->HasValidHash())
        return !memcmp(this->GetUserHash(), tocomp->GetUserHash(), 16);
    if (HasLowID())
        return ((this->GetUserID() == tocomp->GetUserID()) && (GetServerIP() == tocomp->GetServerIP()));
    else
        return (this->GetUserID() == tocomp->GetUserID() || (this->GetIP() && this->GetIP() == tocomp->GetIP()) );
}

void CUpDownClient::AskForDownload(){
    if (theApp.listensocket->TooManySockets() && !(socket && socket->IsConnected()) ){
        if (GetDownloadState() != DS_TOOMANYCONNS)
            SetDownloadState(DS_TOOMANYCONNS);
        return;
    }

    m_bUDPPending = false;
    m_dwLastAskedTime = ::GetTickCount();
    SetDownloadState(DS_CONNECTING);
    TryToConnect();

}

bool CUpDownClient::IsSourceRequestAllowed() {
    DWORD dwTickCount = ::GetTickCount() + CONNECTION_LATENCY;
    int nTimePassedClient = dwTickCount - GetLastSrcAnswerTime();
    int nTimePassedFile   = dwTickCount - reqfile->GetLastAnsweredTime();
    bool bNeverAskedBefore = GetLastSrcReqTime() == 0;

    return (
             //if client has the correct extended protocol
             ExtProtocolAvailable() && m_bySourceExchangeVer >= 1 &&
             //AND if we need more sources
             theApp.glob_prefs->GetMaxSourcePerFileSoft() > reqfile->GetSourceCount() &&
             //AND if...
             (
               //source is not complete and file is rare, allow once every 10 minutes
               ( !m_bCompleteSource &&
                 ( reqfile->GetSourceCount() - reqfile->GetValidSourcesCount() <= RARE_FILE / 4 ||
                   reqfile->GetSourceCount() <= RARE_FILE * 2
                 ) &&
                 (bNeverAskedBefore || nTimePassedClient > SOURCECLIENTREASK)
               ) ||
               // otherwise, allow every 90 minutes, but only if we haven't
               //   asked someone else in last 10 minutes
               ( (bNeverAskedBefore || nTimePassedClient > SOURCECLIENTREASK * reqfile->GetCommonFilePenalty()) &&
                 (nTimePassedFile > SOURCECLIENTREASK)
               )
             )
           );
}

void CUpDownClient::SendFileRequest(){
    ASSERT(reqfile != NULL);
    if(!reqfile)
        return;
    AddAskedCountDown();
    CMemFile* data = new CMemFile();
    data->Write(reqfile->GetFileHash(),16);
    if( GetExtendedRequestsVersion() > 0 ){
        reqfile->WritePartStatus(data);
    }
    Packet* packet = new Packet(data);
    packet->opcode=OP_FILEREQUEST;
    theApp.uploadqueue->AddUpDataOverheadFileRequest(packet->size);
    socket->SendPacket(packet, true);
    delete data;
    data = new CMemFile();
    data->Write(reqfile->GetFileHash(),16);
    packet = new Packet(data);
    packet->opcode = OP_SETREQFILEID;
    theApp.uploadqueue->AddUpDataOverheadFileRequest(packet->size);
    socket->SendPacket(packet, true);
    delete data;
    if( IsEmuleClient() )
        SetRemoteQueueFull( true );
    SetRemoteQueueRank(0);
    
    if(theApp.glob_prefs->GetDisabledXS() == false && IsSourceRequestAllowed() == true){ // Maella => -Enable/Disable source exchange in preference- (Tarod)       
        reqfile->SetLastAnsweredTimeTimeout();
        Packet* packet = new Packet(OP_REQUESTSOURCES,16,OP_EMULEPROT);
        memcpy(packet->pBuffer,reqfile->GetFileHash(),16);
        theApp.uploadqueue->AddUpDataOverheadSourceExchange(packet->size);
        socket->SendPacket(packet,true,true);
        theApp.emuledlg->AddDebugLogLine( false, "Send:Source Request User(%s) File(%s)", GetUserName(), reqfile->GetFileName() );
    }
}

void CUpDownClient::ProcessFileInfo(char* packet,uint32 size){
    CSafeMemFile* data = new CSafeMemFile((BYTE*)packet,size);
    uchar cfilehash[16];
    data->Read(cfilehash,16);
    uint16 namelength;
    data->Read(&namelength,2);
    if (m_pszClientFilename)
        delete[] m_pszClientFilename;
    m_pszClientFilename = new char[namelength+1];
    memset(m_pszClientFilename, 0, namelength+1);
    data->Read(m_pszClientFilename,namelength); // TODO why does this overwrite the end of the buffer 
                                                    //- because sizeof(uint16) is always 2 
    delete data;
    if ( (!reqfile) || memcmp(cfilehash,reqfile->GetFileHash(),16))
        throw CString(GetResString(IDS_ERR_WRONGFILEID)+ " (ProcessFileInfo)");
}

void CUpDownClient::ProcessFileStatus(char* packet,uint32 size){
    CSafeMemFile* data = new CSafeMemFile((BYTE*)packet,size);
    uchar cfilehash[16];
    data->Read(cfilehash,16);
    if ( (!reqfile) || memcmp(cfilehash,reqfile->GetFileHash(),16)){
        delete data;    //mf
        throw CString(GetResString(IDS_ERR_WRONGFILEID)+ " (ProcessFileStatus)");   
    }
    data->Read(&m_nPartCount,2);
    if (m_abyPartStatus){
        delete[] m_abyPartStatus;
        m_abyPartStatus = NULL;
    }
    bool bPartsNeeded = false;
    if (!m_nPartCount){
        m_nPartCount = reqfile->GetPartCount();
        m_abyPartStatus = new uint8[m_nPartCount];
        memset(m_abyPartStatus,1,m_nPartCount);
        bPartsNeeded = true;
        m_bCompleteSource = true;
    }
    else{
        if (reqfile->GetPartCount() != m_nPartCount){
            delete data;    //mf
            throw GetResString(IDS_ERR_WRONGPARTNUMBER);
        }
        m_bCompleteSource = false;
        m_abyPartStatus = new uint8[m_nPartCount];
        uint16 done = 0;
        while (done != m_nPartCount){
            uint8 toread;
            data->Read(&toread,1);
            for (sint32 i = 0;i != 8;i++){
                m_abyPartStatus[done] = ((toread>>i)&1)? 1:0;   
                if (m_abyPartStatus[done] && !reqfile->IsComplete(done*PARTSIZE,((done+1)*PARTSIZE)-1))
                    bPartsNeeded = true;
                done++;
                if (done == m_nPartCount)
                    break;
            }
        }
    }

    //theApp.emuledlg->transferwnd.downloadlistctrl.UpdateItem(this);
    UpdateDisplayedInfo();
    reqfile->UpdateAvailablePartsCount();

    if (!bPartsNeeded)
        SetDownloadState(DS_NONEEDEDPARTS);
    else if (reqfile->hashsetneeded){
        Packet* packet = new Packet(OP_HASHSETREQUEST,16);
        memcpy(packet->pBuffer,reqfile->GetFileHash(),16);
        theApp.uploadqueue->AddUpDataOverheadFileRequest(packet->size);
        socket->SendPacket(packet, true, true);
        SetDownloadState(DS_REQHASHSET);
        reqfile->hashsetneeded = false;
    }
    else{
        SetDownloadState(DS_ONQUEUE);
        delete data;
        data = new CSafeMemFile(16);
        data->Write(this->reqfile->GetFileHash(),16);
        Packet* packet = new Packet(data);
        packet->opcode = OP_STARTUPLOADREQ;
        theApp.uploadqueue->AddUpDataOverheadFileRequest(packet->size);
        socket->SendPacket(packet, true, true);
    }
    reqfile->NewSrcPartsInfo();
    delete data;
}

bool CUpDownClient::AddRequestForAnotherFile(CPartFile* file){
    for (POSITION pos = m_OtherNoNeeded_list.GetHeadPosition();pos != 0;m_OtherNoNeeded_list.GetNext(pos)){
        if (m_OtherNoNeeded_list.GetAt(pos) == file)
            return false;
    }
    for (POSITION pos = m_OtherRequests_list.GetHeadPosition();pos != 0;m_OtherRequests_list.GetNext(pos)){
        if (m_OtherRequests_list.GetAt(pos) == file)
            return false;
    }
    m_OtherRequests_list.AddTail(file);
    return true;
}

void CUpDownClient::SetDownloadState(uint8 byNewState){
    if (m_nDownloadState != byNewState){

        // Maella -Downloading source list-
        if(byNewState == DS_DOWNLOADING){
            reqfile->AddDownloadingSource(this);
        }
        else if(m_nDownloadState == DS_DOWNLOADING){
            reqfile->RemoveDownloadingSource(this);
        }

        if(byNewState != DS_DOWNLOADING && socket != NULL){
            socket->DisableDownloadLimit();
        }
        // Maella end

        if (m_nDownloadState == DS_DOWNLOADING ){
            m_nDownloadState = byNewState;
            for (POSITION pos = m_DownloadBlocks_list.GetHeadPosition();pos != 0;m_DownloadBlocks_list.GetNext(pos)){
                Requested_Block_Struct* cur_block = m_DownloadBlocks_list.GetAt(pos);
                reqfile->RemoveBlockFromList(cur_block->StartOffset,cur_block->EndOffset);
                delete m_DownloadBlocks_list.GetAt(pos);
            }
            m_DownloadBlocks_list.RemoveAll();

            for (POSITION pos = m_PendingBlocks_list.GetHeadPosition();pos != 0;m_PendingBlocks_list.GetNext(pos))
            {
                Pending_Block_Struct *pending = m_PendingBlocks_list.GetAt(pos);
                if (reqfile)
                    reqfile->RemoveBlockFromList(pending->block->StartOffset, pending->block->EndOffset);
                
                delete pending->block;
                // Not always allocated
                if (pending->zStream) delete pending->zStream;
                delete pending;
            }
            m_PendingBlocks_list.RemoveAll();
            m_nDownDatarate = 0;
            if (byNewState == DS_NONE){
                if (m_abyPartStatus)
                    delete[] m_abyPartStatus;
                m_abyPartStatus = 0;
            }
            if (socket && byNewState != DS_ERROR)
                    socket->DisableDownloadLimit();
        }
        m_nDownloadState = byNewState;
        if( GetDownloadState() == DS_DOWNLOADING ){
            if ( IsEmuleClient() )
                SetRemoteQueueFull(false);
            SetRemoteQueueRank(1);
            SetAskedCountDown(0);
        }
        //theApp.emuledlg->transferwnd.downloadlistctrl.UpdateItem(this);
        UpdateDisplayedInfo(true);
    }
}

void CUpDownClient::ProcessHashSet(char* packet,uint32 size){
    if ( (!reqfile) || memcmp(packet,reqfile->GetFileHash(),16))
        throw CString(GetResString(IDS_ERR_WRONGFILEID)+" (ProcessHashSet)");   
    CSafeMemFile* data = new CSafeMemFile((BYTE*)packet,size);
    if (reqfile->LoadHashsetFromFile(data,true)){
    }
    else{
        reqfile->hashsetneeded = true;
        delete data;    //mf
        throw GetResString(IDS_ERR_BADHASHSET);
    }
    SetDownloadState(DS_ONQUEUE);
    delete data;
    data = new CSafeMemFile(16);
    data->Write(this->reqfile->GetFileHash(),16);
    Packet* opacket = new Packet(data);
    opacket->opcode = OP_STARTUPLOADREQ;
    theApp.uploadqueue->AddUpDataOverheadFileRequest(opacket->size);
    socket->SendPacket(opacket, true, true);
    delete data;
}

void CUpDownClient::SendBlockRequests(){
    m_dwLastBlockReceived = ::GetTickCount();
    if (!reqfile)
        return;
    if (m_DownloadBlocks_list.IsEmpty())
    {
        // Barry - instead of getting 3, just get how many is needed
        uint16 count = 3 - m_PendingBlocks_list.GetCount();
        Requested_Block_Struct** toadd = new Requested_Block_Struct*[count];
        if (reqfile->GetNextRequestedBlock(this,toadd,&count)){
            for (int i = 0; i != count; i++)
                m_DownloadBlocks_list.AddTail(toadd[i]);            
        }
        delete[] toadd;
    }

    // Barry - Why are unfinished blocks requested again, not just new ones?

    while (m_PendingBlocks_list.GetCount() < 3 && !m_DownloadBlocks_list.IsEmpty()){
        Pending_Block_Struct* pblock = new Pending_Block_Struct;
        pblock->block = m_DownloadBlocks_list.RemoveHead();
        pblock->zStream = NULL;
        pblock->totalUnzipped = 0;
        m_PendingBlocks_list.AddTail(pblock);
    }
    if (m_PendingBlocks_list.IsEmpty()){
        Packet* packet = new Packet(OP_CANCELTRANSFER,0);
        theApp.uploadqueue->AddUpDataOverheadFileRequest(packet->size);
        socket->SendPacket(packet,true,true);
        SetDownloadState(DS_NONEEDEDPARTS);
        return;
    }
    Packet* packet = new Packet(OP_REQUESTPARTS,40);
    CMemFile* data = new CMemFile((BYTE*)packet->pBuffer,40);
    data->Write(reqfile->GetFileHash(),16);
    POSITION pos = m_PendingBlocks_list.GetHeadPosition();
    uint32 null = 0;
    Requested_Block_Struct* block;
    for (uint32 i = 0; i != 3; i++){
        if (pos){
            block = m_PendingBlocks_list.GetAt(pos)->block;
            m_PendingBlocks_list.GetNext(pos);
            data->Write(&block->StartOffset,4);
        }
        else
            data->Write(&null,4);
    }
    pos = m_PendingBlocks_list.GetHeadPosition();
    for (uint32 i = 0; i != 3; i++){
        if (pos){
            block = m_PendingBlocks_list.GetAt(pos)->block;
            m_PendingBlocks_list.GetNext(pos);
            uint32 endpos = block->EndOffset+1;
            data->Write(&endpos,4);
        }
        else
            data->Write(&null,4);
    }
    delete data;
    theApp.uploadqueue->AddUpDataOverheadFileRequest(packet->size);
    socket->SendPacket(packet,true,true);
}

/* Barry - Originally this only wrote to disk when a full 180k block 
           had been received from a client, and only asked for data in 
           180k blocks.

           This meant that on average 90k was lost for every connection
           to a client data source. That is a lot of wasted data.

           To reduce the lost data, packets are now written to a buffer
           and flushed to disk regularly regardless of size downloaded.
           This includes compressed packets.

           Data is also requested only where gaps are, not in 180k blocks.
           The requests will still not exceed 180k, but may be smaller to
           fill a gap.
*/
void CUpDownClient::ProcessBlockPacket(char *packet, uint32 size, bool packed)
{
  try {
    // Ignore if no data required
    if (!(GetDownloadState() == DS_DOWNLOADING || GetDownloadState() == DS_NONEEDEDPARTS))
        return;

    const int HEADER_SIZE = 24; // FileId(16) + StartOffset(4) + EndOffset(4)
                                // FileId(16) + StartOffset(4) + Size(4)

    // Update stats
    m_dwLastBlockReceived = ::GetTickCount();

    // Read data from packet
    CSafeMemFile *data = new CSafeMemFile((BYTE*)packet, size);
    uchar fileID[16];
    data->Read(fileID, 16);

    // Check that this data is for the correct file
    if ( (!reqfile) || memcmp(packet, reqfile->GetFileHash(), 16))
    {
        delete data;
        throw CString(GetResString(IDS_ERR_WRONGFILEID) + " (ProcessBlockPacket)");
    }

    // Find the start & end positions, and size of this chunk of data
    uint32 nStartPos;
    uint32 nEndPos;
    uint32 nBlockSize = 0;
    data->Read(&nStartPos, 4);
    if (packed)
    {
        data->Read(&nBlockSize, 4);
        nEndPos = nStartPos + (size - HEADER_SIZE);
        usedcompressiondown = true;
    }
    else
        data->Read(&nEndPos,4);
    delete data;

    // Check that packet size matches the declared data size + header size (24)
    if ( size != ((nEndPos - nStartPos) + HEADER_SIZE))
        throw CString(GetResString(IDS_ERR_BADDATABLOCK)+" (ProcessBlockPacket)");

    // Move end back one, should be inclusive 

    // Maella -Accurate download rate measurement directly at socket-
    credits->AddDownloaded(size - HEADER_SIZE);
    // Maella end

    nEndPos--;

    // Loop through to find the reserved block that this is within
    Pending_Block_Struct *cur_block;
    for (POSITION pos = m_PendingBlocks_list.GetHeadPosition(); pos != NULL; m_PendingBlocks_list.GetNext(pos))
    {
        cur_block = m_PendingBlocks_list.GetAt(pos);
        if ((cur_block->block->StartOffset <= nStartPos) && (cur_block->block->EndOffset >= nStartPos))
        {
            // Found reserved block

            // Remember this start pos, used to draw part downloading in list
            m_nLastBlockOffset = nStartPos;  

            // Occasionally packets are duplicated, no point writing it twice
            // This will be 0 in these cases, or the length written otherwise
            uint32 lenWritten = 0;

            // Handle differently depending on whether packed or not
            if (!packed)
            {
                // Write to disk (will be buffered in part file class)
                lenWritten = reqfile->WriteToBuffer(size - HEADER_SIZE, 
                                                    (BYTE *) (packet + HEADER_SIZE),
                                                    nStartPos,
                                                    nEndPos,
                                                    cur_block->block );
            }
            else // Packed
            {
                // Create space to store unzipped data, the size is only an initial guess, will be resized in unzip() if not big enough
                uint32 lenUnzipped = (size * 2); 
                // Don't get too big
                if (lenUnzipped > (BLOCKSIZE + 300))
                    lenUnzipped = (BLOCKSIZE + 300);
                BYTE *unzipped = new BYTE[lenUnzipped];

                // Try to unzip the packet
                int result = unzip(cur_block, (BYTE *)(packet + HEADER_SIZE), (size - HEADER_SIZE), &unzipped, &lenUnzipped);
                if (result == Z_OK)
                {
                    // Maella -Defeat 0-filled Part Senders- (Idea of xrmb)
                    if(theApp.glob_prefs->GetEnableZeroFilledTest() == true){
                        if(lenUnzipped > 5*size && reqfile->IsCompressedFile() == true){
                            // Log
                            theApp.emuledlg->AddLogLine(true,  "received suspicious compressed block: file '%s', part %i, block %i, blocksize %i, comp. blocksize %i, comp. factor %0.1f)", reqfile->GetFileName(), cur_block->block->StartOffset/PARTSIZE, cur_block->block->StartOffset/BLOCKSIZE, lenUnzipped, nBlockSize, lenUnzipped/size);
                            theApp.emuledlg->AddLogLine(false, "username '%s', ip %i.%i.%i.%i:%i, hash %s", m_pszUsername, (uint8)m_dwUserIP, (uint8)(m_dwUserIP>>8), (uint8)(m_dwUserIP>>16),(uint8)(m_dwUserIP>>24), GetUserPort(), GetUserHash());

                            // Do Not Save
                            lenWritten = 0;
                            lenUnzipped = 0; // skip writting
                            reqfile->RemoveBlockFromList(cur_block->block->StartOffset, cur_block->block->EndOffset);
                        } 
                        else if(lenUnzipped > 25*size){
                            // Check if the block has been filled with zero
                            bool allDaSameShit=true;

                            for(uint32 i=0; i < lenUnzipped-1; i++) {
                                if(unzipped[i]!=unzipped[i+1]) {
                                    allDaSameShit = false;
                                    break;
                                }
                            }

                            if(allDaSameShit == true){
                                // Log
                                theApp.emuledlg->AddLogLine(true,  "received suspicious block: file '%s', part %i, block %i, blocksize %i, comp. blocksize %i)", reqfile->GetFileName(), cur_block->block->StartOffset/PARTSIZE, cur_block->block->StartOffset/BLOCKSIZE, lenUnzipped, nBlockSize);
                                theApp.emuledlg->AddLogLine(false, "username '%s', ip %i.%i.%i.%i:%i, hash %s", m_pszUsername, (uint8)m_dwUserIP, (uint8)(m_dwUserIP>>8), (uint8)(m_dwUserIP>>16),(uint8)(m_dwUserIP>>24), GetUserPort(), GetUserHash());

                                // Do Not Save
                                lenWritten = 0;
                                lenUnzipped = 0; // skip writting
                                reqfile->RemoveBlockFromList(cur_block->block->StartOffset, cur_block->block->EndOffset);
                            }
                        }
                    }
                    // Maella end       

                    // Write any unzipped data to disk
                    if (lenUnzipped > 0)
                    {
                        // Use the current start and end positions for the uncompressed data
                        nStartPos = cur_block->block->StartOffset + cur_block->totalUnzipped - lenUnzipped;
                        nEndPos = cur_block->block->StartOffset + cur_block->totalUnzipped - 1;

                        // Write uncompressed data to file
                        lenWritten = reqfile->WriteToBuffer(size - HEADER_SIZE,
                                                            unzipped,
                                                            nStartPos,
                                                            nEndPos,
                                                            cur_block->block );
                    }
                }
                else
                {
                    theApp.emuledlg->AddDebugLogLine(false, GetResString(IDS_ERR_CORRUPTCOMPRPKG), reqfile->GetFileName(), result);
                    reqfile->RemoveBlockFromList(cur_block->block->StartOffset, cur_block->block->EndOffset);
                }
                delete [] unzipped;
            }

            // These checks only need to be done if any data was written
            if (lenWritten > 0)
            {
                m_nTransferedDown += lenWritten;

                // If finished reserved block
                if (nEndPos == cur_block->block->EndOffset)
                {
                    reqfile->RemoveBlockFromList(cur_block->block->StartOffset, cur_block->block->EndOffset);
                    delete cur_block->block;
                    // Not always allocated
                    if (cur_block->zStream) delete cur_block->zStream;
                    delete cur_block;
                    m_PendingBlocks_list.RemoveAt(pos);

                    // Request next block
                    SendBlockRequests();    
                }
            }

            // Stop looping and exit method
            return;
        }
    }
  } catch (...) {}
}

int CUpDownClient::unzip(Pending_Block_Struct *block, BYTE *zipped, uint32 lenZipped, BYTE **unzipped, uint32 *lenUnzipped, bool recursive)
{
  int err = Z_DATA_ERROR;
  try
  {
    // Save some typing
    z_stream *zS = block->zStream;

    // Is this the first time this block has been unzipped
    if (zS == NULL)
    {
        // Create stream
        block->zStream = new z_stream;
        zS = block->zStream;

        // Initialise stream values
        zS->zalloc = (alloc_func)0;
        zS->zfree = (free_func)0;
        zS->opaque = (voidpf)0;

        // Set output data streams, do this here to avoid overwriting on recursive calls
        zS->next_out = (*unzipped);
        zS->avail_out = (*lenUnzipped);

        // Initialise the z_stream
        err = inflateInit(zS);
        if (err != Z_OK)
            return err;
    }

    // Use whatever input is provided
    zS->next_in  = zipped;
    zS->avail_in = lenZipped;

    // Only set the output if not being called recursively
    if (!recursive)
    {
        zS->next_out = (*unzipped);
        zS->avail_out = (*lenUnzipped);
    }

    // Try to unzip the data
    err = inflate(zS, Z_SYNC_FLUSH);

    // Is zip finished reading all currently available input and writing all generated output
    if (err == Z_STREAM_END)
    {
        // Finish up
        err = inflateEnd(zS);
        if (err != Z_OK)
            return err;

        // Got a good result, set the size to the amount unzipped in this call (including all recursive calls)
        (*lenUnzipped) = (zS->total_out - block->totalUnzipped);
        block->totalUnzipped = zS->total_out;
    }
    else if ((err == Z_OK) && (zS->avail_out == 0))
    {
        // Output array was not big enough, call recursively until there is enough space

        // What size should we try next
        uint32 newLength = (*lenUnzipped) *= 2;
        if (newLength == 0)
            newLength = lenZipped * 2;

        // Copy any data that was successfully unzipped to new array
        BYTE *temp = new BYTE[newLength];
        memcpy(temp, (*unzipped), (zS->total_out - block->totalUnzipped));
        delete [] (*unzipped);
        (*unzipped) = temp;
        (*lenUnzipped) = newLength;

        // Position stream output to correct place in new array
        zS->next_out = (*unzipped) + (zS->total_out - block->totalUnzipped);
        zS->avail_out = (*lenUnzipped) - (zS->total_out - block->totalUnzipped);

        // Try again
        err = unzip(block, zS->next_in, zS->avail_in, unzipped, lenUnzipped, true);
    }
    else if ((err == Z_OK) && (zS->avail_in == 0))
    {
        // All available input has been processed, everything ok.
        // Set the size to the amount unzipped in this call (including all recursive calls)
        (*lenUnzipped) = (zS->total_out - block->totalUnzipped);
        block->totalUnzipped = zS->total_out;
    }
    else
    {
        // Should not get here unless input data is corrupt
        theApp.emuledlg->AddDebugLogLine(false,"Unexpected zip error");
//      DebugBreak(); I removed this so that we could let the client run for more the five minutes.. Barry needs to see if there is or isn't a preoblem here..
    }

    if (err != Z_OK)
        (*lenUnzipped) = 0;
  } catch (...) {}
  return err;
}

// Maella => -Accurate measure of bandwidth: IP, TCP or UDP, eDonkey protocol, etc-
void CUpDownClient::CompDownDataRate(){
    // Remark: This method is called once every second
    TransferredData newItem = {m_nDownDatarateMeasure, ::GetTickCount()};
    m_nDownDatarateMeasure = 0; // Reset counter

    uint32 deltaTime = newItem.timeStamp - m_downHistory_list.GetHead().timeStamp; // [ms]

    m_sumDownHistory += newItem.dataLength;
    m_sumDownHistory -= m_downHistory_list.RemoveHead().dataLength; 
    m_downHistory_list.AddTail(newItem);

    m_nDownDatarate = (deltaTime > 0) ? (1000 * m_sumDownHistory / deltaTime) : 0; // [bytes/s]

    // Check and then refresh GUI
    m_displayUpDatarateCounter++;
    if(m_displayUpDatarateCounter >= DISPLAY_REFRESH && GetDownloadState() == DS_DOWNLOADING){
        m_displayUpDatarateCounter = 0;
        theApp.emuledlg->transferwnd.downloadlistctrl.UpdateItem(this);
    }
}

void CUpDownClient::CheckDownloadTimeout(){
    if ((::GetTickCount() - m_dwLastBlockReceived) > DOWNLOADTIMEOUT){
        Packet* packet = new Packet(OP_CANCELTRANSFER,0);
        socket->SendPacket(packet,true,true);
        SetDownloadState(DS_ONQUEUE);
    }
}
// Maella End

uint16 CUpDownClient::GetAvailablePartCount(){
    uint16 result = 0;
    for (int i = 0;i != m_nPartCount;i++){
        if (IsPartAvailable(i))
            result++;
    }
    return result;
}

void CUpDownClient::SetRemoteQueueRank(uint16 nr){
    m_nRemoteQueueRank = nr;
    //theApp.emuledlg->transferwnd.downloadlistctrl.UpdateItem(this);
    UpdateDisplayedInfo();
}

bool CUpDownClient::SwapToAnotherFile(bool bIgnoreNoNeeded){
    if (GetDownloadState() == DS_DOWNLOADING)
        return false;
    CPartFile* SwapTo = NULL;
    if (!m_OtherRequests_list.IsEmpty()){
        for (POSITION pos = m_OtherRequests_list.GetHeadPosition();pos != 0;m_OtherRequests_list.GetNext(pos)){
            CPartFile* cur_file = m_OtherRequests_list.GetAt(pos);
            if (cur_file != reqfile && theApp.downloadqueue->IsPartFile(cur_file) && (cur_file->GetStatus(false) == PS_READY || cur_file->GetStatus(false) == PS_EMPTY) ){
                SwapTo = cur_file;
                m_OtherRequests_list.RemoveAt(pos);
                break;
            }
        }
    }
    if (!SwapTo && bIgnoreNoNeeded){
        for (POSITION pos = m_OtherNoNeeded_list.GetHeadPosition();pos != 0;m_OtherNoNeeded_list.GetNext(pos)){
            CPartFile* cur_file = m_OtherNoNeeded_list.GetAt(pos);
            if (cur_file != reqfile && theApp.downloadqueue->IsPartFile(cur_file) && (cur_file->GetStatus(false) == PS_READY || cur_file->GetStatus(false) == PS_EMPTY) ){
                SwapTo = cur_file;
                m_OtherNoNeeded_list.RemoveAt(pos);
                break;
            }
        }
    }
    if (SwapTo){
        m_OtherNoNeeded_list.AddHead(reqfile);
        theApp.downloadqueue->RemoveSource(this);
        m_nRemoteQueueRank = 0;
        if (m_abyPartStatus){
            delete[] m_abyPartStatus;
            m_abyPartStatus = 0;
        }
        m_dwLastAskedTime = 0;
        m_iRate=0;
        m_strComment="";
        // Maella -SwapToAnotherFile-
        theApp.downloadqueue->CheckAndAddKnownSource(SwapTo,this); // It may failed...
        return (reqfile != NULL); 
        // Maella end
    }
    else
        return false;

}

void CUpDownClient::UDPReaskACK(uint16 nNewQR){
    m_bUDPPending = false;
    SetRemoteQueueRank(nNewQR);
    m_dwLastAskedTime = ::GetTickCount();
}

void CUpDownClient::UDPReaskFNF(){
    m_bUDPPending = false;
    theApp.emuledlg->AddDebugLogLine(false,CString("UDP ANSWER FNF : %s"),GetUserName());
    theApp.downloadqueue->RemoveSource(this);
    if (!socket)
        Disconnected();
}

void CUpDownClient::UDPReaskForDownload(){
    ASSERT ( reqfile );
    if(!reqfile || m_bUDPPending)
        return;

    //the line "m_bUDPPending = true;" use to be here

    if(m_nUDPPort != 0 && theApp.glob_prefs->GetUDPPort() != 0 &&
       !theApp.serverconnect->IsLowID() && !HasLowID() && !(socket && socket->IsConnected())) {

        //don't use udp to ask for sources
        if(IsSourceRequestAllowed())
            return;
        m_bUDPPending = true;
        Packet* response = new Packet(OP_REASKFILEPING,16,OP_EMULEPROT);
        memcpy(response->pBuffer,reqfile->GetFileHash(),16);
        theApp.uploadqueue->AddUpDataOverheadFileRequest(response->size);
        theApp.clientudp->SendPacket(response,GetIP(),GetUDPPort());
    }
}

// Barry - Sets string to show parts downloading, eg NNNYNNNNYYNYN
void CUpDownClient::ShowDownloadingParts(CString *partsYN)
{
    Requested_Block_Struct *cur_block;
    int x;

    // Initialise to all N's
    char *n = new char[m_nPartCount+1];
    _strnset(n, 'N', m_nPartCount);
    n[m_nPartCount] = 0;
    partsYN->SetString(n, m_nPartCount);
    delete [] n;

    for (POSITION pos = m_PendingBlocks_list.GetHeadPosition(); pos != 0; m_PendingBlocks_list.GetNext(pos))
    {
        cur_block = m_PendingBlocks_list.GetAt(pos)->block;
        x = (cur_block->StartOffset / PARTSIZE);
        partsYN->SetAt(x, 'Y');
    }
}

void CUpDownClient::UpdateDisplayedInfo(boolean force) {
    DWORD curTick = ::GetTickCount();

    if(force || curTick-m_lastRefreshedDLDisplay > MINWAIT_BEFORE_DLDISPLAY_WINDOWUPDATE+(uint32)(rand()/(RAND_MAX/1000))) {
        theApp.emuledlg->transferwnd.downloadlistctrl.UpdateItem(this);
        m_lastRefreshedDLDisplay = curTick;
    }
}