Difference Analysis Generated by HtmlDiff on 01.03.2003 16:52  

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

Modified file: vampirev1esrc\src\DownloadQueue.cpp

//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 "downloadqueue.h"
#include "updownclient.h"
#include "partfile.h"
#include "ed2klink.h"

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


CDownloadQueue::CDownloadQueue(CPreferences* in_prefs,CSharedFileList* in_sharedfilelist){
    app_prefs = in_prefs;
    sharedfilelist = in_sharedfilelist;
    filesrdy = 0;
    cur_udpserver = 0;
    lastfile = 0;
    lastudpsearchtime = 0;
    lastudpstattime = 0;
    udcounter = 0;

    // Maella => -Accurate measure of bandwidth: IP, TCP or UDP, eDonkey protocol, etc-
    m_nDownDataOverheadSourceExchange = 0;
    m_nDownDataOverheadFileRequest = 0;
    m_nDownDataOverheadOther = 0;
    m_nDownDataOverheadServer = 0;

    m_nDownDataOverheadSourceExchangePackets = 0;
    m_nDownDataOverheadFileRequestPackets = 0;
    m_nDownDataOverheadOtherPackets = 0;
    m_nDownDataOverheadServerPackets = 0;

    m_nDownDatarate = 0;
    // Maella end

    // Maella => -Overhead compensation (pseudo full download rate control)-
    m_lastProcessTime = ::GetTickCount();
    m_lastOverallReceivedBytes = theApp.stat_overallReceivedBytes;
    m_lastReceivedBytes = theApp.stat_receivedBytes;
    m_nDownloadSlopeControl = 0;
    // Maella end

    // Maella => -Code Improvement-
    m_lastRefreshedDLDisplay = 0;
    // Maella end
}

void CDownloadQueue::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;
    }
}

void CDownloadQueue::AddPartFilesToShare(){
    for (POSITION pos = filelist.GetHeadPosition();pos != 0;filelist.GetNext(pos)){
        CPartFile* cur_file = filelist.GetAt(pos);
        if (cur_file->GetStatus(true) == PS_READY)
            sharedfilelist->SafeAddKFile(cur_file,true);
    }
}

void CDownloadQueue::Init(){
    // find all part files, read & hash them if needed and store into a list
    CFileFind ff;
    int count = 0;

    //char* searchpath = new char[strlen(app_prefs->GetTempDir())+MAX_PATH];
    CString searchPath(app_prefs->GetTempDir());
    searchPath += "\\*.part.met";
    //sprintf(searchpath,"%s\\*.part.met",app_prefs->GetTempDir());
    //delete[] searchpath;

    //check all part.met files
    bool end = !ff.FindFile(searchPath, 0);
    while (!end){
        end = !ff.FindNextFile();
        if (ff.IsDirectory())
            continue;
        CPartFile* toadd = new CPartFile();
        if (toadd->LoadPartFile(app_prefs->GetTempDir(),ff.GetFileName().GetBuffer())){
            count++;
            filelist.AddTail(toadd);            // to downloadqueue
            if (toadd->GetStatus(true) == PS_READY)
                sharedfilelist->SafeAddKFile(toadd); // part files are always shared files
            theApp.emuledlg->transferwnd.downloadlistctrl.AddFile(toadd);// show in downloadwindow
        }
        else
            delete toadd;
    }
    ff.Close();

    //try recovering any part.met files
    searchPath += ".backup";
    end = !ff.FindFile(searchPath, 0);
    while (!end){
        end = !ff.FindNextFile();
        if (ff.IsDirectory())
            continue;
        CPartFile* toadd = new CPartFile();
        if (toadd->LoadPartFile(app_prefs->GetTempDir(),ff.GetFileName().GetBuffer())){
            toadd->SavePartFile(); // resave backup
            count++;
            filelist.AddTail(toadd);            // to downloadqueue
            if (toadd->GetStatus(true) == PS_READY)
                sharedfilelist->SafeAddKFile(toadd); // part files are always shared files
            theApp.emuledlg->transferwnd.downloadlistctrl.AddFile(toadd);// show in downloadwindow

            theApp.emuledlg->AddLogLine(false, GetResString(IDS_RECOVERED_PARTMET), toadd->GetFileName());
        }
        else {
            delete toadd;
        }
    }
    ff.Close();

    if(count == 0) {
        theApp.emuledlg->AddLogLine(false,GetResString(IDS_NOPARTSFOUND));
    } else {
        theApp.emuledlg->AddLogLine(false,GetResString(IDS_FOUNDPARTS),count);
        SortByPriority();
    }
}

CDownloadQueue::~CDownloadQueue(){
//  if (!theApp.emuledlg->m_app_state== APP_STATE_SHUTINGDOWN )
//      SavePartFiles(true);    // changed by InterCeptor

    for (POSITION pos =filelist.GetHeadPosition();pos != 0;filelist.GetNext(pos)){
        //filelist.GetAt(pos)->m_hpartfile.Flush() ; // Flush before close app [Tarod]
        //filelist.GetAt(pos)->SavePartFile();
        delete filelist.GetAt(pos);
    }

}

// [InterCeptor]
void CDownloadQueue::SavePartFiles(bool del /*= false*/) {
    for (POSITION pos =filelist.GetHeadPosition();pos != 0;filelist.GetNext(pos))
    {   

        filelist.GetAt(pos)->m_hpartfile.Flush();
        filelist.GetAt(pos)->SavePartFile();
        if (del)
            delete filelist.GetAt(pos);
    }
}

void CDownloadQueue::AddSearchToDownload(CSearchFile* toadd){
    if (IsFileExisting(toadd->GetFileHash()))
        return;
    CPartFile* newfile = new CPartFile(toadd);
    if (newfile->GetStatus() == PS_ERROR){
        delete newfile;
        return;
    }
    AddDownload(newfile);
}

void CDownloadQueue::AddSearchToDownload(CString link){
    CPartFile* newfile = new CPartFile(link);
    if (newfile->GetStatus() == PS_ERROR){
        delete newfile;
        return;
    }
    AddDownload(newfile);
}

void CDownloadQueue::StartNextFile(){
    if( !theApp.glob_prefs->StartNextFile() )
        return;
    CPartFile*  pfile = NULL;
    for (POSITION pos = filelist.GetHeadPosition();pos != 0;filelist.GetNext(pos)){
        CPartFile* cur_file = filelist.GetAt(pos);
        if (cur_file->GetStatus() == PS_PAUSED){
            if (!pfile){
                pfile = cur_file;
                if (pfile->GetPriority() == PR_HIGH) break;
            }
            else{
                if (cur_file->GetPriority() > pfile->GetPriority()){
                    pfile = cur_file;
                    if (pfile->GetPriority() == PR_HIGH) break;
                }
            }
        }
    }
    if (pfile) pfile->ResumeFile();
}

void CDownloadQueue::AddFileLinkToDownload(CED2KFileLink* pLink)
{
    CPartFile* newfile = new CPartFile(pLink);
    if (newfile->GetStatus() == PS_ERROR){
        delete newfile;
        newfile=NULL;
    }
    else AddDownload(newfile);
    if(pLink->HasValidSources()) {
        if (newfile) newfile->AddClientSources(pLink->SourcesList);
        else{
                CPartFile* partfile = GetFileByID((uchar*)pLink->GetHashKey());
                if (partfile) partfile->AddClientSources(pLink->SourcesList);
        }

    }
}

void CDownloadQueue::AddDownload(CPartFile* newfile) {
    // Barry - Add in paused mode if required
    if (theApp.glob_prefs->AddNewFilesPaused())
        newfile->PauseFile();

    filelist.AddTail(newfile);
    SortByPriority();
    theApp.emuledlg->transferwnd.downloadlistctrl.AddFile(newfile);
    theApp.emuledlg->AddLogLine(true,GetResString(IDS_NEWDOWNLOAD),newfile->GetFileName());
    CString msgTemp;
    msgTemp.Format(GetResString(IDS_NEWDOWNLOAD)+"\n",newfile->GetFileName());
    theApp.emuledlg->ShowNotifier(msgTemp, TBN_DLOAD);
}

bool CDownloadQueue::IsFileExisting(uchar* fileid){
    if (CKnownFile* file = sharedfilelist->GetFileByID((uchar*)fileid)){
        if (file->IsPartFile())
            theApp.emuledlg->AddLogLine(true, GetResString(IDS_ERR_ALREADY_DOWNLOADING), file->GetFileName() );
        else
            theApp.emuledlg->AddLogLine(true, GetResString(IDS_ERR_ALREADY_DOWNLOADED), file->GetFileName() );
        return true;
    }
    else if ( file = this->GetFileByID((uchar*)fileid)){
        theApp.emuledlg->AddLogLine(true, GetResString(IDS_ERR_ALREADY_DOWNLOADING), file->GetFileName() );
        return true;
    }
    return false;
}

// Maella => -New bandwidth control-
void CDownloadQueue::Process(){

    bool isLimited = (app_prefs->GetMaxDownload() < UNLIMITED);

    // Elapsed time (TIMER_PERIOD not accurate) 
    uint32 deltaTime = ::GetTickCount() - m_lastProcessTime;
    m_lastProcessTime += deltaTime;

    if(isLimited == false){
        m_nDownloadSlopeControl = 0; // No limit
    }
    else {
        // Bandwidth control
        const sint32 slopeCredit = (sint32)(app_prefs->GetMaxDownload() * 1.024f * (float)deltaTime);

        // Correct the slope with the bytes sent since to last processing           
        if(theApp.glob_prefs->GetCompensateOverhead() == true){
            
            m_nDownloadSlopeControl += slopeCredit; // [bytes/period]   
            m_nDownloadSlopeControl -= (sint32)(theApp.stat_overallReceivedBytes - m_lastOverallReceivedBytes);

            // Trunk negative value => possible when Overhead compensation activated
            if(m_nDownloadSlopeControl > slopeCredit){
                m_nDownloadSlopeControl = slopeCredit;
            }
            else if(m_nDownloadSlopeControl < -1 * MAXFRAGSIZE){
                m_nDownloadSlopeControl = -1 * MAXFRAGSIZE;
            }
        }
        else {
            // The Bandwitch control should be valid for an AVERAGE value
            m_nDownloadSlopeControl = slopeCredit; // [bytes/period]

            // Compensate the missing received bytes (e.g. control packet)
            const sint32 deltaByte = (sint32)(theApp.stat_receivedBytes - m_lastReceivedBytes);
            m_nDownloadSlopeControl += (deltaByte >= slopeCredit) ? 0 : (slopeCredit - deltaByte);

            // Allow instant pick of 20%
            const sint32 slopeCreditPick = 120 * slopeCredit / 100;
            if(m_nDownloadSlopeControl > slopeCreditPick){
                m_nDownloadSlopeControl = slopeCreditPick;
            }
        }
    }

    // Keep current value for next processing
    m_lastOverallReceivedBytes = theApp.stat_overallReceivedBytes;
    m_lastReceivedBytes = theApp.stat_receivedBytes;

    // PR_HIGH, PR_NORMAL, PR_LOW || PR_AUTO
    sint32 nDownloadSlopeControl = m_nDownloadSlopeControl;
    for(int priority = 0; priority < 3; priority++){
        // Try to receive the blocks    
        POSITION next_pos = filelist.GetHeadPosition();
        for(int i=0; i<filelist.GetCount(); i++){
            POSITION cur_pos = next_pos;
            CPartFile* cur_file = filelist.GetNext(next_pos); // Already point to the next element

            if(cur_file->GetStatus() == PS_READY || cur_file->GetStatus() == PS_EMPTY){
                if((priority == 0 && cur_file->GetPriority() == PR_HIGH) ||
                   (priority == 1 && cur_file->GetPriority() == PR_NORMAL) ||
                   (priority == 2 && (cur_file->GetPriority() == PR_LOW || cur_file->GetPriority() == PR_AUTO))){

                    // The method must be called regardless of nDownloadSlopeControl
                    // The method returns the size of the received blocks if the download rate is limited (otherwise zero)
                    uint32 maxAmmount = (nDownloadSlopeControl <= 0) ? 0 : (uint32)nDownloadSlopeControl;
                    uint32 receivedBlock = cur_file->Process(maxAmmount, isLimited);
                    if(receivedBlock > 0){
                        ASSERT(nDownloadSlopeControl >= (sint32)receivedBlock);
                        nDownloadSlopeControl -= receivedBlock;             
                    
                        // Try to 'balance' the download between sources (=> clients).
                        // Move the 'uploaded' at the end of the list.
                        filelist.AddTail(cur_file);
                        filelist.RemoveAt(cur_pos);
                    }
                }
            }
        }
    }

    // Server statistic + UDP socket
    udcounter++;
    if (udcounter == (500/TIMER_PERIOD)) { // Maella => -Small latency- every 0.5 second
        if((!lastudpstattime) || (::GetTickCount() - lastudpstattime) > UDPSERVERSTATTIME){
            lastudpstattime = ::GetTickCount();
            theApp.serverlist->ServerStats();
        }
    }
    if (udcounter >= (1000/TIMER_PERIOD)) { // Maella => -Small latency- every 1 second
        udcounter = 0;
        if ((!lastudpsearchtime) || (::GetTickCount() - lastudpsearchtime) > UDPSERVERREASKTIME)
            SendNextUDPPacket();
    }
}
// Maella end

// Maella => -Accurate measure of bandwidth: IP, TCP or UDP, eDonkey protocol, etc-
void CDownloadQueue::CompDownDataRate(){
    m_nDownDatarate = 0;
    for (POSITION pos = filelist.GetHeadPosition(); pos != 0; ){
        CPartFile* cur_file =  filelist.GetNext(pos);
        cur_file->CompDownDataRate();
        m_nDownDatarate += cur_file->GetDownDatarate();  // [bytes/s]
    }
}
// Maella end

CPartFile*  CDownloadQueue::GetFileByID(uchar* filehash){
    for (POSITION pos = filelist.GetHeadPosition();pos != 0;filelist.GetNext(pos)){
        if (!memcmp(filehash,filelist.GetAt(pos)->GetFileHash(),16))
            return filelist.GetAt(pos);
    }
    return 0;
}

bool CDownloadQueue::IsPartFile(void* totest){
    for (POSITION pos = filelist.GetHeadPosition();pos != 0;filelist.GetNext(pos))
        if (totest == filelist.GetAt(pos))
            return true;
    return false;
}

void CDownloadQueue::CheckAndAddSource(CPartFile* sender,CUpDownClient* source){
    // if we block loopbacks at this point it should prevent us from connecting to ourself
    if(!source->HasLowID() && (source->GetUserID() & 0xFF) == 0x7F) {
        delete source;
        return;
    }

    // uses this only for temp. clients
    for (POSITION pos = filelist.GetHeadPosition();pos != 0;filelist.GetNext(pos)){
        CPartFile* cur_file = filelist.GetAt(pos);
        for (int sl=0;sl<SOURCESSLOTS;sl++) if (!cur_file->srclists[sl].IsEmpty())
        for (POSITION pos2 = cur_file->srclists[sl].GetHeadPosition();pos2 != 0; cur_file->srclists[sl].GetNext(pos2)){
            if (cur_file->srclists[sl].GetAt(pos2)->Compare(source)){
                if (cur_file == sender){ // this file has already this source
                    delete source;
                    return;
                }
                // set request for this source
                if (cur_file->srclists[sl].GetAt(pos2)->AddRequestForAnotherFile(sender)){
                    // add it to uploadlistctrl
                    theApp.emuledlg->transferwnd.downloadlistctrl.AddSource(sender,cur_file->srclists[sl].GetAt(pos2),true);
                    delete source;
                    return;
                }
                else{
                    delete source;
                    return;
                }
            }
        }
    }
    //our new source is real new but maybe it is already uploading to us?
    //if yes the known client will be attached to the var "source"
    //and the old sourceclient will be deleted
    if (theApp.clientlist->AttachToAlreadyKnown(&source,0))
        source->reqfile = sender;
    else
        theApp.clientlist->AddClient(source,true);
    
    sender->srclists[source->sourcesslot].AddTail(source);
    theApp.emuledlg->transferwnd.downloadlistctrl.AddSource(sender,source,false);
    //theApp.emuledlg->transferwnd.downloadlistctrl.UpdateItem(this);
    UpdateDisplayedInfo();
}

void CDownloadQueue::CheckAndAddKnownSource(CPartFile* sender,CUpDownClient* source){
    if(!source->HasLowID() && (source->GetUserID() & 0xFF) == 0x7F)
        return;

    // use this for client which are already know (downloading for example)
    for (POSITION pos = filelist.GetHeadPosition();pos != 0;filelist.GetNext(pos)){
        CPartFile* cur_file = filelist.GetAt(pos);
        for (int sl=0;sl<SOURCESSLOTS;sl++) if (!cur_file->srclists[sl].IsEmpty())
        if (cur_file->srclists[sl].Find(source)){
            if (cur_file == sender)
                return;
            if (source->AddRequestForAnotherFile(sender))
                theApp.emuledlg->transferwnd.downloadlistctrl.AddSource(sender,source,true);
            return;
        }
    }
    source->reqfile = sender;
    sender->srclists[source->sourcesslot].AddTail(source);
    theApp.emuledlg->transferwnd.downloadlistctrl.AddSource(sender,source,false);
    UpdateDisplayedInfo();
}

bool CDownloadQueue::RemoveSource(CUpDownClient* toremove, bool updatewindow){
    bool removed = false;
    for (POSITION pos = filelist.GetHeadPosition();pos != 0;filelist.GetNext(pos)){
        CPartFile* cur_file = filelist.GetAt(pos);
        for (int sl=0;sl<SOURCESSLOTS;sl++) if (!cur_file->srclists[sl].IsEmpty())
        for (POSITION pos2 = cur_file->srclists[sl].GetHeadPosition();pos2 != 0; cur_file->srclists[sl].GetNext(pos2)){
            if (toremove == cur_file->srclists[sl].GetAt(pos2)){
                cur_file->srclists[sl].RemoveAt(pos2);
                cur_file->NewSrcPartsInfo();
                removed = true;

                // Maella -Downloading source list-
                // Remark: always try to remove to be sure
                cur_file->RemoveDownloadingSource(toremove);
                // Maella end

                break;
            }
        }
        cur_file->UpdateAvailablePartsCount();
    }
    if (updatewindow){
        toremove->SetDownloadState(DS_NONE);
        theApp.emuledlg->transferwnd.downloadlistctrl.RemoveSource(toremove,0);
    }
    toremove->reqfile = 0;
    return removed;
}

void CDownloadQueue::RemoveFile(CPartFile* toremove){
    for (POSITION pos = filelist.GetHeadPosition();pos != 0;filelist.GetNext(pos)){
        if (toremove == filelist.GetAt(pos)){
            filelist.RemoveAt(pos);
            return;
        }
    }
}

void CDownloadQueue::DeleteAll(){
    POSITION pos;
    for (pos = filelist.GetHeadPosition();pos != 0;filelist.GetNext(pos)){
        CPartFile* cur_file = filelist.GetAt(pos);
        for (int sl=0;sl<SOURCESSLOTS;sl++) if (!cur_file->srclists[sl].IsEmpty())
            cur_file->srclists[sl].RemoveAll();
        // Barry - Should also remove all requested blocks
        // Don't worry about deleting the blocks, that gets handled 
        // when CUpDownClient is deleted in CClientList::DeleteAll()
        cur_file->RemoveAllRequestedBlocks();
    }
}

bool CDownloadQueue::SendNextUDPPacket(){
    if (filelist.IsEmpty() || !theApp.serverconnect->IsConnected())
        return false;
    if (!cur_udpserver)
        if (!(cur_udpserver = theApp.serverlist->GetNextServer(cur_udpserver))){
            TRACE("ERROR:SendNextUDPPacket() no server found");
            StopUDPRequests();
        };

    // get nextfile
    CPartFile* nextfile = 0;
    while (!(nextfile && (nextfile->GetStatus() == PS_READY ||nextfile->GetStatus() == PS_EMPTY))){
        if (lastfile == 0){
            nextfile = filelist.GetHead();
            lastfile = nextfile;
        }
        else{
            POSITION pos = filelist.Find(lastfile);
            if (!pos){
                TRACE("Error: CDownloadQueue::SendNextUDPPacket()");
                nextfile = filelist.GetHead();
                lastfile = nextfile;
            }
            else{
                filelist.GetNext(pos);
                if (pos == 0){
                    cur_udpserver = theApp.serverlist->GetNextServer(cur_udpserver);
                    if (cur_udpserver == 0){
                        //TRACE("finished");
                        lastudpsearchtime = ::GetTickCount();
                        lastfile = 0;
                        return false; // finished (processed all file & all servers)
                    }
                    nextfile = filelist.GetHead();
                    lastfile = nextfile;
                }
                else{
                    nextfile = filelist.GetAt(pos);
                    lastfile = nextfile;
                }
            }
        }
    }
    if( (theApp.glob_prefs->GetMaxSourcePerFileUDP()) < nextfile->GetSourceCount()) //<<--
        return true; 
    Packet packet(OP_GLOBGETSOURCES,16);
    memcpy(packet.pBuffer,nextfile->GetFileHash(),16);
    if (cur_udpserver != theApp.serverlist->GetServerByAddress(theApp.serverconnect->GetCurrentServer()->GetAddress(),theApp.serverconnect->GetCurrentServer()->GetPort()))
        theApp.serverconnect->SendUDPPacket(&packet,cur_udpserver,false);
    return true;
}

void CDownloadQueue::StopUDPRequests(){
    cur_udpserver = 0;
    lastudpsearchtime = ::GetTickCount();
    lastfile = 0;
}

void CDownloadQueue::SortByPriority(){
   POSITION pos1, pos2;
   uint16 i = 0;
   for( pos1 = filelist.GetHeadPosition(); ( pos2 = pos1 ) != NULL; ){
       filelist.GetNext(pos1);
       CPartFile* cur_file = filelist.GetAt(pos2);
       if (cur_file->GetPriority() == PR_HIGH){
           filelist.AddHead(cur_file);
           filelist.RemoveAt(pos2);
       }
       else if (cur_file->GetPriority() == PR_LOW){
           filelist.AddTail(cur_file);
           filelist.RemoveAt(pos2);
       }
       i++;
       if (i == filelist.GetCount())
           break;
   }
}

void CDownloadQueue::GetDownloadStats(int results[]) {
    
    results[0]=0;
    results[1]=0;

    for (POSITION pos =theApp.downloadqueue->filelist.GetHeadPosition();pos != 0;theApp.downloadqueue->filelist.GetNext(pos)){
        CPartFile* cur_file =  filelist.GetAt(pos);
        results[0]+=cur_file->GetSourceCount();
        results[1]+=cur_file->GetTransferingSrcCount();
    }
}

CUpDownClient* CDownloadQueue::GetDownloadClientByIP(uint32 dwIP){
    for (POSITION pos = filelist.GetHeadPosition();pos != 0;filelist.GetNext(pos)){
        CPartFile* cur_file = filelist.GetAt(pos);
        for (int sl=0;sl<SOURCESSLOTS;sl++) if (!cur_file->srclists[sl].IsEmpty())
        for (POSITION pos2 = cur_file->srclists[sl].GetHeadPosition();pos2 != 0; cur_file->srclists[sl].GetNext(pos2)){
            if (dwIP == cur_file->srclists[sl].GetAt(pos2)->GetIP()){
                return cur_file->srclists[sl].GetAt(pos2);
            }
        }
    }
    return NULL;
}