//this file is part of eMule
//Copyright (C)2002 Merkur ( devs@emule-project.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 <io.h>
#include "emule.h"
#include "DownloadQueue.h"
#include "UpDownClient.h"
#include "PartFile.h"
#include "ed2kLink.h"
#include "SearchFile.h"
#include "ClientList.h"
#include "Statistics.h"
#include "SharedFileList.h"
#include "OtherFunctions.h"
#include "SafeFile.h"
#include "ServerConnect.h"
#include "ServerList.h"
#include "Server.h"
#include "Packets.h"
#include "Kademlia/Kademlia/Kademlia.h"
#include "kademlia/utils/uint128.h"
#include "ipfilter.h"
#include "emuledlg.h"
#include "TransferWnd.h"
#include "TaskbarNotifier.h"
#include "MenuCmds.h"
#include "Log.h"
#include "Neo/EMBackup.h" // NEO: NB - [NeoBackup] <-- Xanatos --
#ifdef NEO_BC // NEO: NBC - [NeoBandwidthControl] -- Xanatos -->
#include "Neo/BandwidthControl/BandwidthControl.h"
#endif // NEO_BC // NEO: NBC END <-- Xanatos --
#ifdef NEO_DBT // NEO: NDBT - [NeoDownloadBandwidthThrottler] -- Xanatos -->
#include "Neo/BandwidthControl/DownloadBandwidthThrottler.h"
#endif // NEO_DBT // NEO: NDBT END <-- Xanatos --
#ifdef VOODOO // NEO: VOODOO - [UniversalPartfileInterface] -- Xanatos -->
#include "Neo/voodoo.h"
#include "Neo/GUI/VoodooListDlg.h"
#endif // VOODOO // NEO: VOODOO END <-- Xanatos --
#ifdef A4AF_CATS // NEO: MAC - [MorphA4AFCategories] -- Xanatos -->
#include "Neo/GUI/SelCategoryDlg.h"
#include "Neo/Functions.h"
#endif // A4AF_CATS // NEO: MAC END <-- Xanatos --
#include "sharedfileswnd.h"// NEO: NSC - [NeoSharedCategories] <-- Xanatos --
#include "Neo/ClientFileStatus.h"// NEO: SCFS - [SmartClientFileStatus] <-- Xanatos --
#ifdef NEO_CD // NEO: NCD - [NeoClientDatabase] -- Xanatos -->
#include "Neo/SourceList.h"
#endif // NEO_CD // NEO: NCD END <-- Xanatos --
// NEO: PP - [PasswordProtection] -- Xanatos -->
#include "InputBox.h"
#include "MD5Sum.h" 
// NEO: PP END <-- Xanatos --
#include "Neo/GUI/FilePreferencesDialog.h" // NEO: FCFG - [FileConfiguration] <-- Xanatos --
#include "FileDetailDialog.h"
#ifdef A4AF_CATS // NEO: MAC - [MorphA4AFCategories] -- Xanatos -->
#include "KnownFileList.h"
#endif // A4AF_CATS // NEO: MAC END <-- Xanatos --

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


CDownloadQueue::CDownloadQueue()
{
	//filesrdy = 0;
	datarate = 0;
	cur_udpserver = 0;
	lastfile = 0;
	lastcheckdiskspacetime = 0;
	lastudpsearchtime = 0;
	lastudpstattime = 0;
	SetLastKademliaFileRequest();
	udcounter = 0;
	m_iSearchedServers = 0;
	m_datarateMS=0;
	m_nUDPFileReasks = 0;
	m_nFailedUDPFileReasks = 0;
	m_dwNextTCPSrcReq = 0;
	m_cRequestsSentToServer = 0;
    m_dwLastA4AFtime = 0; // ZZ:DownloadManager
#ifdef NEO_DBT // NEO: NDBT - [NeoDownloadBandwidthThrottler] -- Xanatos -->
	numberOfDownloading = 0;
#endif // NEO_DBT // NEO: NDBT END <-- Xanatos --
	// NEO: QS - [QuickStart] -- Xanatos -->
	m_bQuickStartDone = false; 
	m_dwQuickStartEndTime = 0;
	// NEO: QS END <-- Xanatos --

	// NEO: AHL - [AutoHardLimit] -- Xanatos -->
	m_uActiveFileCount = 0;
	m_uTotalSourceCount = 0;
	// NEO: AHL END <-- Xanatos --
	// NEO: NCAP - [NeoCustomAutoPriority] -- Xanatos -->
	m_uGlobalSourceCount = 0;
	m_uTopSourceCount = 0;
	m_uBotomSourceCount = 0;
	// NEO: NCAP END <-- Xanatos --

#ifdef A4AF_CATS // NEO: MAC - [MorphA4AFCategories] -- Xanatos -->
	m_iLastLinkQueuedTick = 0;
	m_bBusyPurgingLinks = false;
	m_ED2KLinkQueue.RemoveAll();
	
	forcea4af_file = NULL;
#endif // A4AF_CATS // NEO: MAC END <-- Xanatos --
}

void CDownloadQueue::AddPartFilesToShare()
{
	for (POSITION pos = filelist.GetHeadPosition(); pos != 0; )
	{
		CPartFile* cur_file = filelist.GetNext(pos);
		//if (cur_file->GetStatus(true) == PS_READY) // NEO: SEF - [ShareAlsoEmptyFiles] <-- Xanatos --	 || (thePrefs.IsShareAlsoEmptyFiles() && cur_file->GetStatus(true) == PS_EMPTY)) 
		theApp.sharedfiles->SafeAddKFile(cur_file, true);
	}
}

// NEO: MTD - [MultiTempDirectories] -- Xanatos -->
void CDownloadQueue::Init()
{
	theApp.UpdateSplash(GetResString(IDS_X_SS_PREP_DQ)); // NEO: SS - [SplashScreen]

	theApp.BackupEngine->RestoreTemp(); // NEO: NB - [NeoBackup]

	Init(thePrefs.GetTempDir());

	POSITION pos;
	for (pos = thePrefs.tempdir_list.GetHeadPosition();pos != 0;thePrefs.tempdir_list.GetNext(pos))
		Init(thePrefs.tempdir_list.GetAt(pos));

#ifdef NEO_SS // NEO: NSS - [NeoSourceStorage]
	// Load sources
	theApp.UpdateSplash(GetResString(IDS_X_SS_LOAD_SRC)); // NEO: SS - [SplashScreen]
	for (pos = filelist.GetHeadPosition();pos != 0;){
		CPartFile* cur_file = filelist.GetNext(pos);
		if(cur_file->PartPrefs.AutoLoadStoredSources() && !cur_file->IsStopped()){
retry: // NEO: NB - [NeoBackup]
			if(!cur_file->LoadSources()){
				// NEO: NB - [NeoBackup]
				if (theApp.BackupEngine->RestorePartMet(cur_file->GetTempPath(),cur_file->GetPartMetFileName(),PartMetSrc)) // NEO: MTD - [MultiTempDirectories]
					goto retry; // Not very nice but it works
				// NEO: NB END
			}
		}
	}
	theApp.UpdateSplash(GetResString(IDS_X_SS_PREP_DQ)); // NEO: SS - [SplashScreen]
#endif // NEO_SS // NEO: NSS END

	ResetLocalServerRequests(); // NEO: FIX <-- Xanatos -- // need for some storage related features (SetActive())

	VERIFY( m_srcwnd.CreateEx(0, AfxRegisterWndClass(0),_T("Hostname Resolve Wnd"),WS_OVERLAPPED, 0, 0, 0, 0, NULL, NULL));
}

void CDownloadQueue::Init(CString useTempDir) // NEO: MTD - [MultiTempDirectories]
{
	//CStringList metsfound;	// SLUGFILLER: SafeHash - ensure each met is loaded once per tempdir // NEO: SSH - [SlugFillerSafeHash] <-- Xanatos --

	// find all part files, read & hash them if needed and store into a list
	CFileFind ff;
	int count = 0;

	CString searchPath(useTempDir); // NEO: MTD - [MultiTempDirectories]
	searchPath += _T("\\*.part.met");

	//check all part.met files
	bool end = !ff.FindFile(searchPath, 0);
	while (!end){
		end = !ff.FindNextFile();
		if (ff.IsDirectory())
			continue;
		// NEO: SSH - [SlugFillerSafeHash]
		// BEGIN SLUGFILLER: SafeHash - one is enough
		//if (metsfound.Find(CString(ff.GetFileName()).MakeLower()))
		//	continue;
		//metsfound.AddTail(CString(ff.GetFileName()).MakeLower());
		// END SLUGFILLER: SafeHash
		// NEO: SSH END
		CPartFile* toadd = new CPartFile();
retry: // NEO: NB - [NeoBackup]
		if (toadd->LoadPartFile(useTempDir,ff.GetFileName())){ // NEO: MTD - [MultiTempDirectories]
			count++;
			filelist.AddTail(toadd);			// to downloadqueue
			// NEO: SSH - [SlugFillerSafeHash]
			// SLUGFILLER: SafeHash remove - part files are shared later
			/* if (toadd->GetStatus(true) == PS_READY
			 || (thePrefs.IsShareAlsoEmptyFiles() && toadd->GetStatus(true) == PS_EMPTY)) // NEO: SEF - [ShareAlsoEmptyFiles]
				theApp.sharedfiles->SafeAddKFile(toadd); // part files are always shared files */
			// NEO: SSH END
			theApp.emuledlg->transferwnd->downloadlistctrl.AddFile(toadd);// show in downloadwindow
			// NEO: PP - [PasswordProtection]
			if (toadd->IsPWProtHidden()) 
				theApp.emuledlg->transferwnd->downloadlistctrl.HideFile(toadd);
			// NEO: PP END
		}
		else{ 
			// NEO: NB - [NeoBackup]
			if (theApp.BackupEngine->RestorePartMet(useTempDir,ff.GetFileName().GetBuffer(),PartMet)) // NEO: MTD - [MultiTempDirectories]
				goto retry; // Not very nice but it works
			delete toadd;
			// NEO: NB END
		}
	}
	ff.Close();

	//try recovering any part.met files
	searchPath += _T(".backup");
	end = !ff.FindFile(searchPath, 0);
	while (!end){
		end = !ff.FindNextFile();
		if (ff.IsDirectory())
			continue;
		// NEO: SSH - [SlugFillerSafeHash]
		// BEGIN SLUGFILLER: SafeHash - one is enough
		//if (metsfound.Find(RemoveFileExtension(CString(ff.GetFileName()).MakeLower())))
		//	continue;
		//metsfound.AddTail(RemoveFileExtension(CString(ff.GetFileName()).MakeLower()));
		// END SLUGFILLER: SafeHash
		// NEO: SSH END
		CPartFile* toadd = new CPartFile();
		if (toadd->LoadPartFile(useTempDir,ff.GetFileName())){ // NEO: MTD - [MultiTempDirectories]
			toadd->SavePartFile(); // resave backup
			count++;
			filelist.AddTail(toadd);			// to downloadqueue
			// NEO: SSH - [SlugFillerSafeHash]
			// SLUGFILLER: SafeHash remove - part files are shared later
			/*if (toadd->GetStatus(true) == PS_READY
			 || (thePrefs.IsShareAlsoEmptyFiles() && toadd->GetStatus(true) == PS_EMPTY)) // NEO: SEF - [ShareAlsoEmptyFiles]
				theApp.sharedfiles->SafeAddKFile(toadd); // part files are always shared files*/
			// NEO: SSH END
			theApp.emuledlg->transferwnd->downloadlistctrl.AddFile(toadd);// show in downloadwindow
			// NEO: PP - [PasswordProtection]
			if (toadd->IsPWProtHidden()) 
				theApp.emuledlg->transferwnd->downloadlistctrl.HideFile(toadd);
			// NEO: PP END

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

	if(count == 0) {
		AddLogLine(false,GetResString(IDS_NOPARTSFOUND) + _T(" (%s)"),useTempDir); // NEO: MTD - [MultiTempDirectories]
	} else {
		AddLogLine(false,GetResString(IDS_FOUNDPARTS) + _T(" (%s)"),count,useTempDir); // NEO: MTD - [MultiTempDirectories]
		SortByPriority();
		CheckDiskspace();
	}
	//VERIFY( m_srcwnd.CreateEx(0, AfxRegisterWndClass(0), _T("eMule Async DNS Resolve Socket Wnd #2"), WS_OVERLAPPED, 0, 0, 0, 0, NULL, NULL)); // NEO: MTD - [MultiTempDirectories]

	ExportPartMetFilesOverview();
}
// NEO: MTD END <-- Xanatos --

CDownloadQueue::~CDownloadQueue(){
	for (POSITION pos = filelist.GetHeadPosition();pos != 0;)
		delete filelist.GetNext(pos);
	m_srcwnd.DestroyWindow(); // just to avoid a MFC warning
}

#ifdef A4AF_CATS // NEO: MAC - [MorphA4AFCategories] -- Xanatos -->
void CDownloadQueue::AddSearchToDownload(CSearchFile* toadd,uint8 paused,int cat, int useOrder)
#else
void CDownloadQueue::AddSearchToDownload(CSearchFile* toadd, uint8 paused, int cat)
#endif // A4AF_CATS // NEO: MAC END <-- Xanatos --
{
	// NEO: SSC - [SearchCatch] -- Xanatos --
	//if (toadd->GetFileSize()== (uint64)0 || IsFileExisting(toadd->GetFileHash()))
	//	return;

	if (toadd->GetFileSize() > OLD_MAX_EMULE_FILE_SIZE && !thePrefs.CanFSHandleLargeFiles()){
		LogError(LOG_STATUSBAR, GetResString(IDS_ERR_FSCANTHANDLEFILE));
		return;
	}

	// NEO: SSC - [SearchCatch] -- Xanatos -->
	CPartFile* newfile;
	if (!IsFileExisting(toadd->GetFileHash(),toadd->GetFileName())){ // NEO: MAC - [MorphA4AFCategories]
		newfile = new CPartFile(toadd, cat);

		if (newfile->GetStatus() == PS_ERROR){
			delete newfile;
			return;
		}
#ifdef A4AF_CATS // NEO: MAC - [MorphA4AFCategories]
		newfile->SetCatResumeOrder(useOrder);
#endif // A4AF_CATS // NEO: MAC END
		if (paused == 2)
			paused = (uint8)thePrefs.AddNewFilesPaused();
		AddDownload(newfile, (paused==1));
	}
	else if ((newfile = GetFileByID(toadd->GetFileHash())) == NULL)
		return;
	// NEO: SSC END <-- Xanatos --

	// If the search result is from OP_GLOBSEARCHRES there may also be a source
	if (toadd->GetClientID() && toadd->GetClientPort()){
		CSafeMemFile sources(1+4+2);
		try{
			sources.WriteUInt8(1);
			sources.WriteUInt32(toadd->GetClientID());
			sources.WriteUInt16(toadd->GetClientPort());
		    sources.SeekToBegin();
		    newfile->AddSources(&sources, toadd->GetClientServerIP(), toadd->GetClientServerPort(), false);
		}
		catch(CFileException* error){
			ASSERT(0);
			error->Delete();
		}
	}

	// Add more sources which were found via global UDP search
	const CSimpleArray<CSearchFile::SClient>& aClients = toadd->GetClients();
	for (int i = 0; i < aClients.GetSize(); i++){
		CSafeMemFile sources(1+4+2);
		try{
			sources.WriteUInt8(1);
			sources.WriteUInt32(aClients[i].m_nIP);
			sources.WriteUInt16(aClients[i].m_nPort);
		    sources.SeekToBegin();
			newfile->AddSources(&sources,aClients[i].m_nServerIP, aClients[i].m_nServerPort, false);
	    }
		catch(CFileException* error){
			ASSERT(0);
			error->Delete();
			break;
		}
	}

	// NEO: XUC - [ExtendedUdpCache] -- Xanatos -->
	if(thePrefs.UseUDPCache()){
		const CSimpleArray<CSearchFile::SServer>& aServers = toadd->GetServers();
		for (int i = 0; i < aServers.GetSize(); i++){
			CPartFile::SServer tmpServer(aServers[i].m_nIP, aServers[i].m_nPort);
			tmpServer.m_uAvail = aServers[i].m_uAvail;
			newfile->AddAvailServer(tmpServer);
			Debug(_T("Caching server with %i sources for %s"), aServers[i].m_uAvail, newfile->GetFileName());
		}
	}
	// NEO: XUC END <-- Xanatos --
}

#ifdef A4AF_CATS // NEO: MAC - [MorphA4AFCategories] -- Xanatos -->
void CDownloadQueue::AddSearchToDownload(CString link,uint8 paused, int cat, int useOrder)
#else
void CDownloadQueue::AddSearchToDownload(CString link, uint8 paused, int cat)
#endif // A4AF_CATS // NEO: MAC END <-- Xanatos --
{
	CPartFile* newfile = new CPartFile(link, cat);
	if (newfile->GetStatus() == PS_ERROR){
		delete newfile;
		return;
	}
#ifdef A4AF_CATS // NEO: MAC - [MorphA4AFCategories] -- Xanatos -->
	newfile->SetCatResumeOrder(useOrder);
#endif // A4AF_CATS // NEO: MAC END <-- Xanatos --
	if (paused == 2)
		paused = (uint8)thePrefs.AddNewFilesPaused();
	AddDownload(newfile, (paused==1));
}

void CDownloadQueue::StartNextFileIfPrefs(int cat) {
	if (thePrefs.StartNextFile()){
#ifdef A4AF_CATS // NEO: MAC - [MorphA4AFCategories] -- Xanatos -->
		int catTemp = thePrefs.StartNextFile() > 1 ? cat: -1;
		Category_Struct* cur_cat = thePrefs.GetCategory(cat);
		if (cur_cat && cur_cat->bResumeFileOnlyInSameCat)
			catTemp = cat;
		StartNextFile(catTemp, (thePrefs.StartNextFile()!=3));
#else
		StartNextFile((thePrefs.StartNextFile() > 1?cat:-1), (thePrefs.StartNextFile()!=3));
#endif // A4AF_CATS // NEO: MAC END <-- Xanatos --
	}
}

#ifdef A4AF_CATS // NEO: MAC - [MorphA4AFCategories] -- Xanatos -->
bool CDownloadQueue::StartNextFile(int cat, bool force){
#else
void CDownloadQueue::StartNextFile(int cat, bool force){
#endif // A4AF_CATS // NEO: MAC END <-- Xanatos --

	CPartFile*  pfile = NULL;
	CPartFile* cur_file ;
	POSITION pos;
	
	if (cat != -1) {
#ifdef A4AF_CATS // NEO: MAC - [MorphA4AFCategories] -- Xanatos -->
		Category_Struct* cur_cat = thePrefs.GetCategory(cat);
		force &= !(cur_cat && cur_cat->bResumeFileOnlyInSameCat);
#endif // A4AF_CATS // NEO: MAC END <-- Xanatos --
        // try to find in specified category
		for (pos = filelist.GetHeadPosition();pos != 0;){
			cur_file = filelist.GetNext(pos);
			if (cur_file->GetStatus()==PS_PAUSED &&
                ( cur_file->GetCategory()==cat
#ifndef A4AF_CATS // NEO: MAC - [MorphA4AFCategories] -- Xanatos -->
				 || cat==0 && thePrefs.GetCategory(0)->filter==0 && cur_file->GetCategory() > 0
#endif // A4AF_CATS // NEO: MAC END <-- Xanatos --
                //) && CPartFile::RightFileHasHigherPrio(pfile, cur_file)
				) && CPartFile::NextRightFileHasHigherPrio(pfile, cur_file)
			   ) {
    			pfile = cur_file;
			}
		}
		if (pfile == NULL && !force)
#ifdef A4AF_CATS // NEO: MAC - [MorphA4AFCategories] -- Xanatos -->
			return false;
#else
			return;
#endif // A4AF_CATS // NEO: MAC END <-- Xanatos --
	}

    if(cat == -1 || pfile == NULL && force) {
	    for (pos = filelist.GetHeadPosition();pos != 0;){
		    cur_file = filelist.GetNext(pos);
#ifdef A4AF_CATS // NEO: MAC - [MorphA4AFCategories] -- Xanatos -->
		    Category_Struct* cur_cat = thePrefs.GetCategory(cur_file->GetCategory());  //MORPH - Added by SiRoB, Resume file only in the same category
#endif // A4AF_CATS // NEO: MAC END <-- Xanatos --
		    if (cur_file->GetStatus() == PS_PAUSED &&
#ifdef A4AF_CATS // NEO: MAC - [MorphA4AFCategories] -- Xanatos -->
				//CPartFile::RightFileHasHigherPrio(pfile, cur_file) && !(cur_cat && cur_cat->bResumeFileOnlyInSameCat)  //MORPH - Added by SiRoB, Resume file only in the same category
				CPartFile::NextRightFileHasHigherPrio(pfile, cur_file) && !(cur_cat && cur_cat->bResumeFileOnlyInSameCat)
#else
                CPartFile::RightFileHasHigherPrio(pfile, cur_file)
#endif // A4AF_CATS // NEO: MAC END <-- Xanatos --
		    ){
                // pick first found matching file, since they are sorted in prio order with most important file first.
			    pfile = cur_file;
		    }
	    }
    }
	if (pfile)
		pfile->ResumeFile();

#ifdef A4AF_CATS // NEO: MAC - [MorphA4AFCategories] -- Xanatos -->
	return (pfile != NULL); //MORPH - Added by SiRoB, Khaos Category
#endif // A4AF_CATS // NEO: MAC END <-- Xanatos --
}

#ifdef A4AF_CATS // NEO: MAC - [MorphA4AFCategories] -- Xanatos -->
// This function is used for the category commands Stop Last and Pause Last.
// This is a new function.
void CDownloadQueue::StopPauseLastFile(int Mode, int Category) {
	CPartFile*  pfile = NULL;
	for (POSITION pos = filelist.GetHeadPosition();pos != 0;filelist.GetNext(pos)) {
		CPartFile* cur_file = filelist.GetAt(pos);
		if ((cur_file->GetStatus() < 4) && (cur_file->GetCategory() == Category || Category == -1)) {
			if (!pfile)
				pfile = cur_file;
			else {
				if (cur_file->GetCatResumeOrder() > pfile->GetCatResumeOrder())
					pfile = cur_file;
				else if (cur_file->GetCatResumeOrder() == pfile->GetCatResumeOrder() && (cur_file->GetDownPriority() < pfile->GetDownPriority()))
					pfile = cur_file;
			}
		}
	}
	if (pfile) {
		Mode == MP_STOP ? pfile->StopFile() : pfile->PauseFile();
	}
}

// This function returns the highest resume order in a given category.
// It can be used to automatically assign a resume order to a partfile
// when it is added...  Or maybe it will have other uses in the future.
int CDownloadQueue::GetMaxCatResumeOrder(int iCategory /* = 0*/)
{
	int	max = 0;
	
	for (POSITION pos = filelist.GetHeadPosition();pos != 0;filelist.GetNext(pos))
	{
		CPartFile* cur_file = filelist.GetAt(pos);
		if (cur_file->GetCategory() == iCategory && cur_file->GetCatResumeOrder() > max)
			max = cur_file->GetCatResumeOrder();
	}

	return max;
}

// khaos::categorymod+ New function, used to add all of the
// ED2K links on the link queue to the downloads.  This is
// called when no new links have been added to the download
// list for half a second, or when there are queued links
// and the user has disabled the SelectCatForLinks feature.
bool CDownloadQueue::PurgeED2KLinkQueue()
{
	if (m_ED2KLinkQueue.IsEmpty()) return false;
	
	m_bBusyPurgingLinks = true;

	int		useCat;
	int		addedFiles = 0;
	bool	bCreatedNewCat = false;
	bool	bCanceled = false; //MORPH - Added by SiRoB, WasCanceled
	if (thePrefs.SelectCatForNewDL())
	{
		CSelCategoryDlg* getCatDlg = new CSelCategoryDlg((CWnd*)theApp.emuledlg);
		getCatDlg->DoModal();
		
		// Returns 0 on 'Cancel', otherwise it returns the selected category
		// or the index of a newly created category.  Users can opt to add the
		// links into a new category.
		useCat = getCatDlg->GetInput();
		bCreatedNewCat = getCatDlg->CreatedNewCat();
		bCanceled = getCatDlg->WasCancelled(); //MORPH - Added by SiRoB, WasCanceled
		delete getCatDlg;
	}
	else if (thePrefs.UseActiveCatForLinks())
		useCat = theApp.emuledlg->transferwnd->GetActiveCategory();
	else
		useCat = 0;

	for (POSITION pos = m_ED2KLinkQueue.GetHeadPosition(); pos != 0; m_ED2KLinkQueue.GetNext(pos))
	{
		CED2KFileLink*	pLink = m_ED2KLinkQueue.GetAt(pos);
		//MORPH START - Added by SiRoB, WasCanceled
		if (bCanceled) {
			delete pLink;
			pLink = NULL;
			continue;
		}
		//MORPH END   - Added by SiRoB, WasCanceled
		if (!thePrefs.SelectCatForNewDL() && thePrefs.UseAutoCat())
		{
			useCat = GetAutoCat(CString(pLink->GetName()), pLink->GetSize());
			if (!useCat && thePrefs.UseActiveCatForLinks())
				useCat = theApp.emuledlg->transferwnd->GetActiveCategory();
		}
		CPartFile*		newfile =	new CPartFile(pLink, useCat);
		
		if (newfile->GetStatus() == PS_ERROR) 
		{
			delete newfile;
			newfile = NULL;
		}
		else
		{
			if (thePrefs.SmallFileDLPush() && newfile->GetFileSize() < thePrefs.SmallFileDLPushSizeB())
				newfile->SetCatResumeOrder(0);
			else if (thePrefs.AutoSetResumeOrder()) {
				newfile->SetCatResumeOrder(GetMaxCatResumeOrder(useCat)+1);
			}
			AddDownload(newfile,thePrefs.AddNewFilesPaused());
			addedFiles++;
		}

		CPartFile* partfile = newfile;
		if (partfile == NULL)
			partfile = GetFileByID(pLink->GetHashKey());
		if (partfile)
		{
			if (pLink->HasValidSources())
				partfile->AddClientSources(pLink->SourcesList,1);
			if (pLink->HasValidAICHHash() ){
				if ( !(partfile->GetAICHHashset()->HasValidMasterHash() && partfile->GetAICHHashset()->GetMasterHash() == pLink->GetAICHHash())){
					partfile->GetAICHHashset()->SetMasterHash(pLink->GetAICHHash(), AICH_VERIFIED);
					partfile->GetAICHHashset()->FreeHashSet();
				}
			}
		}

		if(pLink->HasHostnameSources())
		{
			POSITION pos = pLink->m_HostnameSourcesList.GetHeadPosition();
			while (pos != NULL)
			{
				const SUnresolvedHostname* pUnresHost = pLink->m_HostnameSourcesList.GetNext(pos);
				m_srcwnd.AddToResolve(pLink->GetHashKey(), pUnresHost->strHostname, pUnresHost->nPort, pUnresHost->strURL);
			}
		}
		// We're done with this link.
		delete pLink;
		pLink = NULL;
	}
	
	m_ED2KLinkQueue.RemoveAll();

	// This bit of code will resume the number of files that the user specifies in preferences (Off by default)
	if (thePrefs.StartDLInEmptyCats() && bCreatedNewCat && thePrefs.AddNewFilesPaused())
		for (int i = 0; i < thePrefs.StartDLInEmptyCatsAmount(); i++)
			if (!StartNextFile(useCat)) break;

	m_bBusyPurgingLinks = false;
	m_iLastLinkQueuedTick = 0;
	return true;
}

// Returns statistics about a category's files' states.
void CDownloadQueue::GetCategoryFileCounts(int iCategory, int cntFiles[])
{
	// cntFiles Array Indices:
	// 0 = Total
	// 1 = Transferring (Transferring Sources > 0)
	// 2 = Active (READY and EMPTY)
	// 3 = Unactive (PAUSED)
	// 4 = Complete (COMPLETE and COMPLETING)
	// 5 = Error (ERROR)
	// 6 = Other (Everything else...)
	for (int i = 0; i < 7; i++) cntFiles[i] = 0;

	for (POSITION pos = filelist.GetHeadPosition(); pos != NULL; filelist.GetNext(pos))
	{
		CPartFile* cur_file = filelist.GetAt(pos);
		if (cur_file->GetCategory() != iCategory) continue;

		cntFiles[0]++;
		if (cur_file->GetTransferringSrcCount() > 0) cntFiles[1]++;

		switch (cur_file->GetStatus(false))
		{
			case	PS_READY:
			case	PS_EMPTY:
				cntFiles[2]++;
				break;

			case	PS_PAUSED:
			//case	PS_INSUFFICIENT:
				cntFiles[3]++;
				break;

			case	PS_COMPLETING:
			case	PS_COMPLETE:
				cntFiles[4]++;
				break;

			case	PS_ERROR:
				cntFiles[5]++;
				break;
			
			// NEO: SSH - [SlugFillerSafeHash] -- Xanatos --
			//case	PS_WAITINGFORHASH:
			//case	PS_HASHING:
			case	PS_UNKNOWN:
				cntFiles[6]++;
				break;
		}
	}
}

// Returns the number of active files in a category.
UINT CDownloadQueue::GetCatActiveFileCount(int iCategory)
{
	UINT uCount = 0;

	for (POSITION pos = filelist.GetHeadPosition(); pos != NULL; filelist.GetNext(pos))
	{
		CPartFile* cur_file = filelist.GetAt(pos);
		if (cur_file->GetCategory() != iCategory) continue;

		switch (cur_file->GetStatus(false))
		{
			case	PS_READY:
			case	PS_EMPTY:
			case	PS_COMPLETING:
				uCount++;
				break;
			default:
				break;
		}
	}

	return uCount;
}

// Returns the number of files in a category.
UINT CDownloadQueue::GetCategoryFileCount(int iCategory)
{
	UINT uCount = 0;

	for (POSITION pos = filelist.GetHeadPosition(); pos != NULL; filelist.GetNext(pos))
	{
		CPartFile* cur_file = filelist.GetAt(pos);
		if (cur_file->GetCategory() == iCategory) uCount++;
	}

	return uCount;
}

// Returns the source count of the file with the highest available source count.
// nCat is optional and allows you to specify a certain category.
UINT CDownloadQueue::GetHighestAvailableSourceCount(int nCat)
{
	UINT nCount = 0;

	for (POSITION pos = filelist.GetHeadPosition(); pos != NULL; filelist.GetNext(pos))
	{
		CPartFile* curFile = filelist.GetAt(pos);
		if (nCount < curFile->GetAvailableSrcCount() && (nCat == -1 || curFile->GetCategory() == nCat))
			nCount = curFile->GetAvailableSrcCount();
	}

	return nCount;
}

// khaos::categorymod+ GetAutoCat returns a category index of a category
// that passes the filters.
// Idea by HoaX_69.
int CDownloadQueue::GetAutoCat(CString sFullName, EMFileSize nFileSize)
{
	if (sFullName.IsEmpty())
		return 0;

	if (thePrefs.GetCatCount() <= 1)
		return 0;

	for (int ix = 1; ix < thePrefs.GetCatCount(); ix++)
	{ 
		Category_Struct* curCat = thePrefs.GetCategory(ix);
		if (!curCat->selectioncriteria.bAdvancedFilterMask && !curCat->selectioncriteria.bFileSize)
			continue;
		if (curCat->selectioncriteria.bAdvancedFilterMask && !ApplyFilterMask(sFullName, ix))
			continue;
		if (curCat->selectioncriteria.bFileSize && (nFileSize < curCat->viewfilters.nFSizeMin || (curCat->viewfilters.nFSizeMax == 0 || nFileSize > curCat->viewfilters.nFSizeMax)))
			continue;
		return ix;
	}

	return 0;
}

// Checks a part-file's "pretty filename" against a filter mask and returns
// true if it passes.  See read-me for details.
bool CDownloadQueue::ApplyFilterMask(CString sFullName, int nCat)
{
	CString sFilterMask = thePrefs.GetCategory(nCat)->viewfilters.sAdvancedFilterMask;
	sFilterMask.Trim();

	if (sFilterMask == "")
		return false;

		sFullName.MakeLower();
	sFilterMask.MakeLower();

	if (sFilterMask.Left(1) == "<")
		{
			bool bPassedGlobal[3];
			bPassedGlobal[0] = false;
			bPassedGlobal[1] = true;
			bPassedGlobal[2] = false;

			for (int i = 0; i < 3; i++)
			{
				int iStart = 0;
				switch (i)
				{
			case 0: iStart = sFilterMask.Find(_T("<all(")); break;
			case 1: iStart = sFilterMask.Find(_T("<any(")); break;
			case 2: iStart = sFilterMask.Find(_T("<none(")); break;
				}

				if (iStart == -1)
				{
					bPassedGlobal[i] = true; // We need to do this since not all criteria are needed in order to match the category.
					continue; // Skip this criteria block.
				}

				i !=2 ? (iStart += 5) : (iStart += 6);

			int iEnd = sFilterMask.Find(_T(")>"), iStart);
			int iLT = sFilterMask.Find(_T("<"), iStart);
			int iGT = sFilterMask.Find(_T(">"), iStart);

				if (iEnd == -1 || (iLT != -1 && iLT < iEnd) || iGT < iEnd)
				{
				AddDebugLogLine(false, _T("Category '%s' has invalid Category Mask String."), thePrefs.GetCategory(nCat)->title);
					break; // Move on to next category.
				}
				if (iStart == iEnd)
				{
					bPassedGlobal[i] = true; // Just because this criteria block is empty doesn't mean the mask should fail.
					continue; // Skip this criteria block.
				}

			CString sSegment = sFilterMask.Mid(iStart, iEnd - iStart);

				int curPosBlock = 0;
				CString cmpSubBlock = sSegment.Tokenize(_T(":"), curPosBlock);

				while (cmpSubBlock != "")
				{
					bool bPassed = (i == 1) ? false : true;

					int curPosToken = 0;
					CString cmpSubStr = cmpSubBlock.Tokenize(_T("|"), curPosToken);

				while (cmpSubStr != "")
				{
					int cmpResult;

					if (cmpSubStr.Find(_T("*")) != -1 || cmpSubStr.Find(_T("?")) != -1)
						cmpResult = (wildcmp(cmpSubStr.GetBuffer(), sFullName.GetBuffer()) == 0) ? -1 : 1;
					else
						cmpResult = sFullName.Find(cmpSubStr);

					switch (i)
					{
							case 0:	if (cmpResult == -1) bPassed = false; break;
							case 1:	if (cmpResult != -1) bPassed = true; break;
							case 2:	if (cmpResult != -1) bPassed = false; break;
						}
						cmpSubStr = cmpSubBlock.Tokenize(_T("|"), curPosToken);
						}
					switch (i)
						{
						case 0:
						case 2: if (bPassed) bPassedGlobal[i] = true; break;
						case 1: if (!bPassed) bPassedGlobal[i] = false; break;
						}
					cmpSubBlock = sSegment.Tokenize(_T(":"), curPosBlock);
					}
				}
			for (int i = 0; i < 3; i++)
			if (!bPassedGlobal[i]) return false;
		return true;
		}
		else
		{
			int curPos = 0;
		CString cmpSubStr = sFilterMask.Tokenize(_T("|"), curPos);

			while (cmpSubStr != "")
			{
				int cmpResult;

				if (cmpSubStr.Find(_T("*")) != -1 || cmpSubStr.Find(_T("?")) != -1)
					cmpResult = (wildcmp(cmpSubStr.GetBuffer(), sFullName.GetBuffer()) == 0) ? -1 : 1;
				else
					cmpResult = sFullName.Find(cmpSubStr);

				if(cmpResult != -1)
				return true;
			cmpSubStr = sFilterMask.Tokenize(_T("|"), curPos);
			}
		}
	return false;
}
#endif // A4AF_CATS // NEO: MAC END <-- Xanatos --

#ifdef A4AF_CATS // NEO: MAC - [MorphA4AFCategories] -- Xanatos -->
void CDownloadQueue::AddFileLinkToDownload(CED2KFileLink* pLink, int cat, bool AllocatedLink)
{
	if (thePrefs.SelectCatForNewDL() && cat==-1)
	{
		m_ED2KLinkQueue.AddTail(pLink);
		m_iLastLinkQueuedTick = GetTickCount();
		return;
	}

	if (cat == -1){
		if (thePrefs.UseAutoCat())
			cat = theApp.downloadqueue->GetAutoCat(CString(pLink->GetName()), pLink->GetSize());
		else
			cat = 0;
		
		if (!cat && thePrefs.UseActiveCatForLinks())
			cat = theApp.emuledlg->transferwnd->GetActiveCategory();
	}
	// Just in case...
	if (m_ED2KLinkQueue.GetCount() && !thePrefs.SelectCatForNewDL()) PurgeED2KLinkQueue();
		m_iLastLinkQueuedTick = 0;
#else
void CDownloadQueue::AddFileLinkToDownload(CED2KFileLink* pLink, int cat)
{
#endif // A4AF_CATS // NEO: MAC END <-- Xanatos --

	CPartFile* newfile = new CPartFile(pLink, cat);
	if (newfile->GetStatus() == PS_ERROR){
		delete newfile;
		newfile=NULL;
	}
	else {
#ifdef A4AF_CATS // NEO: MAC - [MorphA4AFCategories] -- Xanatos -->
		if (thePrefs.SmallFileDLPush() && newfile->GetFileSize() < thePrefs.SmallFileDLPushSizeB())
			newfile->SetCatResumeOrder(0);
		else if (thePrefs.AutoSetResumeOrder())
			newfile->SetCatResumeOrder(GetMaxCatResumeOrder(cat)+1);
#endif // A4AF_CATS // NEO: MAC END <-- Xanatos --
		AddDownload(newfile,thePrefs.AddNewFilesPaused());
	}

	CPartFile* partfile = newfile;
	if (partfile == NULL)
		partfile = GetFileByID(pLink->GetHashKey());
	if (partfile)
	{
		if (pLink->HasValidSources())
			partfile->AddClientSources(pLink->SourcesList,1);
		if (pLink->HasValidAICHHash() ){
			if ( !(partfile->GetAICHHashset()->HasValidMasterHash() && partfile->GetAICHHashset()->GetMasterHash() == pLink->GetAICHHash())){
				partfile->GetAICHHashset()->SetMasterHash(pLink->GetAICHHash(), AICH_VERIFIED);
				partfile->GetAICHHashset()->FreeHashSet();
			}
		}
	}

	if (pLink->HasHostnameSources())
	{
		POSITION pos = pLink->m_HostnameSourcesList.GetHeadPosition();
		while (pos != NULL)
		{
			const SUnresolvedHostname* pUnresHost = pLink->m_HostnameSourcesList.GetNext(pos);
			m_srcwnd.AddToResolve(pLink->GetHashKey(), pUnresHost->strHostname, pUnresHost->nPort, pUnresHost->strURL);
		}
	}

#ifdef A4AF_CATS // NEO: MAC - [MorphA4AFCategories] -- Xanatos -->
	// Deallocate memory, because if we've gotten here,
	// this link wasn't added to the queue and therefore there's no reason to
	// not delete it.
	if (AllocatedLink) {
		delete pLink;
		pLink = NULL;
	}
#endif // A4AF_CATS // NEO: MAC END <-- Xanatos --
}

void CDownloadQueue::AddToResolved( CPartFile* pFile, SUnresolvedHostname* pUH )
{
	if( pFile && pUH )
		m_srcwnd.AddToResolve( pFile->GetFileHash(), pUH->strHostname, pUH->nPort, pUH->strURL);
}

void CDownloadQueue::AddDownload(CPartFile* newfile,bool paused) {
	// Barry - Add in paused mode if required
	if (paused)
		newfile->PauseFile();
	
#ifndef A4AF_CATS // NEO: MAC - [MorphA4AFCategories] -- Xanatos -->
	SetAutoCat(newfile);// HoaX_69 / Slugfiller: AutoCat
#endif // A4AF_CATS // NEO: MAC END <-- Xanatos --

	filelist.AddTail(newfile);
	SortByPriority();
	CheckDiskspace();
	theApp.emuledlg->transferwnd->downloadlistctrl.AddFile(newfile);
	// NEO: SEF - [ShareAlsoEmptyFiles] -- Xanatos -->
	//if (newfile->GetStatus(true) == PS_READY // moved from CPartFileConvert::performConvertToeMule
	// || (thePrefs.IsShareAlsoEmptyFiles() && newfile->GetStatus(true) == PS_EMPTY)) 
	theApp.sharedfiles->SafeAddKFile(newfile);
	// NEO: SEF END <-- Xanatos --
	AddLogLine(true, GetResString(IDS_NEWDOWNLOAD), newfile->GetFileName());
	CString msgTemp;
	msgTemp.Format(GetResString(IDS_NEWDOWNLOAD) + _T("\n"), newfile->GetFileName());
	theApp.emuledlg->ShowNotifier(msgTemp, TBN_DOWNLOADADDED);
	ExportPartMetFilesOverview();

#ifdef VOODOO // NEO: VOODOO - [UniversalPartfileInterface] -- Xanatos -->
	if(newfile->KnownPrefs.UseVoodoo())
		theApp.voodoo->ManifestDownloadOrder(newfile);
#endif // VOODOO // NEO: VOODOO END <-- Xanatos --
}

bool CDownloadQueue::IsFileExisting(const uchar* fileid, CString filename) const // NEO: MAC - [MorphA4AFCategories] <-- Xanatos --
{
	bool bLogWarnings = filename.IsEmpty() == false; // NEO: MAC - [MorphA4AFCategories] <-- Xanatos --
	const CKnownFile* file = theApp.sharedfiles->GetFileByID(fileid);
	if (file){
		if (bLogWarnings){
			if (file->IsPartFile())
				LogWarning(LOG_STATUSBAR, GetResString(IDS_ERR_ALREADY_DOWNLOADING), file->GetFileName());
			else
				LogWarning(LOG_STATUSBAR, GetResString(IDS_ERR_ALREADY_DOWNLOADED), file->GetFileName());
		}
		return true;
	}
	else if ((file = GetFileByID(fileid)) != NULL){
		if (bLogWarnings)
			LogWarning(LOG_STATUSBAR, GetResString(IDS_ERR_ALREADY_DOWNLOADING), file->GetFileName());
		return true;
	}
#ifdef A4AF_CATS // NEO: MAC - [MorphA4AFCategories] -- Xanatos -->
	if(bLogWarnings && thePrefs.CheckAlreadyDownloaded())
	{
		if(theApp.knownfiles->CheckAlreadyDownloadedFileQuestion(fileid,filename) == false)
			return true;
	}
#endif // A4AF_CATS // NEO: MAC END <-- Xanatos --
	return false;
}


void CDownloadQueue::Process(){
	
	ProcessLocalRequests(); // send src requests to local server

	// NEO: QS - [QuickStart] -- Xanatos -->
	if (!m_bQuickStartDone)
		if(thePrefs.UseQuickStart() || thePrefs.OnQuickStart())
		{
			if(thePrefs.UseQuickStart() == TRUE || theApp.IsWorkingAllowed(WRK_ASKING))
			{
				if(!thePrefs.OnQuickStart())
					DoQuickStart();
				else if (m_dwQuickStartEndTime <= ::GetTickCount())
					StopQuickStart();
			}
		}else
			m_bQuickStartDone = true;
	// NEO: QS END <-- Xanatos --

	uint32 downspeed = 0;
#ifdef NEO_BC // NEO: NBC - [NeoBandwidthControl] -- Xanatos -->
	uint64 maxDownload = (uint64)(theApp.bandwidthControl->GetMaxDownload() * 1024);

	if(thePrefs.IsDirectDownload())
#else
	uint64 maxDownload = thePrefs.GetMaxDownloadInBytesPerSec(true);
#endif // NEO_BC // NEO: NBC END <-- Xanatos --
	{
		//if (maxDownload != UNLIMITED*1024 && datarate > maxDownload/2 + 1500){ // NEO: NBC - [NeoBandwidthControl] <-- Xanatos --
		if (maxDownload != UNLIMITED*1024){ // NEO: NBC - [NeoBandwidthControl] <-- Xanatos --
			downspeed = (UINT)((maxDownload*100)/(datarate+1));
			if (downspeed < 50)
				downspeed = 50;
			else if (downspeed > 200)
				downspeed = 200;
		}
	}

	udcounter++;

	// NEO: AHL - [AutoHardLimit] -- Xanatos -->
	uint16	FileCount = 0;
	uint32	SourceCount = 0;
	// NEO: AHL END <-- Xanatos --
	// NEO: NCAP - [NeoCustomAutoPriority] -- Xanatos -->
	uint32 CurrentCount = 0;
	uint32 uGlobalSourceCount = 0x00000000;
	uint32 uTopSourceCount = 0x00000000;
	uint32 uBotomSourceCount = 0xFFFFFFFF;
	// NEO: NCAP END <-- Xanatos --

	//filelist is already sorted by prio, therefore I removed all the extra loops..
	for (POSITION pos = filelist.GetHeadPosition();pos != 0;){
		CPartFile* cur_file = filelist.GetNext(pos);
		if (cur_file->GetStatus() == PS_READY || cur_file->GetStatus() == PS_EMPTY){
			/*datarateX +=*/ cur_file->Process(downspeed,udcounter);
			// NEO: AHL - [AutoHardLimit] -- Xanatos -->
			FileCount++;
			SourceCount += cur_file->GetSourceCount() - cur_file->GetCachedSourceCount(); // NEO: XSC - [ExtremeSourceCache]
			// NEO: AHL END <-- Xanatos --
			// NEO: NCAP - [NeoCustomAutoPriority] -- Xanatos -->
#if defined(NEO_SS) || defined(NEO_SK) // NEO: NSS - [NeoSourceStorage] -- Xanatos -->
			CurrentCount = cur_file->GetActiveSourceCount();
#else
			CurrentCount = cur_file->GetSourceCount();
#endif // NEO_SS // NEO_SK // NEO: NSS END <-- Xanatos --

			uGlobalSourceCount += CurrentCount;
			if(uTopSourceCount < CurrentCount)
				uTopSourceCount = CurrentCount;
			if(uBotomSourceCount > CurrentCount)
				uBotomSourceCount = CurrentCount;
			// NEO: NCAP END <-- Xanatos --
		}
		else{
			//This will make sure we don't keep old sources to paused and stoped files..
			cur_file->StopPausedFile();
		}
	}

	// NEO: AHL - [AutoHardLimit] -- Xanatos -->
	m_uActiveFileCount = FileCount;
	m_uTotalSourceCount = SourceCount;
	// NEO: AHL END <-- Xanatos --
	
	// NEO: NCAP - [NeoCustomAutoPriority] -- Xanatos -->
	m_uGlobalSourceCount = uGlobalSourceCount;
	m_uTopSourceCount = uTopSourceCount;
	m_uBotomSourceCount = uBotomSourceCount;
	// NEO: NCAP END <-- Xanatos --

#ifdef WEBCACHE // NEO: WC - [WebCache] -- Xanatos -->
	if (thePrefs.IsWebCacheEnabled() && udcounter == 7 && thePrefs.expectingWebCachePing && (::GetTickCount() - thePrefs.WebCachePingSendTime > SEC2MS(30)))
	{
		thePrefs.expectingWebCachePing = false;
		thePrefs.WebCacheDisabledThisSession = true; //Disable webcache downloads for the current proxy settings
		//JP we need a modeless dialogue here!!
		//			AfxMessageBox(_T("Proxy configuration Test Failed please review your proxy-settings"));
		theApp.QueueLogLine(false, _T("Proxy configuration Test Failed please review your proxy-settings. Webcache downloads have been deactivated until it has been disabled and reenabled in preferences."));
	}

#endif // NEO: WC END <-- Xanatos --

	if (udcounter == 5){
		if (theApp.serverconnect->IsUDPSocketAvailable()){
		    if((!lastudpstattime) || (::GetTickCount() - lastudpstattime) > UDPSERVERSTATTIME){
			    lastudpstattime = ::GetTickCount();
			    theApp.serverlist->ServerStats();
		    }
	    }
	}

	if (udcounter >= 10){ // NEO: FIX <-- Xanatos --
		udcounter = 0;
		if (theApp.serverconnect->IsUDPSocketAvailable()){
			if ((!lastudpsearchtime) || (::GetTickCount() - lastudpsearchtime) > thePrefs.GetGlobalUDPIntervalsMs() /*UDPSERVERREASKTIME*/) // NEO: NST - [NeoSourceTweaks] <-- Xanatos --
				SendNextUDPPacket();
		}
	}

#ifdef A4AF_CATS // NEO: MAC - [MorphA4AFCategories] -- Xanatos -->
	if (m_iLastLinkQueuedTick && !m_bBusyPurgingLinks && (GetTickCount() - m_iLastLinkQueuedTick) > 400)
		PurgeED2KLinkQueue();
	else if (m_ED2KLinkQueue.GetCount() && !thePrefs.SelectCatForNewDL()) // This should not happen.
	{
		PurgeED2KLinkQueue();
		AddDebugLogLine(false, _T("ERROR: Links in ED2K Link Queue while SelectCatForNewDL was disabled!"));
	}
#endif // A4AF_CATS // NEO: MAC END <-- Xanatos --

	CheckDiskspaceTimed();

// ZZ:DownloadManager -->
    if((!m_dwLastA4AFtime) || (::GetTickCount() - m_dwLastA4AFtime) > 2*60*1000) {
        theApp.clientlist->ProcessA4AFClients();
        m_dwLastA4AFtime = ::GetTickCount();
    }
// <-- ZZ:DownloadManager
}

// NEO: ASM - [AccurateSpeedMeasure] -- Xanatos -->
void CDownloadQueue::CalculateDownloadRate()
{
	while(avarage_dr_list.GetCount()>0 && (GetTickCount() - avarage_dr_list.GetHead().timestamp > 10*1000) )
		m_datarateMS-=avarage_dr_list.RemoveHead().datalen;
	
	if (avarage_dr_list.GetCount()>1){
		datarate = (UINT)(m_datarateMS / avarage_dr_list.GetCount());
	} else {
		datarate = 0;
	}

	uint32 datarateX=0;

	for (POSITION pos = filelist.GetHeadPosition(); pos != 0; ){
		datarateX += filelist.GetNext(pos)->CalculateDownloadRate();
	}

	TransferredData newitem = {datarateX, ::GetTickCount()};
	avarage_dr_list.AddTail(newitem);
	m_datarateMS+=datarateX;
}
// NEO: ASM END <-- Xanatos --

CPartFile* CDownloadQueue::GetFileByIndex(int index) const
{
	POSITION pos = filelist.FindIndex(index);
	if (pos)
		return filelist.GetAt(pos);
	return NULL;
}

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

CPartFile* CDownloadQueue::GetFileByKadFileSearchID(uint32 id) const
{
	for (POSITION pos = filelist.GetHeadPosition(); pos != 0; )
	{
		CPartFile* cur_file = filelist.GetNext(pos);
		if (id == cur_file->GetKadFileSearchID())
			return cur_file;
	}
	return NULL;
}

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

// NEO: SSH - [SlugFillerSafeHash] -- Xanatos -->
// BEGIN SLUGFILLER: SafeHash
bool CDownloadQueue::IsTempFile(const CString& , const CString& rstrName) const
{
	// do not share a part file from the temp directory, if there is still a corresponding entry in
	// the download queue -- because that part file is not yet complete.
	CString othername = rstrName + _T(".met");
	for (POSITION pos = filelist.GetHeadPosition();pos != 0;){
		CPartFile* cur_file = filelist.GetNext(pos);
		if (!othername.CompareNoCase(cur_file->GetPartMetFileName()))
			return true;
	}

	return false;
}
// END SLUGFILLER: SafeHash
// NEO: SSH END <-- Xanatos --

bool CDownloadQueue::CheckAndAddSource(CPartFile* sender,CUpDownClient* source){
	if (sender->IsStopped()){
		delete source;
		return false;
	}

	if (source->HasValidHash())
	{
		if(!md4cmp(source->GetUserHash(), thePrefs.GetUserHash()))
		{
			if (thePrefs.GetVerbose())
				AddDebugLogLine(false, _T("Tried to add source with matching hash to your own."));
			delete source;
			return false;
		}
	}
	// filter sources which are known to be dead/useless
		if (theApp.clientlist->m_globDeadSourceList.IsDeadSource(source) || sender->m_DeadSourceList.IsDeadSource(source)){
			//if (thePrefs.GetLogFilteredIPs())
			//	AddDebugLogLine(DLP_DEFAULT, false, _T("Rejected source because it was found on the DeadSourcesList (%s) for file %s : %s")
			//	,sender->m_DeadSourceList.IsDeadSource(source)? _T("Local") : _T("Global"), sender->GetFileName(), source->DbgGetClientInfo() );
			delete source;
			return false;
		}

	// filter sources which are incompatible with our encryption setting (one requires it, and the other one doesn't supports it)
	if ( (source->RequiresCryptLayer() && (!thePrefs.IsClientCryptLayerSupported() || !source->HasValidHash())) || (thePrefs.IsClientCryptLayerRequired() && (!source->SupportsCryptLayer() || !source->HasValidHash())))
	{
#if defined(_DEBUG) || defined(_BETA)
		//if (thePrefs.GetDebugSourceExchange()) // TODO: Uncomment after testing
			AddDebugLogLine(DLP_DEFAULT, false, _T("Rejected source because CryptLayer-Setting (Obfuscation) was incompatible for file %s : %s"), sender->GetFileName(), source->DbgGetClientInfo() );
#endif
		delete source;
		return false;
	}

	// "Filter LAN IPs" and/or "IPfilter" is not required here, because it was already done in parent functions

#ifdef LANCAST // NEO: NLC - [NeoLanCast] -- Xanatos -->
	const bMultiTransfer = source->IsLanClient() && thePrefs.UseLanMultiTransfer();
#endif //LANCAST // NEO: NLC END <-- Xanatos --

	// uses this only for temp. clients
	for (POSITION pos = filelist.GetHeadPosition();pos != 0;){
		CPartFile* cur_file = filelist.GetNext(pos);
#ifdef LANCAST // NEO: NLC - [NeoLanCast] -- Xanatos -->
		if(bMultiTransfer && cur_file != sender)
			continue;
#endif //LANCAST // NEO: NLC END <-- Xanatos --
		for (POSITION pos2 = cur_file->srclist.GetHeadPosition();pos2 != 0; ){
			CUpDownClient* cur_client = cur_file->srclist.GetNext(pos2);
			if (cur_client->Compare(source, true) || cur_client->Compare(source, false)){
#if defined(NEO_SK) || defined(NEO_SS) // NEO: NSK - [NeoSourceKeeper] // NEO: NSS - [NeoSourceStorage] -- Xanatos -->
 #ifdef VOODOO // NEO: VOODOOx - [VoodooSourceExchange]
				bool bRevivalSource = false;
 #endif // VOODOO // NEO: VOODOOx END
				// If we find a matching clinet, that is in suspend state
				// It meen he is avalibly again, so the proper state
				// And if needed update the ip of our source
				if(cur_client->IsSurceSuspended(true,true)){
					if(cur_client->GetIP() != source->GetConnectIP() || cur_client->GetUserPort() != source->GetUserPort()){ // UserHash match, but IP is new
						cur_client->SetConnectIP(source->GetConnectIP());
						cur_client->SetUserPort(source->GetUserPort());
						cur_client->SetSourceFrom(source->GetSourceFrom());
					}
					if(source->GetDownloadState() != DS_CACHED){
						cur_client->SetDownloadState(DS_REVIVAL);
 #ifdef VOODOO // NEO: VOODOOx - [VoodooSourceExchange]
						bRevivalSource = true;
 #endif // VOODOO // NEO: VOODOOx END
					}
				}
#endif // NEO_SK // NEO_SS // NEO: NSK END // NEO: NSS END <-- Xanatos --

				//if (cur_file != sender // this file has already this source
				// && cur_client->AddRequestForAnotherFile(sender)){
				//	theApp.emuledlg->transferwnd->downloadlistctrl.AddSource(sender,cur_client,true);
                //    if(cur_client->GetDownloadState() != DS_CONNECTED) {
                //        cur_client->SwapToAnotherFile(_T("New A4AF source found. CDownloadQueue::CheckAndAddSource()"), false, false, false, NULL, true, false); // ZZ:DownloadManager
                //    }
				//}
				//delete source;
				//return false;

				if (cur_file == sender){ // this file has already this source
#ifdef VOODOO // NEO: VOODOOx - [VoodooSourceExchange] -- Xanatos -->
					// If we have a revival source notyfy the other clinet about it
					if(thePrefs.UseVoodooSourceExchange() == TRUE && sender->KnownPrefs.UseVoodoo() && bRevivalSource)
						theApp.voodoo->ManifestSingleSource(sender,cur_client);
#endif // VOODOO // NEO: VOODOOx END <-- Xanatos --
					delete source;
					return false;
				}
				// set request for this source
				if (cur_client->AddRequestForAnotherFile(sender)){
					theApp.emuledlg->transferwnd->downloadlistctrl.AddSource(sender,cur_client,true);
#ifdef VOODOO // NEO: VOODOOx - [VoodooSourceExchange] -- Xanatos -->
					// Exchange also a4af sources
					if(thePrefs.UseVoodooSourceExchange() == TRUE && sender->KnownPrefs.UseVoodoo())
						theApp.voodoo->ManifestSingleSource(sender,cur_client);
#endif // VOODOO // NEO: VOODOOx END <-- Xanatos --
					delete source;
                    if(cur_client->GetDownloadState() != DS_CONNECTED) {
                        cur_client->SwapToAnotherFile(_T("New A4AF source found. CDownloadQueue::CheckAndAddSource()"), false, false, false, NULL, true, false); // ZZ:DownloadManager
                    }
					return false;
				}
				else{
					delete source;
					return false;
				}

			}
		}
	}
	//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(
#ifdef LANCAST // NEO: NLC - [NeoLanCast] -- Xanatos -->
	 !bMultiTransfer &&
#endif //LANCAST // NEO: NLC END <-- Xanatos --
	 theApp.clientlist->AttachToAlreadyKnown(&source,0)
	 ){
#ifdef _DEBUG
		if (thePrefs.GetVerbose() && source->GetRequestFile()){
			// if a client sent us wrong sources (sources for some other file for which we asked but which we are also
			// downloading) we may get a little in trouble here when "moving" this source to some other partfile without
			// further checks and updates.
			if (md4cmp(source->GetRequestFile()->GetFileHash(), sender->GetFileHash()) != 0)
				AddDebugLogLine(false, _T("*** CDownloadQueue::CheckAndAddSource -- added potential wrong source (%u)(diff. filehash) to file \"%s\""), source->GetUserIDHybrid(), sender->GetFileName());
			if (source->GetRequestFile()->GetPartCount() != 0 && source->GetRequestFile()->GetPartCount() != sender->GetPartCount())
				AddDebugLogLine(false, _T("*** CDownloadQueue::CheckAndAddSource -- added potential wrong source (%u)(diff. partcount) to file \"%s\""), source->GetUserIDHybrid(), sender->GetFileName());
		}
#endif
		source->SetRequestFile(sender);
	}
	else{
		// here we know that the client instance 'source' is a new created client instance (see callers) 
		// which is therefor not already in the clientlist, we can avoid the check for duplicate client list entries 
		// when adding this client
		theApp.clientlist->AddClient(source,true);
	}
	
	// NEO: SCFS - [SmartClientFileStatus] -- Xanatos --
//#ifdef _DEBUG
//	if (thePrefs.GetVerbose() && source->GetPartCount()!=0 && source->GetPartCount()!=sender->GetPartCount()){
//		DEBUG_ONLY(AddDebugLogLine(false, _T("*** CDownloadQueue::CheckAndAddSource -- New added source (%u, %s) had still value in partcount"), source->GetUserIDHybrid(), sender->GetFileName()));
//	}
//#endif

	sender->srclist.AddTail(source);
	theApp.emuledlg->transferwnd->downloadlistctrl.AddSource(sender,source,false);
#ifdef VOODOO // NEO: VOODOOx - [VoodooSourceExchange] -- Xanatos -->
	if(thePrefs.UseVoodooSourceExchange() == TRUE && sender->KnownPrefs.UseVoodoo())
		theApp.voodoo->ManifestSingleSource(sender,source);
#endif // VOODOO // NEO: VOODOOx END <-- Xanatos --
	return true;
}

bool CDownloadQueue::CheckAndAddKnownSource(CPartFile* sender,CUpDownClient* source, bool bIgnoreGlobDeadList){
	if (sender->IsStopped())
		return false;

	// filter sources which are known to be dead/useless
		if ( (theApp.clientlist->m_globDeadSourceList.IsDeadSource(source) && !bIgnoreGlobDeadList) || sender->m_DeadSourceList.IsDeadSource(source)){
			//if (thePrefs.GetLogFilteredIPs())
			//	AddDebugLogLine(DLP_DEFAULT, false, _T("Rejected source because it was found on the DeadSourcesList (%s) for file %s : %s")
			//	,sender->m_DeadSourceList.IsDeadSource(source)? _T("Local") : _T("Global"), sender->GetFileName(), source->DbgGetClientInfo() );
			return false;
		}

	// filter sources which are incompatible with our encryption setting (one requires it, and the other one doesn't supports it)
	if ( (source->RequiresCryptLayer() && (!thePrefs.IsClientCryptLayerSupported() || !source->HasValidHash())) || (thePrefs.IsClientCryptLayerRequired() && (!source->SupportsCryptLayer() || !source->HasValidHash())))
	{
#if defined(_DEBUG) || defined(_BETA)
		//if (thePrefs.GetDebugSourceExchange()) // TODO: Uncomment after testing
			AddDebugLogLine(DLP_DEFAULT, false, _T("Rejected source because CryptLayer-Setting (Obfuscation) was incompatible for file %s : %s"), sender->GetFileName(), source->DbgGetClientInfo() );
#endif
		return false;
	}

	// "Filter LAN IPs" -- this may be needed here in case we are connected to the internet and are also connected
	// to a LAN and some client from within the LAN connected to us. Though this situation may be supported in future
	// by adding that client to the source list and filtering that client's LAN IP when sending sources to
	// a client within the internet.
	//
	// "IPfilter" is not needed here, because that "known" client was already IPfiltered when receiving OP_HELLO.
	if (!source->HasLowID()
#ifdef LANCAST // NEO: NLC - [NeoLanCast] -- Xanatos -->
	 && !source->IsLanClient()	// LAN clients would always be filtered, so don't filter identified LanCast users
#endif //LANCAST // NEO: NLC END <-- Xanatos --
		){
		uint32 nClientIP = ntohl(source->GetUserIDHybrid());
		if (!IsGoodIP(nClientIP)){ // check for 0-IP, localhost and LAN addresses
			//if (thePrefs.GetLogFilteredIPs())
			//	AddDebugLogLine(false, _T("Ignored already known source with IP=%s"), ipstr(nClientIP));
			return false;
		}
	}

	// use this for client which are already know (downloading for example)
	for (POSITION pos = filelist.GetHeadPosition();pos != 0;){
		CPartFile* cur_file = filelist.GetNext(pos);
		if (cur_file->srclist.Find(source)){

#if defined(NEO_SK) || defined(NEO_SS) // NEO: NSK - [NeoSourceKeeper] // NEO: NSS - [NeoSourceStorage] -- Xanatos -->
 #ifdef VOODOO // NEO: VOODOOx - [VoodooSourceExchange]
				bool bRevivalSource = false;
 #endif // VOODOO // NEO: VOODOOx END
				// If we find a matching clinet, that is in suspend state
				// It meen he is avalibly again, so the proper state
				// And if needed update the ip of our source
				if(source->IsSurceSuspended(true,true)){
					if(source->GetDownloadState() != DS_CACHED){
						source->SetDownloadState(DS_REVIVAL);
 #ifdef VOODOO // NEO: VOODOOx - [VoodooSourceExchange]
						bRevivalSource = true;
 #endif // VOODOO // NEO: VOODOOx END
					}
				}
#endif // NEO_SK // NEO_SS // NEO: NSK END // NEO: NSS END <-- Xanatos --

			if (cur_file == sender){
#ifdef VOODOO // NEO: VOODOOx - [VoodooSourceExchange] -- Xanatos -->
				// If we have a revival source notyfy the other clinet about it
				if(thePrefs.UseVoodooSourceExchange() == TRUE && sender->KnownPrefs.UseVoodoo() && bRevivalSource)
					theApp.voodoo->ManifestSingleSource(sender,source);
#endif // VOODOO // NEO: VOODOOx END <-- Xanatos --
				return false;
			}
			if (source->AddRequestForAnotherFile(sender))
				theApp.emuledlg->transferwnd->downloadlistctrl.AddSource(sender,source,true);
#ifdef VOODOO // NEO: VOODOOx - [VoodooSourceExchange] -- Xanatos -->
				if(thePrefs.UseVoodooSourceExchange() == TRUE && sender->KnownPrefs.UseVoodoo())
					theApp.voodoo->ManifestSingleSource(sender,source);
#endif // VOODOO // NEO: VOODOOx END <-- Xanatos --
                if(source->GetDownloadState() != DS_CONNECTED) {
                    source->SwapToAnotherFile(_T("New A4AF source found. CDownloadQueue::CheckAndAddKnownSource()"), false, false, false, NULL, true, false); // ZZ:DownloadManager
                }
			return false;
		}
	}
#ifdef _DEBUG
	if (thePrefs.GetVerbose() && source->GetRequestFile()){
		// if a client sent us wrong sources (sources for some other file for which we asked but which we are also
		// downloading) we may get a little in trouble here when "moving" this source to some other partfile without
		// further checks and updates.
		if (md4cmp(source->GetRequestFile()->GetFileHash(), sender->GetFileHash()) != 0)
			AddDebugLogLine(false, _T("*** CDownloadQueue::CheckAndAddKnownSource -- added potential wrong source (%u)(diff. filehash) to file \"%s\""), source->GetUserIDHybrid(), sender->GetFileName());
		if (source->GetRequestFile()->GetPartCount() != 0 && source->GetRequestFile()->GetPartCount() != sender->GetPartCount())
			AddDebugLogLine(false, _T("*** CDownloadQueue::CheckAndAddKnownSource -- added potential wrong source (%u)(diff. partcount) to file \"%s\""), source->GetUserIDHybrid(), sender->GetFileName());
	}
#endif
	source->SetRequestFile(sender);
	sender->srclist.AddTail(source);
	source->SetSourceFrom(SF_PASSIVE);
	if (thePrefs.GetDebugSourceExchange())
		AddDebugLogLine(false, _T("SXRecv: Passively added source; %s, File=\"%s\""), source->DbgGetClientInfo(), sender->GetFileName());
	// NEO: SCFS - [SmartClientFileStatus] -- Xanatos --
//#ifdef _DEBUG
//	if (thePrefs.GetVerbose() && source->GetPartCount()!=0 && source->GetPartCount()!=sender->GetPartCount()){
//		DEBUG_ONLY(AddDebugLogLine(false, _T("*** CDownloadQueue::CheckAndAddKnownSource -- New added source (%u, %s) had still value in partcount"), source->GetUserIDHybrid(), sender->GetFileName()));
//	}
//#endif

	theApp.emuledlg->transferwnd->downloadlistctrl.AddSource(sender,source,false);
#ifdef VOODOO // NEO: VOODOOx - [VoodooSourceExchange] -- Xanatos -->
	if(thePrefs.UseVoodooSourceExchange() == TRUE && sender->KnownPrefs.UseVoodoo())
		theApp.voodoo->ManifestSingleSource(sender,source);
#endif // VOODOO // NEO: VOODOOx END <-- Xanatos --
	//UpdateDisplayedInfo();
	return true;
}

bool CDownloadQueue::RemoveSource(CUpDownClient* toremove, bool bDoStatsUpdate)
{
	bool bRemovedSrcFromPartFile = false;
	for (POSITION pos = filelist.GetHeadPosition();pos != 0;){
		CPartFile* cur_file = filelist.GetNext(pos);
		for (POSITION pos2 = cur_file->srclist.GetHeadPosition();pos2 != 0; cur_file->srclist.GetNext(pos2)){
			if (toremove == cur_file->srclist.GetAt(pos2)){
				cur_file->srclist.RemoveAt(pos2);
				bRemovedSrcFromPartFile = true;
				if ( bDoStatsUpdate ){
					cur_file->RemoveDownloadingSource(toremove);
					cur_file->UpdatePartsInfo();
					// NEO: ICS - [InteligentChunkSelection] -- Xanatos -->
					if(toremove->GetIncompletePartVersion())
						cur_file->UpdatePartsInfoEx(CFS_Incomplete);
					// NEO: ICS END <-- Xanatos --
					// NEO: RPS - [RealPartStatus] -- Xanatos -->
					if(toremove->GetHidenPartStatusVersion())
						cur_file->UpdatePartsInfoEx(CFS_Hiden);
					if(toremove->GetBlockedPartStatusVersion())
						cur_file->UpdatePartsInfoEx(CFS_Blocked);
					// NEO: RPS END <-- Xanatos --
				}
				break;
			}
		}
		if ( bDoStatsUpdate )
			cur_file->UpdateAvailablePartsCount();
	}
	
	// remove this source on all files in the downloadqueue who link this source
	// pretty slow but no way arround, maybe using a Map is better, but that's slower on other parts
	POSITION pos3, pos4;
	for(pos3 = toremove->m_OtherRequests_list.GetHeadPosition();(pos4=pos3)!=NULL;)
	{
		toremove->m_OtherRequests_list.GetNext(pos3);				
		POSITION pos5 = toremove->m_OtherRequests_list.GetAt(pos4)->A4AFsrclist.Find(toremove); 
		if(pos5)
		{ 
			toremove->m_OtherRequests_list.GetAt(pos4)->A4AFsrclist.RemoveAt(pos5);
			theApp.emuledlg->transferwnd->downloadlistctrl.RemoveSource(toremove,toremove->m_OtherRequests_list.GetAt(pos4));
			toremove->m_OtherRequests_list.RemoveAt(pos4);
		}
	}
	for(pos3 = toremove->m_OtherNoNeeded_list.GetHeadPosition();(pos4=pos3)!=NULL;)
	{
		toremove->m_OtherNoNeeded_list.GetNext(pos3);				
		POSITION pos5 = toremove->m_OtherNoNeeded_list.GetAt(pos4)->A4AFsrclist.Find(toremove); 
		if(pos5)
		{ 
			toremove->m_OtherNoNeeded_list.GetAt(pos4)->A4AFsrclist.RemoveAt(pos5);
			theApp.emuledlg->transferwnd->downloadlistctrl.RemoveSource(toremove,toremove->m_OtherNoNeeded_list.GetAt(pos4));
			toremove->m_OtherNoNeeded_list.RemoveAt(pos4);
		}
	}

	// NEO: XC - [ExtendedComments] -- Xanatos --
	//if (bRemovedSrcFromPartFile && (toremove->HasFileRating() || !toremove->GetFileComment().IsEmpty()))
	//{
	//	CPartFile* pFile = toremove->GetRequestFile();
	//	if(pFile)
	//		pFile->UpdateFileRatingCommentAvail();
	//}

	toremove->SetDownloadState(DS_NONE);
	theApp.emuledlg->transferwnd->downloadlistctrl.RemoveSource(toremove,0);
	toremove->SetRequestFile(NULL);
	return bRemovedSrcFromPartFile;
}

void CDownloadQueue::RemoveFile(CPartFile* toremove)
{
	RemoveLocalServerRequest(toremove);

	for (POSITION pos = filelist.GetHeadPosition();pos != 0;filelist.GetNext(pos)){
		if (toremove == filelist.GetAt(pos)){
			filelist.RemoveAt(pos);
			break;
		}
	}
	SortByPriority();
	CheckDiskspace();
	ExportPartMetFilesOverview();
}

void CDownloadQueue::DeleteAll(){
	POSITION pos;

#ifdef NEO_SS // NEO: NSS - [NeoSourceStorage] -- Xanatos -->
	// Save source in a separat loop to get all A4AF sources
	theApp.UpdateSplash(GetResString(IDS_X_SS_E_SAVE_SRC)); // NEO: SS - [SplashScreen]
	for (pos = filelist.GetHeadPosition();pos != 0;){
		CPartFile* cur_file = filelist.GetNext(pos);
		if(cur_file->PartPrefs.AutoSaveSources() && !cur_file->IsStopped())
			cur_file->SaveSources();
	}
#endif // NEO_SS // NEO: NSS END <-- Xanatos --

	theApp.UpdateSplash(GetResString(IDS_X_SS_E_CLEAR_SRC)); // NEO: SS - [SplashScreen] <-- Xanatos --
	for (pos = filelist.GetHeadPosition();pos != 0;){
		CPartFile* cur_file = filelist.GetNext(pos);
		cur_file->srclist.RemoveAll();
		cur_file->A4AFsrclist.RemoveAll(); // NEO: FIX <-- Xanatos --
		// 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();
	}

	theApp.UpdateSplash(GetResString(IDS_X_SS_E_CL)); // NEO: SS - [SplashScreen] <-- Xanatos --

#ifdef NEO_DBT // NEO: NDBT - [NeoDownloadBandwidthThrottler] -- Xanatos -->
	ProcessQueue.RemoveAll();
#endif // NEO_DBT // NEO: NDBT END <-- Xanatos --
}

// Max. file IDs per UDP packet
// ----------------------------
// 576 - 30 bytes of header (28 for UDP, 2 for "E3 9A" edonkey proto) = 546 bytes
// 546 / 16 = 34
#define MAX_UDP_PACKET_DATA				510
#define BYTES_PER_FILE_G1				16
#define BYTES_PER_FILE_G2				20
#define ADDITIONAL_BYTES_PER_LARGEFILE	8
#define MAX_REQUESTS_PER_SERVER			35

bool CDownloadQueue::IsMaxFilesPerUDPServerPacketReached(uint32 nFiles, uint32 nIncludedLargeFiles, bool bFullAmount) const // NEO: HUC - [HyperUDPCollecting] <-- Xanatos --
{
	if (cur_udpserver && cur_udpserver->GetUDPFlags() & SRV_UDPFLG_EXT_GETSOURCES)
	{
		
		const int nBytesPerNormalFile = ((cur_udpserver->GetUDPFlags() & SRV_UDPFLG_EXT_GETSOURCES2) > 0)? BYTES_PER_FILE_G2 : BYTES_PER_FILE_G1; 
		const int nUsedBytes = nFiles*nBytesPerNormalFile + nIncludedLargeFiles*ADDITIONAL_BYTES_PER_LARGEFILE;
		if (nIncludedLargeFiles > 0){
			ASSERT( cur_udpserver->SupportsLargeFilesUDP() );
			ASSERT( cur_udpserver->GetUDPFlags() & SRV_UDPFLG_EXT_GETSOURCES2 );
		}
		return (m_cRequestsSentToServer >= /*MAX_REQUESTS_PER_SERVER*/(UINT)thePrefs.GetUDPAmount() && bFullAmount == false) || (nUsedBytes >= MAX_UDP_PACKET_DATA); // NEO: NST - [NeoSourceTweaks] // NEO: HUC - [HyperUDPCollecting] <-- Xanatos --
	}
	else{
		ASSERT( nIncludedLargeFiles == 0);
		return nFiles != 0;
	}
}

bool CDownloadQueue::SendGlobGetSourcesUDPPacket(CSafeMemFile* data, bool bExt2Packet, uint32 nFiles, uint32 nIncludedLargeFiles, CServer* man_udpserver) // NEO: HUC - [HyperUDPCollecting] <-- Xanatos --
{
	bool bSentPacket = false;
	CServer* udpserver = man_udpserver ? man_udpserver : cur_udpserver; // NEO: HUC - [HyperUDPCollecting] <-- Xanatos --
	if (udpserver != NULL) // NEO: HUC - [HyperUDPCollecting] <-- Xanatos --
	{
#ifdef _DEBUG
		int iPacketSize = (int)data->GetLength();
#endif
		Packet packet(data);
		data = NULL;
		if (bExt2Packet){
			ASSERT( iPacketSize > 0 && (uint32)iPacketSize == nFiles*20 + nIncludedLargeFiles*8);
			packet.opcode = OP_GLOBGETSOURCES2;
		}
		else{
			ASSERT( iPacketSize > 0 && (uint32)iPacketSize == nFiles*16 && nIncludedLargeFiles == 0);
			packet.opcode = OP_GLOBGETSOURCES;
		}
		if (thePrefs.GetDebugServerUDPLevel() > 0)
			Debug(_T(">>> Sending %s to server %-21s (%3u of %3u); FileIDs=%u(%u large)\n"), (packet.opcode == OP_GLOBGETSOURCES2) ? _T("OP__GlobGetSources2") : _T("OP__GlobGetSources1"), ipstr(udpserver->GetAddress(), udpserver->GetPort()), m_iSearchedServers + 1, theApp.serverlist->GetServerCount(), nFiles, nIncludedLargeFiles); // NEO: HUC - [HyperUDPCollecting] <-- Xanatos --

		theStats.AddUpDataOverheadServer(packet.size);
		theApp.serverconnect->SendUDPPacket(&packet, udpserver, false); // NEO: HUC - [HyperUDPCollecting] <-- Xanatos --

		if(man_udpserver == NULL) // NEO: HUC - [HyperUDPCollecting] <-- Xanatos --
			m_cRequestsSentToServer += nFiles;
		bSentPacket = true;
	}

	return bSentPacket;
}

bool CDownloadQueue::SendNextUDPPacket()
{
	if (   filelist.IsEmpty()
        || !theApp.serverconnect->IsUDPSocketAvailable()
        || !theApp.IsWorkingAllowed(WRK_REQUESTING) // NEO: SO - [StandAlone] <-- Xanatos --
		|| thePrefs.IsClientCryptLayerRequired()) // we cannot use sources received without userhash, so dont ask
		return false;

	//CServer* pConnectedServer = theApp.serverconnect->GetCurrentServer();
	//if (pConnectedServer)
	//	pConnectedServer = theApp.serverlist->GetServerByAddress(pConnectedServer->GetAddress(), pConnectedServer->GetPort());

	if (!cur_udpserver)
	{
		while ((cur_udpserver = theApp.serverlist->GetSuccServer(cur_udpserver)) != NULL) {
			if (theApp.serverconnect->IsLocalServer(cur_udpserver->GetIP(),cur_udpserver->GetPort())/*cur_udpserver == pConnectedServer*/) // NEO: NEW - [NewCoded] <-- Xanatos --
				continue;
			if (cur_udpserver->GetFailedCount() >= thePrefs.GetDeadServerRetries())
				continue;
			break;
		}
		if (cur_udpserver == NULL) {
			StopUDPRequests();
			return false;
		}
		m_cRequestsSentToServer = 0;
	}

	bool bGetSources2Packet = (cur_udpserver->GetUDPFlags() & SRV_UDPFLG_EXT_GETSOURCES2) > 0;
	bool bServerSupportsLargeFiles = cur_udpserver->SupportsLargeFilesUDP();

	// loop until the packet is filled or a packet was sent
	bool bSentPacket = false;
	CSafeMemFile dataGlobGetSources(20);
	int iFiles = 0;
	int iLargeFiles = 0;
	while (!IsMaxFilesPerUDPServerPacketReached(iFiles, iLargeFiles) && !bSentPacket)
	{
		// get next file to search sources for
		CPartFile* nextfile = NULL;
		while (!bSentPacket && !(nextfile && (nextfile->GetStatus() == PS_READY || nextfile->GetStatus() == PS_EMPTY)))
		{
			if (lastfile == NULL) // we just started the global source searching or have switched the server
			{
				// get first file to search sources for
				nextfile = filelist.GetHead();
				lastfile = nextfile;
			}
			else
			{
				POSITION pos = filelist.Find(lastfile);
				if (pos == 0) // the last file is no longer in the DL-list (may have been finished or canceld)
				{
					// get first file to search sources for
					nextfile = filelist.GetHead();
					lastfile = nextfile;
				}
				else
				{
					filelist.GetNext(pos);
					if (pos == 0) // finished asking the current server for all files
					{
						// if there are pending requests for the current server, send them
						if (dataGlobGetSources.GetLength() > 0)
						{
							if (SendGlobGetSourcesUDPPacket(&dataGlobGetSources, bGetSources2Packet, iFiles, iLargeFiles))
								bSentPacket = true;
							dataGlobGetSources.SetLength(0);
							iFiles = 0;
							iLargeFiles = 0;
						}

						// get next server to ask
						// David: The itsonlyme: cacheUDPsearchResults is a nice idea but it have a very error: 
						// during udp searching normaly ALL servers are asked, but when the feature gives a server from an other place of the list
						// Come parts of the list may not be asked, or are asked more thanone time,
						// I'v removed the part here, but let is for the manual UDP reask becouse then we are asking only one server so it is goot to ask the best one
						while ((cur_udpserver = theApp.serverlist->GetSuccServer(cur_udpserver/*, thePrefs.UseUDPCache() ? lastfile : NULL*/)) != NULL) { // NEO: XUC - [ExtendedUdpCache] <-- Xanatos -- 
							if (theApp.serverconnect->IsLocalServer(cur_udpserver->GetIP(),cur_udpserver->GetPort())/*cur_udpserver == pConnectedServer*/) // NEO: NEW - [NewCoded] <-- Xanatos --
								continue;
							if (cur_udpserver->GetFailedCount() >= thePrefs.GetDeadServerRetries())
								continue;
							break;
						}
						m_cRequestsSentToServer = 0;
						if (cur_udpserver == NULL) {
							// finished asking all servers for all files
							if (thePrefs.GetDebugServerUDPLevel() > 0 && thePrefs.GetDebugServerSourcesLevel() > 0)
								Debug(_T("Finished UDP search processing for all servers (%u)\n"), theApp.serverlist->GetServerCount());
							StopUDPRequests();
							return false; // finished (processed all file & all servers)
						}
						m_iSearchedServers++;

						// if we already sent a packet, switch to the next file at next function call
						if (bSentPacket){
							lastfile = NULL;
							break;
						}

						bGetSources2Packet = (cur_udpserver->GetUDPFlags() & SRV_UDPFLG_EXT_GETSOURCES2) > 0;
						bServerSupportsLargeFiles = cur_udpserver->SupportsLargeFilesUDP();

						// have selected a new server; get first file to search sources for
						nextfile = filelist.GetHead();
						lastfile = nextfile;
					}
					else
					{
						nextfile = filelist.GetAt(pos);
						lastfile = nextfile;
					}
				}
			}
		}

		if (!bSentPacket && nextfile && (bServerSupportsLargeFiles || !nextfile->IsLargeFile()) && 
#if defined(NEO_SS) || defined(NEO_SK) // NEO: NSS - [NeoSourceStorage] -- Xanatos -->
			nextfile->GetActiveSourceCount() < (UINT)nextfile->PartPrefs.GetUDPLimit() 
#else
			nextfile->GetSourceCount() < (UINT)nextfile->PartPrefs.GetUDPLimit() 
#endif // NEO_SS // NEO_SK // NEO: NSS END <-- Xanatos --
			&& nextfile->PartPrefs.UseUDP() // NEO: NST - [NeoSourceTweaks] <-- Xanatos --
#ifdef NEO_SS // NEO: NSS - [NeoSourceStorage] -- Xanatos -->
			&& !nextfile->IsStorageBoot(true)
#endif // NEO_SS // NEO: NSS END <-- Xanatos --
		&& (!nextfile->m_LastSearchTimeUdp || (::GetTickCount() - nextfile->m_LastSearchTimeUdp) > nextfile->PartPrefs.GetUDPIntervalsMs()) /*UDPSERVERREASKTIME*/ // NEO: NST - [NeoSourceTweaks] <-- Xanatos --
		){
			if (bGetSources2Packet){
				if (nextfile->IsLargeFile()){
					// GETSOURCES2 Packet Large File (<HASH_16><IND_4 = 0><SIZE_8> *)
					iLargeFiles++;
					dataGlobGetSources.WriteHash16(nextfile->GetFileHash());
					dataGlobGetSources.WriteUInt32(0);
					dataGlobGetSources.WriteUInt64(nextfile->GetFileSize());
				}
				else{
					// GETSOURCES2 Packet (<HASH_16><SIZE_4> *)
					dataGlobGetSources.WriteHash16(nextfile->GetFileHash());
					dataGlobGetSources.WriteUInt32((uint32)(uint64)nextfile->GetFileSize());
				}
			}
			else{
				// GETSOURCES Packet (<HASH_16> *)
				dataGlobGetSources.WriteHash16(nextfile->GetFileHash());
			}
			iFiles++;
			if (thePrefs.GetDebugServerUDPLevel() > 0 && thePrefs.GetDebugServerSourcesLevel() > 0)
				Debug(_T(">>> Queued  %s to server %-21s (%3u of %3u); Buff  %u(%u)=%s\n"), bGetSources2Packet ? _T("OP__GlobGetSources2") : _T("OP__GlobGetSources1"), ipstr(cur_udpserver->GetAddress(), cur_udpserver->GetPort()), m_iSearchedServers + 1, theApp.serverlist->GetServerCount(), iFiles, iLargeFiles, DbgGetFileInfo(nextfile->GetFileHash()));
		}
	}

	ASSERT( dataGlobGetSources.GetLength() == 0 || !bSentPacket );

	if (!bSentPacket && dataGlobGetSources.GetLength() > 0)
		SendGlobGetSourcesUDPPacket(&dataGlobGetSources, bGetSources2Packet, iFiles, iLargeFiles);

	// send max 35 UDP request to one server per interval
	// if we have more than 35 files, we rotate the list and use it as queue
	if (m_cRequestsSentToServer >= /*MAX_REQUESTS_PER_SERVER*/(UINT)thePrefs.GetUDPAmount()) // NEO: NST - [NeoSourceTweaks] <-- Xanatos --
	{
		if (thePrefs.GetDebugServerUDPLevel() > 0 && thePrefs.GetDebugServerSourcesLevel() > 0)
			Debug(_T("Rotating file list\n"));

		// move the last 35 files to the head
		if (filelist.GetCount() >= /*MAX_REQUESTS_PER_SERVER*/thePrefs.GetUDPAmount()) // NEO: NST - [NeoSourceTweaks] <-- Xanatos --
		{
			for (int i = 0; i != /*MAX_REQUESTS_PER_SERVER*/thePrefs.GetUDPAmount(); i++) // NEO: NST - [NeoSourceTweaks] <-- Xanatos --
				filelist.AddHead(filelist.RemoveTail());
		}

		// and next server
		while ((cur_udpserver = theApp.serverlist->GetSuccServer(cur_udpserver/*, thePrefs.UseUDPCache() ? lastfile : NULL*/)) != NULL) { // NEO: XUC - [ExtendedUdpCache] <-- Xanatos --
			if (theApp.serverconnect->IsLocalServer(cur_udpserver->GetIP(),cur_udpserver->GetPort())/*cur_udpserver == pConnectedServer*/) // NEO: NEW - [NewCoded] <-- Xanatos --
				continue;
			if (cur_udpserver->GetFailedCount() >= thePrefs.GetDeadServerRetries())
				continue;
			break;
		}
		m_cRequestsSentToServer = 0;
		if (cur_udpserver == NULL) {
			if (thePrefs.GetDebugServerUDPLevel() > 0 && thePrefs.GetDebugServerSourcesLevel() > 0)
				Debug(_T("Finished UDP search processing for all servers (%u)\n"), theApp.serverlist->GetServerCount());
			StopUDPRequests();
			return false; // finished (processed all file & all servers)
		}
		m_iSearchedServers++;
		lastfile = NULL;
	}

	return true;
}

void CDownloadQueue::StopUDPRequests()
{
	cur_udpserver = NULL;
	lastudpsearchtime = ::GetTickCount();
	// NEO: NST - [NeoSourceTweaks] -- Xanatos -->
	for( POSITION pos1 = filelist.GetHeadPosition(); pos1 != NULL; )
		filelist.GetNext(pos1)->m_LastSearchTimeUdp = ::GetTickCount();
	// NEO: NST END <-- Xanatos --
	lastfile = NULL;
	m_iSearchedServers = 0;
}

bool CDownloadQueue::CompareParts(POSITION pos1, POSITION pos2){
	CPartFile* file1 = filelist.GetAt(pos1);
	CPartFile* file2 = filelist.GetAt(pos2);
    return CPartFile::RightFileHasHigherPrio(file1, file2);
}

void CDownloadQueue::SwapParts(POSITION pos1, POSITION pos2){
	CPartFile* file1 = filelist.GetAt(pos1);
	CPartFile* file2 = filelist.GetAt(pos2);
	filelist.SetAt(pos1, file2);
	filelist.SetAt(pos2, file1);
}

void CDownloadQueue::HeapSort(UINT first, UINT last)
{
	UINT r;
	POSITION pos1 = filelist.FindIndex(first);
	for ( r = first; !(r & (UINT)INT_MIN) && (r<<1) < last; ){
		UINT r2 = (r<<1)+1;
		POSITION pos2 = filelist.FindIndex(r2);
		if (r2 != last){
			POSITION pos3 = pos2;
			filelist.GetNext(pos3);
			if (!CompareParts(pos2, pos3)){
				pos2 = pos3;
				r2++;
			}
		}
		if (!CompareParts(pos1, pos2)) {
			SwapParts(pos1, pos2);
			r = r2;
			pos1 = pos2;
		}
		else
			break;
	}
}

void CDownloadQueue::SortByPriority()
{
	UINT n = filelist.GetCount();
	if (!n)
		return;
	UINT i;
	for ( i = n/2; i--; )
		HeapSort(i, n-1);
	for ( i = n; --i; ){
		SwapParts(filelist.FindIndex(0), filelist.FindIndex(i));
		HeapSort(0, i-1);
	}
}

void CDownloadQueue::CheckDiskspaceTimed()
{
	if ((!lastcheckdiskspacetime) || (::GetTickCount() - lastcheckdiskspacetime) > DISKSPACERECHECKTIME)
		CheckDiskspace();
}

void CDownloadQueue::CheckDiskspace(bool bNotEnoughSpaceLeft)
{
	lastcheckdiskspacetime = ::GetTickCount();

	// sorting the list could be done here, but I prefer to "see" that function call in the calling functions.
	//SortByPriority();

	// If disabled, resume any previously paused files
	if (!thePrefs.IsCheckDiskspaceEnabled())
	{
		if (!bNotEnoughSpaceLeft) // avoid worse case, if we already had 'disk full'
		{
			for( POSITION pos1 = filelist.GetHeadPosition(); pos1 != NULL; )
			{
				CPartFile* cur_file = filelist.GetNext(pos1);
				switch(cur_file->GetStatus())
				{
				case PS_PAUSED:
				case PS_ERROR:
				case PS_COMPLETING:
				case PS_COMPLETE:
				case PS_MOVING: // NEO: MTD - [MultiTempDirectories] <-- Xanatos --
					continue;
				}
				cur_file->ResumeFileInsufficient();
			}
		}
		return;
	}

	//uint64 nTotalAvailableSpaceMain = bNotEnoughSpaceLeft ? 0 : GetFreeDiskSpaceX(thePrefs.GetTempDir()); // NEO: MTD - [MultiTempDirectories] <-- Xanatos --

	// 'bNotEnoughSpaceLeft' - avoid worse case, if we already had 'disk full'
	if (thePrefs.GetMinFreeDiskSpace() == 0)
	{
		for( POSITION pos1 = filelist.GetHeadPosition(); pos1 != NULL; )
		{
			CPartFile* cur_file = filelist.GetNext(pos1);

			uint64 nTotalAvailableSpace = bNotEnoughSpaceLeft ? 0 : 
				/*((thePrefs.GetTempDirCount()==1)?nTotalAvailableSpaceMain:*/GetFreeDiskSpaceX(cur_file->GetTempPath())/*)*/; // NEO: MTD - [MultiTempDirectories] <-- Xanatos --

			switch(cur_file->GetStatus())
			{
			case PS_PAUSED:
			case PS_ERROR:
			case PS_COMPLETING:
			case PS_COMPLETE:
			case PS_MOVING: // NEO: MTD - [MultiTempDirectories] <-- Xanatos --
				continue;
			}

			// Pause the file only if it would grow in size and would exceed the currently available free space
			uint64 nSpaceToGo = cur_file->GetNeededSpace();
			if (nSpaceToGo <= nTotalAvailableSpace)
			{
				nTotalAvailableSpace -= nSpaceToGo;
				cur_file->ResumeFileInsufficient();
			}
			else
				cur_file->PauseFile(true/*bInsufficient*/);
		}
	}
	else
	{
		for( POSITION pos1 = filelist.GetHeadPosition(); pos1 != NULL; )
		{
			CPartFile* cur_file = filelist.GetNext(pos1);
			switch(cur_file->GetStatus())
			{
			case PS_PAUSED:
			case PS_ERROR:
			case PS_COMPLETING:
			case PS_COMPLETE:
			case PS_MOVING: // NEO: MTD - [MultiTempDirectories] <-- Xanatos --
				continue;
			}

			uint64 nTotalAvailableSpace = bNotEnoughSpaceLeft ? 0 : 
				/*((thePrefs.GetTempDirCount()==1)?nTotalAvailableSpaceMain:*/GetFreeDiskSpaceX(cur_file->GetTempPath())/*)*/; // NEO: MTD - [MultiTempDirectories] <-- Xanatos --
			if (nTotalAvailableSpace < thePrefs.GetMinFreeDiskSpace())
			{
				if (cur_file->IsNormalFile())
				{
					// Normal files: pause the file only if it would still grow
					uint64 nSpaceToGrow = cur_file->GetNeededSpace();
					if (nSpaceToGrow > 0)
						cur_file->PauseFile(true/*bInsufficient*/);
				}
				else
				{
					// Compressed/sparse files: always pause the file
					cur_file->PauseFile(true/*bInsufficient*/);
				}
			}
			else
			{
				// doesn't work this way. resuming the file without checking if there is a chance to successfully
				// flush any available buffered file data will pause the file right after it was resumed and disturb
				// the StopPausedFile function.
				//cur_file->ResumeFileInsufficient();
			}
		}
	}
}

void CDownloadQueue::GetDownloadStats(SDownloadStats& results)
{
	memset(&results, 0, sizeof results);
	for (POSITION pos = filelist.GetHeadPosition(); pos != 0; )
	{
		const CPartFile* cur_file = filelist.GetNext(pos);

		results.a[0]  += cur_file->GetSourceCount();
		results.a[1]  += cur_file->GetTransferringSrcCount();
		results.a[2]  += cur_file->GetSrcStatisticsValue(DS_ONQUEUE);
		results.a[3]  += cur_file->GetSrcStatisticsValue(DS_REMOTEQUEUEFULL);
		results.a[4]  += cur_file->GetSrcStatisticsValue(DS_NONEEDEDPARTS);
		results.a[5]  += cur_file->GetSrcStatisticsValue(DS_CONNECTED);
		results.a[6]  += cur_file->GetSrcStatisticsValue(DS_REQHASHSET);
		results.a[7]  += cur_file->GetSrcStatisticsValue(DS_CONNECTING);
		results.a[7]  += cur_file->GetSrcStatisticsValue(DS_HALTED); // NEO: SD - [StandByDL] <-- Xanatos --
		results.a[8]  += cur_file->GetSrcStatisticsValue(DS_WAITCALLBACK);
		results.a[8]  += cur_file->GetSrcStatisticsValue(DS_WAITCALLBACKKAD);
		results.a[9]  += cur_file->GetSrcStatisticsValue(DS_TOOMANYCONNS);
		results.a[9]  += cur_file->GetSrcStatisticsValue(DS_TOOMANYCONNSKAD);
		results.a[10] += cur_file->GetSrcStatisticsValue(DS_LOWTOLOWIP);
		results.a[11] += cur_file->GetSrcStatisticsValue(DS_NONE);
		results.a[12] += cur_file->GetSrcStatisticsValue(DS_ERROR);
		results.a[13] += cur_file->GetSrcStatisticsValue(DS_BANNED);
		results.a[14] += cur_file->src_stats[3];
		results.a[15] += cur_file->GetSrcA4AFCount();
		results.a[16] += cur_file->src_stats[0];
		results.a[17] += cur_file->src_stats[1];
		results.a[18] += cur_file->src_stats[2];
		results.a[19] += cur_file->net_stats[0];
		results.a[20] += cur_file->net_stats[1];
		results.a[21] += cur_file->net_stats[2];
		results.a[22] += cur_file->m_DeadSourceList.GetDeadSourcesCount();
#ifdef NEO_SS // NEO: NSS - [NeoSourceStorage] -- Xanatos -->
		results.a[23] += cur_file->src_stats[4]; 
		results.a[24] += cur_file->GetSrcStatisticsValue(DS_LOADEDWAITING);
		results.a[25] += cur_file->GetSrcStatisticsValue(DS_LOADED);
		results.a[26] += cur_file->GetSrcStatisticsValue(DS_RESERVE);
		results.a[27] += cur_file->GetSrcStatisticsValue(DS_RETIRED);
#endif // NEO_SS // NEO: NSS END <-- Xanatos --
#if defined(NEO_SK) || defined(NEO_SS) // NEO: NSK - [NeoSourceKeeper] // NEO: NSS - [NeoSourceStorage] -- Xanatos -->
		results.a[28] += cur_file->GetSrcStatisticsValue(DS_REVIVAL);
#endif // NEO_SK // NEO_SS // NEO: NSK END // NEO: NSS END <-- Xanatos --
#ifdef NEO_SK // NEO: NSK - [NeoSourceKeeper] -- Xanatos -->
		results.a[29] += cur_file->GetSrcStatisticsValue(DS_UNREACHABLE);
#endif // NEO_SK // NEO: NSK END <-- Xanatos --
		results.a[30] += cur_file->GetSrcStatisticsValue(DS_CONNECTIONRETRY); // NEO: TCR - [TCPConnectionRetry] <-- Xanatos --
		results.a[31] += cur_file->GetSrcStatisticsValue(DS_CONNECTINGWAITING); // NEO: MDR - [ManualDownloadReask] <-- Xanatos --
#ifdef LANCAST // NEO: NLC - [NeoLanCast] -- Xanatos -->
		results.a[32] += cur_file->src_stats[5];
#endif //LANCAST // NEO: NLC END <-- Xanatos --
#ifdef VOODOO // NEO: VOODOOx - [VoodooSourceExchange] -- Xanatos -->
		results.a[33] += cur_file->src_stats[6];
#endif // VOODOO // NEO: VOODOOx END <-- Xanatos --
		results.a[34] += cur_file->GetSrcStatisticsValue(DS_CACHED); // NEO: XSC - [ExtremeSourceCache] <-- Xanatos --
		// NEO: DS - [DropSources] -- Xanatos -->
		results.a[35] += cur_file->GetHighQSourceCount(); 
#ifdef NEO_SS // NEO: NSD - [NeoSourceDrop]
		results.a[36] += cur_file->GetOutOfDateSourceCount(); 
#endif // NEO_SS // NEO: NSD END
		// NEO: DS END <-- Xanatos --
	}
}

CUpDownClient* CDownloadQueue::GetDownloadClientByIP(uint32 dwIP){
	for (POSITION pos = filelist.GetHeadPosition();pos != 0;){
		CPartFile* cur_file = filelist.GetNext(pos);
		for (POSITION pos2 = cur_file->srclist.GetHeadPosition();pos2 != 0; ){
			CUpDownClient* cur_client = cur_file->srclist.GetNext(pos2);
			if (dwIP == cur_client->GetIP()){
				return cur_client;
			}
		}
	}
	return NULL;
}

CUpDownClient* CDownloadQueue::GetDownloadClientByIP_UDP(uint32 dwIP, uint16 nUDPPort, bool bIgnorePortOnUniqueIP, bool* pbMultipleIPs){
	CUpDownClient* pMatchingIPClient = NULL;
	uint32 cMatches = 0;

	for (POSITION pos = filelist.GetHeadPosition();pos != 0;){
		CPartFile* cur_file = filelist.GetNext(pos);
		for (POSITION pos2 = cur_file->srclist.GetHeadPosition();pos2 != 0;){
			CUpDownClient* cur_client = cur_file->srclist.GetNext(pos2);
			if (dwIP == cur_client->GetIP() && nUDPPort == cur_client->GetUDPPort()){
				return cur_client;
			}
			else if (dwIP == cur_client->GetIP() && bIgnorePortOnUniqueIP && cur_client != pMatchingIPClient){
				pMatchingIPClient = cur_client;
				cMatches++;
			}
		}
	}
	if (pbMultipleIPs != NULL)
		*pbMultipleIPs = cMatches > 1;

	if (pMatchingIPClient != NULL && cMatches == 1)
		return pMatchingIPClient;
	else
		return NULL;
}

bool CDownloadQueue::IsInList(const CUpDownClient* client) const
{
	for (POSITION pos = filelist.GetHeadPosition();pos != 0;){
		CPartFile* cur_file = filelist.GetNext(pos);
		for (POSITION pos2 = cur_file->srclist.GetHeadPosition();pos2 != 0;){
			if (cur_file->srclist.GetNext(pos2) == client)
				return true;
		}
	}
	return false;
}

#ifdef A4AF_CATS // NEO: MAC - [MorphA4AFCategories] -- Xanatos -->
// khaos::categorymod+ We need to reset the resume order, too, so that these files don't
// screw up the order of 'All' category.  This function is modified.
void CDownloadQueue::ResetCatParts(int cat, int useCat)
#else
void CDownloadQueue::ResetCatParts(int cat)
#endif // A4AF_CATS // NEO: MAC END <-- Xanatos --
{
	CPartFile* cur_file;
#ifdef A4AF_CATS // NEO: MAC - [MorphA4AFCategories] -- Xanatos -->
	int useOrder = GetMaxCatResumeOrder(useCat);
#endif // A4AF_CATS // NEO: MAC END <-- Xanatos --
	for (POSITION pos = filelist.GetHeadPosition(); pos != 0; ){
		cur_file = filelist.GetNext(pos);

		if (cur_file->GetCategory()==cat){
#ifdef A4AF_CATS // NEO: MAC - [MorphA4AFCategories] -- Xanatos -->
			useOrder++;
			cur_file->SetCategory(useCat);
			cur_file->SetCatResumeOrder(useOrder);
#else
			cur_file->SetCategory(0);
#endif // A4AF_CATS // NEO: MAC END <-- Xanatos --
		}else if (cur_file->GetCategory() > cat)
			cur_file->SetCategory(cur_file->GetCategory() - 1);
	}
}

void CDownloadQueue::SetCatPrio(int cat, uint8 newprio)
{
	for (POSITION pos = filelist.GetHeadPosition(); pos != 0; ){
		CPartFile* cur_file = filelist.GetNext(pos);
		if (cat==0 || cur_file->GetCategory()==cat)
			if (newprio==PR_AUTO) {
				cur_file->SetAutoDownPriority(true);
				cur_file->SetDownPriority(PR_HIGH, false);
			}
			else {
				cur_file->SetAutoDownPriority(false);
				cur_file->SetDownPriority(newprio, false);
			}
	}

    theApp.downloadqueue->SortByPriority();
	theApp.downloadqueue->CheckDiskspaceTimed();
}

// ZZ:DownloadManager -->
void CDownloadQueue::RemoveAutoPrioInCat(int cat, uint8 newprio){
	CPartFile* cur_file;
	for (POSITION pos = filelist.GetHeadPosition();pos != 0;filelist.GetNext(pos)){
		cur_file = filelist.GetAt(pos);
        if (cur_file->IsAutoDownPriority() && (cat==0 || cur_file->GetCategory()==cat)) {
			cur_file->SetAutoDownPriority(false);
			cur_file->SetDownPriority(newprio, false);
		}
	}

    theApp.downloadqueue->SortByPriority();
	theApp.downloadqueue->CheckDiskspaceTimed();
}
// <-- ZZ:DownloadManager

void CDownloadQueue::SetCatStatus(int cat, int newstatus)
{
	bool reset = false;
    bool resort = false;

	POSITION pos= filelist.GetHeadPosition();
	while (pos != 0)
	{
		CPartFile* cur_file = filelist.GetAt(pos);
		if (!cur_file)
			continue;

		if (cat==-1 || 
			(cat==-2 && cur_file->GetCategory()==0) ||
			(cat==0 && cur_file->CheckShowItemInGivenCat(cat)) || 
			(cat>0 && cat==cur_file->GetCategory()))
		{
			switch (newstatus){
				case MP_CANCEL:
					cur_file->DeleteFile();
					reset = true;
					break;
				case MP_PAUSE:
					cur_file->PauseFile(false, false);
                    resort = true;
					break;
				case MP_STOP:
					cur_file->StopFile(false, false);
                    resort = true;
					break;
				case MP_RESUME: 
					if (cur_file->CanResumeFile()){
						if (cur_file->GetStatus() == PS_INSUFFICIENT)
							cur_file->ResumeFileInsufficient();
                        else {
							cur_file->ResumeFile(false);
                            resort = true;
                        }
					}
					break;
			}
		}
		filelist.GetNext(pos);
		if (reset)
		{
			reset = false;
			pos = filelist.GetHeadPosition();
		}
	}

    if(resort) {
	    theApp.downloadqueue->SortByPriority();
	    theApp.downloadqueue->CheckDiskspace();
    }
}

void CDownloadQueue::MoveCat(int from, int to)
{
	if (from < to)
		--to;

	POSITION pos= filelist.GetHeadPosition();
	while (pos != 0)
	{
		CPartFile* cur_file = filelist.GetAt(pos);
		if (!cur_file)
			continue;

		int mycat = cur_file->GetCategory();
		if ((mycat>=min(from,to) && mycat<=max(from,to)))
		{
			//if ((from<to && (mycat<from || mycat>to)) || (from>to && (mycat>from || mycat<to)) )	continue; //not affected

			if (mycat == from)
				cur_file->SetCategory(to);
			else{
				if (from < to)
					cur_file->SetCategory(mycat - 1);
				else
					cur_file->SetCategory(mycat + 1);
			}
		}
		filelist.GetNext(pos);
	}

	theApp.emuledlg->sharedfileswnd->Reload(true); // NEO: NSC - [NeoSharedCategories] <-- Xanatos --
}

UINT CDownloadQueue::GetDownloadingFileCount() const
{
	UINT result = 0;
	for (POSITION pos = filelist.GetHeadPosition();pos != 0;){
		UINT uStatus = filelist.GetNext(pos)->GetStatus();
		if (uStatus == PS_READY || uStatus == PS_EMPTY)
			result++;
	}
	return result;
}

UINT CDownloadQueue::GetPausedFileCount() const
{
	UINT result = 0;
	for (POSITION pos = filelist.GetHeadPosition();pos != 0;){
		if (filelist.GetNext(pos)->GetStatus() == PS_PAUSED)
			result++;
	}
	return result;
}

#ifndef A4AF_CATS // NEO: MAC - [MorphA4AFCategories] -- Xanatos -->
void CDownloadQueue::SetAutoCat(CPartFile* newfile){
	if(thePrefs.GetCatCount()==1)
		return;
	CString catExt;

	for (int ix=1;ix<thePrefs.GetCatCount();ix++){	
		catExt= thePrefs.GetCategory(ix)->autocat;
		if (catExt.IsEmpty())
			continue;

		if (!thePrefs.GetCategory(ix)->ac_regexpeval) {
			// simple string comparison

			int curPos = 0;
			catExt.MakeLower();

			CString fullname = newfile->GetFileName();
			fullname.MakeLower();
			CString cmpExt = catExt.Tokenize(_T("|"), curPos);

			while (!cmpExt.IsEmpty()) {
				// HoaX_69: Allow wildcards in autocat string
				//  thanks to: bluecow, khaos and SlugFiller
				if(cmpExt.Find(_T("*")) != -1 || cmpExt.Find(_T("?")) != -1){
					// Use wildcards
					if(PathMatchSpec(fullname, cmpExt)){
						newfile->SetCategory(ix);
						return;
					}
				}else{
					if(fullname.Find(cmpExt) != -1){
						newfile->SetCategory(ix);
						return;
					}
				}
				cmpExt = catExt.Tokenize(_T("|"),curPos);
			}
		} else {
			// regular expression evaluation
			if (RegularExpressionMatch(catExt,newfile->GetFileName()))
				newfile->SetCategory(ix);
		}
	}
}
#endif // A4AF_CATS // NEO: MAC END <-- Xanatos --

void CDownloadQueue::ResetLocalServerRequests()
{
	m_dwNextTCPSrcReq = 0;
	m_localServerReqQueue.RemoveAll();

	POSITION pos = filelist.GetHeadPosition();
	while (pos != NULL)
	{ 
		CPartFile* pFile = filelist.GetNext(pos);
		// NEO: POFC - [PauseOnFileComplete] -- Xanatos -->
		if(pFile->GetCompletionBreak()) 
			continue; 
		// NEO: POFC END <-- Xanatos --
		UINT uState = pFile->GetStatus();
		if (uState == PS_READY || uState == PS_EMPTY)
			pFile->ResumeFile(false); // NEO: MOD - [SpeedImprovement] <-- Xanatos --
		pFile->m_bLocalSrcReqQueued = false;
	}
	theApp.downloadqueue->SortByPriority(); // NEO: MOD - [SpeedImprovement] <-- Xanatos --
	//theApp.downloadqueue->CheckDiskspace()
}

void CDownloadQueue::RemoveLocalServerRequest(CPartFile* pFile)
{
	POSITION pos1, pos2;
	for( pos1 = m_localServerReqQueue.GetHeadPosition(); ( pos2 = pos1 ) != NULL; )
	{
		m_localServerReqQueue.GetNext(pos1);
		if (m_localServerReqQueue.GetAt(pos2) == pFile)
		{
			m_localServerReqQueue.RemoveAt(pos2);
			pFile->m_bLocalSrcReqQueued = false;
			// could 'break' here.. fail safe: go through entire list..
		}
	}
}

void CDownloadQueue::ProcessLocalRequests()
{
	if ( (!m_localServerReqQueue.IsEmpty()) && (m_dwNextTCPSrcReq < ::GetTickCount()) )
	{
		CSafeMemFile dataTcpFrame(22);
		const int iMaxFilesPerTcpFrame = theApp.serverconnect->GetCurrentServer() ? theApp.serverconnect->GetCurrentServer()->GetMaxAsksFromCredits(15) : 15; // NEO: KLC - [KhaosLugdunumCredits] <-- Xanatos --
		int iFiles = 0;
		while (!m_localServerReqQueue.IsEmpty() && iFiles < iMaxFilesPerTcpFrame)
		{
			// find the file with the longest waitingtime
			POSITION pos1, pos2;
			uint32 dwBestWaitTime = 0xFFFFFFFF;
			POSITION posNextRequest = NULL;
			CPartFile* cur_file;
			for( pos1 = m_localServerReqQueue.GetHeadPosition(); ( pos2 = pos1 ) != NULL; ){
				m_localServerReqQueue.GetNext(pos1);
				cur_file = m_localServerReqQueue.GetAt(pos2);
				if (cur_file->GetStatus() == PS_READY || cur_file->GetStatus() == PS_EMPTY)
				{
					uint8 nPriority = cur_file->GetDownPriority();
					if (nPriority > PR_HIGH){
						ASSERT(0);
						nPriority = PR_HIGH;
					}

					if (cur_file->m_LastSearchTime + (PR_HIGH-nPriority) < dwBestWaitTime ){
						dwBestWaitTime = cur_file->m_LastSearchTime + (PR_HIGH-nPriority);
						posNextRequest = pos2;
					}
				}
				else{
					m_localServerReqQueue.RemoveAt(pos2);
					cur_file->m_bLocalSrcReqQueued = false;
					if (thePrefs.GetDebugSourceExchange())
						AddDebugLogLine(false, _T("SXSend: Local server source request for file \"%s\" not sent because of status '%s'"), cur_file->GetFileName(), cur_file->getPartfileStatus());
				}
			}
			
			if (posNextRequest != NULL)
			{
				cur_file = m_localServerReqQueue.GetAt(posNextRequest);
				cur_file->m_bLocalSrcReqQueued = false;
				cur_file->m_LastSearchTime = ::GetTickCount();
				m_localServerReqQueue.RemoveAt(posNextRequest);

				if (cur_file->IsLargeFile() && (theApp.serverconnect->GetCurrentServer() == NULL || !theApp.serverconnect->GetCurrentServer()->SupportsLargeFilesTCP())){
					ASSERT( false );
					DebugLogError(_T("Large file (%s) on local requestqueue for server without support for large files"), cur_file->GetFileName());
					continue;
				}

				iFiles++;
				
				// create request packet
				CSafeMemFile smPacket;
				smPacket.WriteHash16(cur_file->GetFileHash());
				if (!cur_file->IsLargeFile()){
					smPacket.WriteUInt32((uint32)(uint64)cur_file->GetFileSize());
				}
				else{
					smPacket.WriteUInt32(0); // indicates that this is a large file and a uint64 follows
					smPacket.WriteUInt64(cur_file->GetFileSize());
				}

				uint8 byOpcode = 0;
				if (thePrefs.IsClientCryptLayerSupported() && theApp.serverconnect->GetCurrentServer() != NULL && theApp.serverconnect->GetCurrentServer()->SupportsGetSourcesObfuscation())
					byOpcode = OP_GETSOURCES_OBFU;
				else
					byOpcode = OP_GETSOURCES;
	
				Packet* packet = new Packet(&smPacket, OP_EDONKEYPROT, byOpcode);
				if (thePrefs.GetDebugServerTCPLevel() > 0)
					Debug(_T(">>> Sending OP__GetSources%s(%2u/%2u); %s\n"), (byOpcode == OP_GETSOURCES) ? _T("") : _T("_OBFU"), iFiles, iMaxFilesPerTcpFrame, DbgGetFileInfo(cur_file->GetFileHash()));
				dataTcpFrame.Write(packet->GetPacket(), packet->GetRealPacketSize());
				delete packet;

				if (thePrefs.GetDebugSourceExchange())
					AddDebugLogLine(false, _T("SXSend: Local server source request; File=\"%s\""), cur_file->GetFileName());
			}
		}

		int iSize = (int)dataTcpFrame.GetLength();
		if (iSize > 0)
		{
			// create one 'packet' which contains all buffered OP_GETSOURCES eD2K packets to be sent with one TCP frame
			// server credits: 16*iMaxFilesPerTcpFrame+1 = 241
			Packet* packet = new Packet(new char[iSize], (UINT)dataTcpFrame.GetLength(), true, false);
			dataTcpFrame.Seek(0, CFile::begin);
			dataTcpFrame.Read(packet->GetPacket(), iSize);
			theStats.AddUpDataOverheadServer(packet->size);
			theApp.serverconnect->SendPacket(packet, true);
			// NEO: KLC - [KhaosLugdunumCredits] -- Xanatos -->
			if(theApp.serverconnect->GetCurrentServer() != NULL)
				theApp.serverconnect->GetCurrentServer()->FileAsksSent(iFiles); 
			// NEO: KLC END <-- Xanatos --
		}

		// next TCP frame with up to 15 source requests is allowed to be sent in..
		m_dwNextTCPSrcReq = ::GetTickCount() + SEC2MS(iMaxFilesPerTcpFrame*(16+4));
	}
}

void CDownloadQueue::SendLocalSrcRequest(CPartFile* sender){
	ASSERT ( !m_localServerReqQueue.Find(sender) );
	m_localServerReqQueue.AddTail(sender);
}

void CDownloadQueue::GetDownloadStats(int results[],
									  uint64& rui64TotFileSize,
									  uint64& rui64TotBytesLeftToTransfer,
									  uint64& rui64TotNeededSpace)
{
	results[0] = 0;
	results[1] = 0;
	results[2] = 0;
	for (POSITION pos = filelist.GetHeadPosition();pos != 0; )
	{
		const CPartFile* cur_file = filelist.GetNext(pos);
		UINT uState = cur_file->GetStatus();
		if (uState == PS_READY || uState == PS_EMPTY)
		{
			uint64 ui64SizeToTransfer = 0;
			uint64 ui64NeededSpace = 0;
			cur_file->GetSizeToTransferAndNeededSpace(ui64SizeToTransfer, ui64NeededSpace);
			rui64TotFileSize += (uint64)cur_file->GetFileSize();
			rui64TotBytesLeftToTransfer += ui64SizeToTransfer;
			rui64TotNeededSpace += ui64NeededSpace;
			results[2]++;
		}
		results[0] += cur_file->GetSourceCount();
		results[1] += cur_file->GetTransferringSrcCount();
	}
}

///////////////////////////////////////////////////////////////////////////////
// CSourceHostnameResolveWnd

#define WM_HOSTNAMERESOLVED		(WM_USER + 0x101)	// does not need to be placed in "UserMsgs.h"

BEGIN_MESSAGE_MAP(CSourceHostnameResolveWnd, CWnd)
	ON_MESSAGE(WM_HOSTNAMERESOLVED, OnHostnameResolved)
END_MESSAGE_MAP()

CSourceHostnameResolveWnd::CSourceHostnameResolveWnd()
{
}

CSourceHostnameResolveWnd::~CSourceHostnameResolveWnd()
{
	while (!m_toresolve.IsEmpty())
		delete m_toresolve.RemoveHead();
}

void CSourceHostnameResolveWnd::AddToResolve(const uchar* fileid, LPCSTR pszHostname, uint16 port, LPCTSTR pszURL)
{
	bool bResolving = !m_toresolve.IsEmpty();

	// double checking
	if (!theApp.downloadqueue->GetFileByID(fileid))
		return;

	Hostname_Entry* entry = new Hostname_Entry;
	md4cpy(entry->fileid, fileid);
	entry->strHostname = pszHostname;
	entry->port = port;
	entry->strURL = pszURL;
	m_toresolve.AddTail(entry);

	if (bResolving)
		return;

	memset(m_aucHostnameBuffer, 0, sizeof(m_aucHostnameBuffer));
	if (WSAAsyncGetHostByName(m_hWnd, WM_HOSTNAMERESOLVED, entry->strHostname, m_aucHostnameBuffer, sizeof m_aucHostnameBuffer) != 0)
		return;
	m_toresolve.RemoveHead();
	delete entry;
}

LRESULT CSourceHostnameResolveWnd::OnHostnameResolved(WPARAM /*wParam*/, LPARAM lParam)
{
	if (m_toresolve.IsEmpty())
		return TRUE;
	Hostname_Entry* resolved = m_toresolve.RemoveHead();
	if (WSAGETASYNCERROR(lParam) == 0)
	{
		int iBufLen = WSAGETASYNCBUFLEN(lParam);
		if (iBufLen >= sizeof(HOSTENT))
		{
			LPHOSTENT pHost = (LPHOSTENT)m_aucHostnameBuffer;
			if (pHost->h_length == 4 && pHost->h_addr_list && pHost->h_addr_list[0])
			{
				uint32 nIP = ((LPIN_ADDR)(pHost->h_addr_list[0]))->s_addr;

				CPartFile* file = theApp.downloadqueue->GetFileByID(resolved->fileid);
				if (file)
				{
					if (resolved->strURL.IsEmpty())
					{
					    CSafeMemFile sources(1+4+2);
					    sources.WriteUInt8(1);
					    sources.WriteUInt32(nIP);
					    sources.WriteUInt16(resolved->port);
					    sources.SeekToBegin();
					    file->AddSources(&sources,0,0, false);
				    }
					else
					{
						file->AddSource(resolved->strURL, nIP);
					}
				}
			}
		}
	}
	delete resolved;

	while (!m_toresolve.IsEmpty())
	{
		Hostname_Entry* entry = m_toresolve.GetHead();
		memset(m_aucHostnameBuffer, 0, sizeof(m_aucHostnameBuffer));
		if (WSAAsyncGetHostByName(m_hWnd, WM_HOSTNAMERESOLVED, entry->strHostname, m_aucHostnameBuffer, sizeof m_aucHostnameBuffer) != 0)
			return TRUE;
		m_toresolve.RemoveHead();
		delete entry;
	}
	return TRUE;
}

bool CDownloadQueue::DoKademliaFileRequest()
{
	return ((::GetTickCount() - lastkademliafilerequest) > KADEMLIAASKTIME);
}

void CDownloadQueue::KademliaSearchFile(CPartFile* temp, const Kademlia::CUInt128* pcontactID, const Kademlia::CUInt128* pbuddyID, uint8 type, uint32 ip, uint16 tcp, uint16 udp, uint32 serverip, uint16 serverport, uint8 byCryptOptions) // NEO: ASP - [ActiveSpreading]
{
	//Safty measure to make sure we are looking for these sources
	//CPartFile* temp = GetFileByKadFileSearchID(searchID);
	//if( !temp )
	//	return;

	//Do we need more sources?
	bool bCache = false; // NEO: XSC - [ExtremeSourceCache] <-- Xanatos --
	if(temp->IsStopped() || (
		(bCache = !((UINT)temp->PartPrefs.GetHardLimit() > temp->GetSourceCount())) == false // NEO: NST - [NeoSourceTweaks] <-- Xanatos --
	 && !(temp->PartPrefs.UseSourceCache() && (UINT)temp->PartPrefs.GetSourceCacheLimit() > temp->GetCachedSourceCount())) // NEO: XSC - [ExtremeSourceCache] <-- Xanatos --
	 )
		return;

	uint32 ED2Kip = ntohl(ip);
	if (theApp.ipfilter->IsFiltered(ED2Kip))
	{
		if (thePrefs.GetLogFilteredIPs())
			AddDebugLogLine(false, _T("IPfiltered source IP=%s (%s) received from Kademlia"), ipstr(ED2Kip), theApp.ipfilter->GetLastHit());
		return;
	}
	if( (ip == Kademlia::CKademlia::GetIPAddress() || ED2Kip == theApp.serverconnect->GetClientID()) && tcp == thePrefs.GetPort())
		return;

	CUpDownClient* ctemp = NULL; 
	switch( type )
	{
		case 4:
		case 1:
		{
			//NonFirewalled users
			if(!tcp)
			{
				if (thePrefs.GetVerbose())
					AddDebugLogLine(false, _T("Ignored source (IP=%s) received from Kademlia, no tcp port received"), ipstr(ip));
				return;
			}
			ctemp = new CUpDownClient(temp,tcp,ip,0,0,false);
			ctemp->SetSourceFrom(SF_KADEMLIA);
			ctemp->SetServerIP(serverip);
			ctemp->SetServerPort(serverport);
			ctemp->SetKadPort(udp);
			byte cID[16];
			pcontactID->ToByteArray(cID);
			ctemp->SetUserHash(cID);
			break;
		}
		case 2:
		{
			//Don't use this type... Some clients will process it wrong..
			break;
		}
		case 5:
		case 3:
		{
			//This will be a firewaled client connected to Kad only.
			//We set the clientID to 1 as a Kad user only has 1 buddy.
			ctemp = new CUpDownClient(temp,tcp,1,0,0,false);
			//The only reason we set the real IP is for when we get a callback
			//from this firewalled source, the compare method will match them.
			ctemp->SetSourceFrom(SF_KADEMLIA);
			ctemp->SetKadPort(udp);
			byte cID[16];
			pcontactID->ToByteArray(cID);
			ctemp->SetUserHash(cID);
			pbuddyID->ToByteArray(cID);
			ctemp->SetBuddyID(cID);
			ctemp->SetBuddyIP(serverip);
			ctemp->SetBuddyPort(serverport);
#ifdef NATTUNNELING // NEO: NATT - [NatTraversal] -- Xanatos -->
			if(tcp == (uint16)-1)
			{
				ctemp->SetConnectIP(ip);
				ctemp->SetNatTraversalSupport();
			}
#endif //NATTUNNELING // NEO: NATT END <-- Xanatos --
			break;
		}
	}

	if (ctemp != NULL){
		// add encryption settings
		ctemp->SetCryptLayerSupport((byCryptOptions & 0x01) != 0);
		ctemp->SetCryptLayerRequest((byCryptOptions & 0x02) != 0);
		ctemp->SetCryptLayerRequires((byCryptOptions & 0x04) != 0);

		// NEO: XSC - [ExtremeSourceCache] -- Xanatos -->
		if(bCache)
			ctemp->SetDownloadState(DS_CACHED);
		if(CheckAndAddSource(temp, ctemp) && bCache)
			temp->m_anStates[DS_CACHED]++;
		// NEO: XSC END <-- Xanatos --
	}
}

void CDownloadQueue::ExportPartMetFilesOverview() const
{
	CString strFileListPath = thePrefs.GetAppDir() + _T("downloads.txt");
	
	CString strTmpFileListPath = strFileListPath;
	PathRenameExtension(strTmpFileListPath.GetBuffer(MAX_PATH), _T(".tmp"));
	strTmpFileListPath.ReleaseBuffer();

	CSafeBufferedFile file;
	CFileException fexp;
	if (!file.Open(strTmpFileListPath, CFile::modeCreate | CFile::modeWrite | CFile::typeBinary | CFile::shareDenyWrite, &fexp))
	{
		CString strError;
		TCHAR szError[MAX_CFEXP_ERRORMSG];
		if (fexp.GetErrorMessage(szError, ARRSIZE(szError))){
			strError += _T(" - ");
			strError += szError;
		}
		LogError(_T("Failed to create part.met file list%s"), strError);
		return;
	}

	// write Unicode byte-order mark 0xFEFF
	fputwc(0xFEFF, file.m_pStream);

	try
	{
		file.printf(_T("Date:      %s\r\n"), CTime::GetCurrentTime().Format(_T("%c")));
		//if (thePrefs.GetTempDirCount()==1)
		file.printf(_T("Directory: %s\r\n"), thePrefs.GetTempDir());
		// NEO: MTD - [MultiTempDirectories] -- Xanatos -->
		for (POSITION pos = thePrefs.tempdir_list.GetHeadPosition();pos != 0;thePrefs.tempdir_list.GetNext(pos))
			file.printf(_T("Directory: %s\n"), thePrefs.tempdir_list.GetAt(pos));
		// NEO: MTD END <-- Xanatos --
		file.printf(_T("\r\n"));
		file.printf(_T("Part file\teD2K link\r\n"));
		file.printf(_T("--------------------------------------------------------------------------------\r\n"));
		for (POSITION pos = filelist.GetHeadPosition(); pos != 0; )
		{
			const CPartFile* pPartFile = filelist.GetNext(pos);
			if (pPartFile->GetStatus(true) != PS_COMPLETE)
			{
				CString strPartFilePath(pPartFile->GetFilePath());
				TCHAR szNam[_MAX_FNAME];
				TCHAR szExt[_MAX_EXT];
				_tsplitpath(strPartFilePath, NULL, NULL, szNam, szExt);
				// NEO: MTD - [MultiTempDirectories] -- Xanatos --
				/*if (thePrefs.GetTempDirCount()==1)
					file.printf(_T("%s%s\t%s\r\n"), szNam, szExt, CreateED2kLink(pPartFile));
				else*/
					file.printf(_T("%s\t%s\r\n"), pPartFile->GetFullName(), CreateED2kLink(pPartFile)); 
			}
		}

		if (thePrefs.GetCommitFiles() >= 2 || (thePrefs.GetCommitFiles() >= 1 && !theApp.emuledlg->IsRunning())){
			file.Flush(); // flush file stream buffers to disk buffers
			if (_commit(_fileno(file.m_pStream)) != 0) // commit disk buffers to disk
				AfxThrowFileException(CFileException::hardIO, GetLastError(), file.GetFileName());
		}
		file.Close();

		CString strBakFileListPath = strFileListPath;
		PathRenameExtension(strBakFileListPath.GetBuffer(MAX_PATH), _T(".bak"));
		strBakFileListPath.ReleaseBuffer();

		if (_taccess(strBakFileListPath, 0) == 0)
			CFile::Remove(strBakFileListPath);
		if (_taccess(strFileListPath, 0) == 0)
			CFile::Rename(strFileListPath, strBakFileListPath);
		CFile::Rename(strTmpFileListPath, strFileListPath);
	}
	catch(CFileException* e)
	{
		CString strError;
		TCHAR szError[MAX_CFEXP_ERRORMSG];
		if (e->GetErrorMessage(szError, ARRSIZE(szError))){
			strError += _T(" - ");
			strError += szError;
		}
		LogError(_T("Failed to write part.met file list%s"), strError);
		e->Delete();
		file.Abort();
		(void)_tremove(file.GetFilePath());
	}
}

void CDownloadQueue::OnConnectionState(bool /*bConnected*/)
{
	for (POSITION pos = filelist.GetHeadPosition(); pos != 0; )
	{
		CPartFile* pPartFile = filelist.GetNext(pos);
		if (pPartFile->GetStatus() == PS_READY || pPartFile->GetStatus() == PS_EMPTY)
			pPartFile->SetActive(/*bConnected*/theApp.IsWorkingAllowed(WRK_ASKING)); // NEO: SO - [StandAlone] <-- Xanatos --
	}
}

CString CDownloadQueue::GetOptimalTempDir(UINT nCat, EMFileSize nFileSize){
	// shortcut
	if (thePrefs.GetTempDirCount() == 1) // NEO: MTD - [MultiTempDirectories] <-- Xanatos --
		return thePrefs.GetTempDir();

	CMap<int, int, sint64, sint64> mapNeededSpaceOnDrive;
	CMap<int, int, sint64, sint64> mapFreeSpaceOnDrive;
	
	sint64 llBuffer = 0;
	sint64 llHighestFreeSpace = 0;
	int	nHighestFreeSpaceDrive = -1;
	// first collect the free space on drives
	for (int i = 0; i < thePrefs.GetTempDirCount(); i++) { // NEO: MTD - [MultiTempDirectories] <-- Xanatos --
		const int nDriveNumber = GetPathDriveNumber(thePrefs.GetTempDir(i));
		if (mapFreeSpaceOnDrive.Lookup(nDriveNumber, llBuffer))
			continue;
		llBuffer = GetFreeDiskSpaceX(thePrefs.GetTempDir(i)) - thePrefs.GetMinFreeDiskSpace();
		mapFreeSpaceOnDrive.SetAt(nDriveNumber, llBuffer);
		if (llBuffer > llHighestFreeSpace){
			nHighestFreeSpaceDrive = nDriveNumber;
			llHighestFreeSpace = llBuffer;
		}
		
	}

	// now get the space we would need to download all files in the current queue
	POSITION pos = filelist.GetHeadPosition();
	while (pos != NULL){
		CPartFile* pCurFile =  filelist.GetNext(pos);
		const int nDriveNumber = GetPathDriveNumber(pCurFile->GetTempPath());

		sint64 llNeededForCompletion = 0;
		switch(pCurFile->GetStatus(false)){
			case PS_READY:
			case PS_EMPTY:
			//case PS_WAITINGFORHASH: // NEO: SSH - [SlugFillerSafeHash] <-- Xanatos --
			case PS_INSUFFICIENT:
				llNeededForCompletion = pCurFile->GetFileSize() - pCurFile->GetRealFileSize();
				if (llNeededForCompletion < 0)
					llNeededForCompletion = 0;
		}
		llBuffer = 0;
		mapNeededSpaceOnDrive.Lookup(nDriveNumber, llBuffer);
		llBuffer += llNeededForCompletion;
		mapNeededSpaceOnDrive.SetAt(nDriveNumber, llBuffer);
	}

	sint64 llHighestTotalSpace = 0;
	int	nHighestTotalSpaceDir = -1;
	int	nHighestFreeSpaceDir = -1;
	int	nAnyAvailableDir = -1;
	// first round (0): on same drive as incomming and enough space for all downloading
	// second round (1): enough space for all downloading
	// third round (2): most actual free space
	for (int i = 0; i < thePrefs.GetTempDirCount(); i++) { // NEO: MTD - [MultiTempDirectories] <-- Xanatos --
		const int nDriveNumber = GetPathDriveNumber(thePrefs.GetTempDir(i));
		llBuffer = 0;

		sint64 llAvailableSpace = 0;
		mapFreeSpaceOnDrive.Lookup(nDriveNumber, llAvailableSpace);
		mapNeededSpaceOnDrive.Lookup(nDriveNumber, llBuffer);
		llAvailableSpace -= llBuffer;

		// no condition can be met for a large file on a FAT volume
		if (nFileSize <= OLD_MAX_EMULE_FILE_SIZE || !IsFileOnFATVolume(thePrefs.GetTempDir(i))){
			// condition 0
			// needs to be same drive and enough space
			if (GetPathDriveNumber(thePrefs.GetCatPath(nCat)) == nDriveNumber &&
				llAvailableSpace > (sint64)nFileSize)
			{
				//this one is perfect
				return thePrefs.GetTempDir(i);
			}
			// condition 1
			// needs to have enough space for downloading
			if (llAvailableSpace > (sint64)nFileSize && llAvailableSpace > llHighestTotalSpace){
				llHighestTotalSpace = llAvailableSpace;
				nHighestTotalSpaceDir = i;
			}
			// condition 2
			// first one which has the highest actualy free space
			if ( nDriveNumber == nHighestFreeSpaceDrive && nHighestFreeSpaceDir == (-1)){
				nHighestFreeSpaceDir = i;
			}
			// condition 3
			// any directory which can be used for this file (ak not FAT for large files)
			if ( nAnyAvailableDir == (-1)){
				nAnyAvailableDir = i;
			}
		}
	}

	if (nHighestTotalSpaceDir != (-1)){	 //condtion 0 was apperently too much, take 1
		return thePrefs.GetTempDir(nHighestTotalSpaceDir);
	}
	else if (nHighestFreeSpaceDir != (-1)){ // condtion 1 could not be met too, take 2
		return thePrefs.GetTempDir(nHighestFreeSpaceDir);
	}
	else if( nAnyAvailableDir != (-1)){
		return thePrefs.GetTempDir(nAnyAvailableDir);
	}
	else{ // so was condtion 2 and 3, take 4.. wait there is no 3 - this must be a bug
		ASSERT( false );
		return thePrefs.GetTempDir();
	}
}

// NEO: MSH - [ManualSourceHandling] -- Xanatos -->
void CDownloadQueue::ManualLocalRequests(CTypedPtrList<CPtrList, CPartFile*>& ServerReqQueue)
{
	if(!theApp.serverconnect->IsConnected())
		return;

	POSITION pos = ServerReqQueue.GetHeadPosition();
	while(pos != NULL)
	//while(!ServerReqQueue.IsEmpty())
	{
		CSafeMemFile dataTcpFrame(22);
		const int iMaxFilesPerTcpFrame = theApp.serverconnect->GetCurrentServer() ? theApp.serverconnect->GetCurrentServer()->GetMaxAsksFromCredits(15) : 15; // NEO: KLC - [KhaosLugdunumCredits] <-- Xanatos --
		int iFiles = 0;

		// NEO: KLC - [KhaosLugdunumCredits] -- Xanatos -->
		// Note: when we exided our credits break, whe the manual reask will be called for the next time, 
		// files we have asked now will have a recent last ask date and will be skilled, so we eill ask to the next files we need.
		// Instad, we may sort the ServerReqQueue list everytime by last ask, but the current sollution is easyer
		if(iMaxFilesPerTcpFrame == 0)
			break;
		// NEO: KLC END <-- Xanatos --

		CPartFile* cur_file;
		//while (!ServerReqQueue.IsEmpty() && (iFiles < iMaxFilesPerTcpFrame)){
		//	cur_file = ServerReqQueue.GetHead();
		//	ServerReqQueue.RemoveHead();
		while ((pos != NULL) && (iFiles < iMaxFilesPerTcpFrame)){
			cur_file = ServerReqQueue.GetNext(pos);

			//cur_file->m_bLocalSrcReqQueued = false;
			if(cur_file->IsKindOf(RUNTIME_CLASS(CPartFile))) // NEO: ASP - [ActiveSpreading]
			{
				if(cur_file->IsStopped())
					continue;
 				if(cur_file->m_LastSearchTime && (::GetTickCount() - cur_file->m_LastSearchTime) < /*PartPrefs.GetSVRIntervalsMs()*/ SERVERREASKTIME)
					continue;

			}

			if (cur_file->IsLargeFile() && (theApp.serverconnect->GetCurrentServer() == NULL || !theApp.serverconnect->GetCurrentServer()->SupportsLargeFilesTCP())){
				ASSERT( false );
				DebugLogError(_T("Large file (%s) on local requestqueue for server without support for large files"), cur_file->GetFileName());
				continue;
			}

			if(cur_file->IsKindOf(RUNTIME_CLASS(CPartFile))) // NEO: ASP - [ActiveSpreading]
				cur_file->m_LastSearchTime = ::GetTickCount();
			iFiles++;
			
			// create request packet
			CSafeMemFile smPacket;
			smPacket.WriteHash16(cur_file->GetFileHash());
			if (!cur_file->IsLargeFile()){
				smPacket.WriteUInt32((uint32)(uint64)cur_file->GetFileSize());
			}
			else{
				smPacket.WriteUInt32(0); // indicates that this is a large file and a uint64 follows
				smPacket.WriteUInt64(cur_file->GetFileSize());
			}

			uint8 byOpcode = 0;
			if (thePrefs.IsClientCryptLayerSupported() && theApp.serverconnect->GetCurrentServer() != NULL && theApp.serverconnect->GetCurrentServer()->SupportsGetSourcesObfuscation())
				byOpcode = OP_GETSOURCES_OBFU;
			else
				byOpcode = OP_GETSOURCES;

			Packet* packet = new Packet(&smPacket, OP_EDONKEYPROT, byOpcode);
			if (thePrefs.GetDebugServerTCPLevel() > 0)
				Debug(_T(">>> Sending OP__GetSources%s(%2u/%2u); %s\n"), (byOpcode == OP_GETSOURCES) ? _T("") : _T("_OBFU"), iFiles, iMaxFilesPerTcpFrame, DbgGetFileInfo(cur_file->GetFileHash()));
			dataTcpFrame.Write(packet->GetPacket(), packet->GetRealPacketSize());
			delete packet;

			if (thePrefs.GetDebugSourceExchange())
				AddDebugLogLine(false, _T("SXSend: Local server source request; File=\"%s\""), cur_file->GetFileName());

			ModLog(GetResString(IDS_X_COLLECT_SVR_DONE), cur_file->GetFileName());
		}

		int iSize = (int)dataTcpFrame.GetLength();
		if (iSize > 0)
		{
			// create one 'packet' which contains all buffered OP_GETSOURCES eD2K packets to be sent with one TCP frame
			// server credits: 16*iMaxFilesPerTcpFrame+1 = 241
			Packet* packet = new Packet(new char[iSize], (UINT)dataTcpFrame.GetLength(), true, false);
			dataTcpFrame.Seek(0, CFile::begin);
			dataTcpFrame.Read(packet->GetPacket(), iSize);
			theStats.AddUpDataOverheadServer(packet->size);
			theApp.serverconnect->SendPacket(packet, true);
			// NEO: KLC - [KhaosLugdunumCredits] -- Xanatos -->
			if(theApp.serverconnect->GetCurrentServer() != NULL)
				theApp.serverconnect->GetCurrentServer()->FileAsksSent(iFiles); 
			// NEO: KLC END <-- Xanatos --
		}

		// next TCP frame with up to 15 source requests is allowed to be sent in..
		m_dwNextTCPSrcReq = ::GetTickCount() + SEC2MS(iMaxFilesPerTcpFrame*(16+4));
		// X?: should this by alpyed also to manual requests above 15 files???
	}
}


void CDownloadQueue::ManualGlobalRequests(CTypedPtrList<CPtrList, CPartFile*>& GlobalReqQueue)
{
	if (!theApp.serverconnect->IsConnected())
		return;
	if (!theApp.serverconnect->IsUDPSocketAvailable())
		return;

	//CServer* pConnectedServer = theApp.serverconnect->GetCurrentServer();
	//if (pConnectedServer)
	//	pConnectedServer = theApp.serverlist->GetServerByAddress(pConnectedServer->GetAddress(), pConnectedServer->GetPort());

	// Ask all servers for all files
	POSITION pos = GlobalReqQueue.GetHeadPosition();
	while(pos != NULL)
	{
		// NEO: HUC - [HyperUDPCollecting]
		POSITION nextpos = pos;

		CPartFile* cur_file = GlobalReqQueue.GetAt(pos);
		CServer* udpserver = thePrefs.UseUDPCache() ? udpserver = cur_file->GetNextAvailServer() : NULL; // NEO: XUC - [ExtendedUdpCache]
		if(udpserver == NULL)
			udpserver = theApp.serverlist->GetSuccServer(udpserver);
		for(;udpserver != NULL;udpserver = theApp.serverlist->GetSuccServer(udpserver))
		//while(
		// (thePrefs.UseUDPCache() && udpserver == NULL && (udpserver = cur_file->GetNextAvailServer()) != NULL) // NEO: XUC - [ExtendedUdpCache]
		//											 || ((udpserver = theApp.serverlist->GetSuccServer(udpserver)) != NULL)
		//)
		{
			if (theApp.serverconnect->IsLocalServer(udpserver->GetIP(),udpserver->GetPort())/*pConnectedServer == udpserver*/) // NEO: NEW - [NewCoded]
				continue;

			bool bGetSources2Packet = ((udpserver->GetUDPFlags() & SRV_UDPFLG_EXT_GETSOURCES2) > 0);
			bool bServerSupportsLargeFiles = udpserver->SupportsLargeFilesUDP();

			// loop until the packet is filled or a packet was sent
			CSafeMemFile dataGlobGetSources(20);
			int iFiles = 0;
			int iLargeFiles = 0;

			nextpos = pos;

			while (IsMaxFilesPerUDPServerPacketReached(iFiles,iLargeFiles,true) && (nextpos != NULL) ){
				cur_file = GlobalReqQueue.GetNext(nextpos);

				if(cur_file->IsStopped())
					continue;
				if(cur_file->m_LastSearchTimeUdp && (::GetTickCount() - cur_file->m_LastSearchTimeUdp) < thePrefs.GetGlobalUDPIntervalsMs() /*UDPSERVERREASKTIME*/)
					continue;

				if(!bServerSupportsLargeFiles && cur_file->IsLargeFile()) // this server does not support large files :'(
					continue;

				iFiles++;
				if (bGetSources2Packet){
					if (cur_file->IsLargeFile()){
						// GETSOURCES2 Packet Large File (<HASH_16><IND_4 = 0><SIZE_8> *)
						iLargeFiles++;
						dataGlobGetSources.WriteHash16(cur_file->GetFileHash());
						dataGlobGetSources.WriteUInt32(0);
						dataGlobGetSources.WriteUInt64(cur_file->GetFileSize());
					}
					else{
						// GETSOURCES2 Packet (<HASH_16><SIZE_4> *)
						dataGlobGetSources.WriteHash16(cur_file->GetFileHash());
						dataGlobGetSources.WriteUInt32((uint32)(uint64)cur_file->GetFileSize());
					}
				}
				else{
					// GETSOURCES Packet (<HASH_16> *)
					dataGlobGetSources.WriteHash16(cur_file->GetFileHash());
				}

				if (thePrefs.GetDebugServerUDPLevel() > 0 && thePrefs.GetDebugServerSourcesLevel() > 0)
					Debug(_T(">>> Queued  %s to server %-21s (%3u of %3u); Buff  %u(%u)=%s\n"), bGetSources2Packet ? _T("OP__GlobGetSources2") : _T("OP__GlobGetSources1"), ipstr(udpserver->GetAddress(), udpserver->GetPort()), m_iSearchedServers + 1, theApp.serverlist->GetServerCount(), iFiles, iLargeFiles, DbgGetFileInfo(cur_file->GetFileHash()));
			}

			if (dataGlobGetSources.GetLength() == 0)
				break; // No files where as is alowed or that are resumed, exit loop
			
			SendGlobGetSourcesUDPPacket(&dataGlobGetSources, bGetSources2Packet, iFiles, iLargeFiles, udpserver);

			if(thePrefs.UseUDPCache())
				break; // In cache mode only ask one server, exit loop
		}

		if(pos == nextpos)
			break; // no usefull servers found, exit main loop

		// shift to the right place and set the last search time
		while ((pos != NULL) && (pos != nextpos)){
			cur_file = GlobalReqQueue.GetNext(pos);

			if(cur_file->IsStopped())
				continue;
			if(cur_file->m_LastSearchTimeUdp && (::GetTickCount() - cur_file->m_LastSearchTimeUdp) < thePrefs.GetGlobalUDPIntervalsMs() /*UDPSERVERREASKTIME*/)
				continue;

			cur_file->m_LastSearchTimeUdp = ::GetTickCount();

			ModLog(GetResString(IDS_X_COLLECT_UDP_DONE), cur_file->GetFileName());
		}
		// NEO: HUC END
	}
}
// NEO: MSH - [ManualSourceHandling] <-- Xanatos --

// NEO: QS - [QuickStart] -- Xanatos -->
void CDownloadQueue::DoQuickStart()
{
	int CountFiles = 0;
	for(POSITION pos = filelist.GetHeadPosition(); pos != NULL;) 
	{ 
		CPartFile* cur_file = filelist.GetNext(pos); 
		if(cur_file->GetStatus() == PS_READY || cur_file->GetStatus() == PS_EMPTY )
			CountFiles ++;
	}
	if(CountFiles == 0)
		return;

	DWORD QuickStartTime = min((CountFiles * thePrefs.GetQuickStartTimePerFileMs()), thePrefs.GetQuickStartMaxTimeMs());

	if (QuickStartTime > 0){
		m_bQuickStartDone = false;
		thePrefs.SetOnQuickStart(true);
		m_dwQuickStartEndTime = ::GetTickCount() + QuickStartTime;
		ModLog(LOG_STATUSBAR, GetResString(IDS_X_QUICK_START_BEGIN), MS2MIN((float)QuickStartTime));
	}else
		m_bQuickStartDone = true;
}

void CDownloadQueue::StopQuickStart()
{
	thePrefs.SetOnQuickStart(false);
	m_bQuickStartDone = true;
	ModLog(LOG_STATUSBAR, GetResString(IDS_X_QUICK_START_END));
}
// NEO: QS END <-- Xanatos --

#ifdef NEO_DBT // NEO: NDBT - [NeoDownloadBandwidthThrottler] -- Xanatos -->
void CDownloadQueue::ReSortDownloadSlots(CUpDownClient* client) {
	if(!thePrefs.IsDirectDownload()
	 && client)
		theApp.downloadBandwidthThrottler->ReSortDownloadSlots(client);
}

void CDownloadQueue::ProcessReceiving(){
	POSITION posSock = ProcessQueue.GetHeadPosition();
	ThrottledControlSocket* socket = NULL;
	while (posSock){
		socket = ProcessQueue.GetNext(posSock);
		if(socket->ProcessData() == false) // not longer on reciving queue, all datas processed
			RemoveFromProcessQueue(socket);
	}
}

void CDownloadQueue::AddToProcessQueue(ThrottledControlSocket* sock){
	if (sock && !sock->onDownProcessQueue){
		ProcessQueue.AddTail(sock);
		sock->onDownProcessQueue = true;
	}
}

void CDownloadQueue::RemoveFromProcessQueue(ThrottledControlSocket* sock){
	if (sock && sock->onDownProcessQueue){
		sock->onDownProcessQueue = false;
		POSITION pos = ProcessQueue.Find(sock);
		if (pos){
			//if (pos == posSock)
			//	ProcessQueue.GetNext(posSock);
			ProcessQueue.RemoveAt(pos);
		}
	}
}
#endif // NEO_DBT // NEO: NDBT END <-- Xanatos --

// NEO: NDC - [NeoDownloadCommands] -- Xanatos -->
void CDownloadQueue::ExecuteNeoCommand(CTypedPtrList<CPtrList, CPartFile*>& selectedList, uint8 uNeoCmdL, uint8 uNeoCmdW)
{
#ifdef VOODOO // NEO: VOODOOn - [VoodooExtensionForNeo]
	if(thePrefs.IsVoodooNeoCommands())
		theApp.voodoo->ManifestDownloadCommand(selectedList,uNeoCmdL,uNeoCmdW);
#endif // VOODOO // NEO: VOODOOn END

	switch(uNeoCmdL)
	{
		case INST_COLLECT:
			switch(uNeoCmdW)
			{
				case INST_COLLECT_ALL_SOURCES: 
					theApp.downloadqueue->ManualLocalRequests(selectedList);	// Get Server Sources
					theApp.downloadqueue->ManualGlobalRequests(selectedList);	// Get UDP Sources
					while (!selectedList.IsEmpty()){
						selectedList.GetHead()->SetXSSourcesCollecting();		// Get X-Change Sources
						selectedList.GetHead()->CollectKADSources();			// Get KAD Source
						selectedList.RemoveHead();
					}
					break;

				case INST_COLLECT_XS_SOURCES: 
					while (!selectedList.IsEmpty()){
						selectedList.GetHead()->SetXSSourcesCollecting();
						selectedList.RemoveHead();
					}
					break;
				case INST_COLLECT_SVR_SOURCES: 
					theApp.downloadqueue->ManualLocalRequests(selectedList);
					break;
				case INST_COLLECT_KAD_SOURCES: 
					while (!selectedList.IsEmpty()){
						selectedList.GetHead()->CollectKADSources();
						selectedList.RemoveHead();
					}
					break;
				case INST_COLLECT_UDP_SOURCES: 
					theApp.downloadqueue->ManualGlobalRequests(selectedList);
					break;
#ifdef VOODOO // NEO: VOODOOn - [VoodooExtensionForNeo]
				case INST_COLLECT_VOODOO_SOURCES:
					while (!selectedList.IsEmpty()){
						if(selectedList.GetHead()->KnownPrefs.UseVoodoo())
							theApp.voodoo->ManifestSourceListRequest(selectedList.GetHead());
						selectedList.RemoveHead();
					}
					break;
#endif // VOODOO // NEO: VOODOOn END

				case INST_AHL_INCREASE: 
					while (!selectedList.IsEmpty()){
						selectedList.GetHead()->IncreaseAutoHardLimit();
						selectedList.RemoveHead();
					}
					break;
				case INST_AHL_DECREASE: 
					while (!selectedList.IsEmpty()){
						selectedList.GetHead()->DecreaseAutoHardLimit();
						selectedList.RemoveHead();
					}
					break;
				default:
					DebugLogError(_T("Unknown Extended Instruction: INST_COLLECT; %u"),(uint32)uNeoCmdW);
			}
			break;
		case INST_DROP:
			switch(uNeoCmdW)
			{
				case INST_DROP_NNP:
					while (!selectedList.IsEmpty()){
						selectedList.GetHead()->DropSources(DS_NONEEDEDPARTS);
						selectedList.RemoveHead();
					}
					break;
				case INST_DROP_FULLQ:
					while (!selectedList.IsEmpty()){
						selectedList.GetHead()->DropSources(DS_REMOTEQUEUEFULL);
						selectedList.RemoveHead();
					}
					break;
				case INST_DROP_HIGHQ:
					while (!selectedList.IsEmpty()){
						selectedList.GetHead()->DropHighQSources();
						selectedList.RemoveHead();
					}
					break;
				// NEO: TCR - [TCPConnectionRetry]
				case INST_DROP_WAITINGRETRY:
					while (!selectedList.IsEmpty()){
						selectedList.GetHead()->DropSources(DS_CONNECTIONRETRY);
						selectedList.RemoveHead();
					}
					break;
				// NEO: TCR END
				// NEO: XSC - [ExtremeSourceCache]
				case INST_DROP_CACHED:
					while (!selectedList.IsEmpty()){
						selectedList.GetHead()->DropSources(DS_CACHED);
						selectedList.RemoveHead();
					}
					break;
				// NEO: XSC END
#ifdef NEO_SS // NEO: NSS - [NeoSourceStorage]
				case INST_DROP_OUTOFDATE:
					while (!selectedList.IsEmpty()){
						selectedList.GetHead()->DropOutOfDateSources();
						selectedList.RemoveHead();
					}
					break;
				case INST_DROP_LOADED:
					while (!selectedList.IsEmpty()){
						selectedList.GetHead()->DropSources(DS_LOADED);
						selectedList.RemoveHead();
					}
					break;
				case INST_DROP_LOADEDWAITING:
					while (!selectedList.IsEmpty()){
						selectedList.GetHead()->DropSources(DS_LOADEDWAITING);
						selectedList.RemoveHead();
					}
					break;
#ifdef NEO_SA // NEO: NSA - [NeoSourceAnaliser]
				case INST_DROP_RESERVE:
					while (!selectedList.IsEmpty()){
						selectedList.GetHead()->DropSources(DS_RESERVE);
						selectedList.RemoveHead();
					}
					break;
#endif // NEO_SA // NEO: NSA END
				case INST_DROP_RETIRED:
					while (!selectedList.IsEmpty()){
						selectedList.GetHead()->DropSources(DS_RETIRED);
						selectedList.RemoveHead();
					}
					break;
#endif // NEO_SS // NEO: NSS END
#ifdef NEO_SK // NEO: NSK - [NeoSourceKeeper]
				case INST_DROP_UNREACHABLE:
					while (!selectedList.IsEmpty()){
						selectedList.GetHead()->DropSources(DS_UNREACHABLE);
						selectedList.RemoveHead();
					}
					break;
#endif // NEO_SK // NEO: NSK END
				case INST_DROP_TOMANY:
					while (!selectedList.IsEmpty()){
						selectedList.GetHead()->DropSources(DS_TOOMANYCONNS);
						selectedList.GetHead()->DropSources(DS_TOOMANYCONNSKAD);
						selectedList.RemoveHead();
					}
					break;
				case INST_DROP_UNKNOWN:
					while (!selectedList.IsEmpty()){
						selectedList.GetHead()->DropSources(DS_NONE);
						selectedList.RemoveHead();
					}
					break;
				case INST_DROP_BAD:
					while (!selectedList.IsEmpty()){
						selectedList.GetHead()->DropSources(DS_ERROR);
						selectedList.GetHead()->DropSources(DS_BANNED);
						selectedList.RemoveHead();
					}
					break;
				case INST_DROP_LOW2LOW:
					while (!selectedList.IsEmpty()){
						selectedList.GetHead()->DropSources(DS_LOWTOLOWIP);
						selectedList.RemoveHead();
					}
					break;
				default:
					DebugLogError(_T("Unknown Extended Instruction: INST_DROP; %u/%u"),(uint32)uNeoCmdW);
			}
			break;
		case INST_REASK:
			switch(uNeoCmdW)
			{
				case INST_REASK_FULLQ:
					while (!selectedList.IsEmpty()){
						selectedList.GetHead()->ReaskSources(DS_REMOTEQUEUEFULL);
						selectedList.RemoveHead();
					}
					break;
				case INST_REASK_NNP:
					while (!selectedList.IsEmpty()){
						selectedList.GetHead()->ReaskSources(DS_NONEEDEDPARTS);
						selectedList.RemoveHead();
					}
					break;
				// NEO: TCR - [TCPConnectionRetry]
				case INST_REASK_WAITINGRETRY: 
					while (!selectedList.IsEmpty()){
						selectedList.GetHead()->ReaskSources(DS_CONNECTIONRETRY);
						selectedList.RemoveHead();
					}
					break;
				// NEO: TCR END
				// NEO: XSC - [ExtremeSourceCache]
				case INST_REASK_CACHED: 
					while (!selectedList.IsEmpty()){
						selectedList.GetHead()->ReaskSources(DS_CACHED);
						selectedList.RemoveHead();
					}
					break;
				// NEO: XSC END
#ifdef NEO_SS // NEO: NSS - [NeoSourceStorage]
				case INST_REASK_LOADEDGROUP: 
					while (!selectedList.IsEmpty()){
						selectedList.GetHead()->ReaskNextGroup();
						selectedList.RemoveHead();
					}
					break;
				case INST_REASK_LOADED: 
					while (!selectedList.IsEmpty()){
						selectedList.GetHead()->ReaskSources(DS_LOADED);
						selectedList.RemoveHead();
					}
					break;
				case INST_REASK_LOADEDWAITING: 
					while (!selectedList.IsEmpty()){
						selectedList.GetHead()->ReaskSources(DS_LOADEDWAITING);
						selectedList.RemoveHead();
					}
					break;
#ifdef NEO_SA // NEO: NSA - [NeoSourceAnaliser]
				case INST_REASK_RESERVE: 
					while (!selectedList.IsEmpty()){
						selectedList.GetHead()->ReaskSources(DS_RESERVE);
						selectedList.RemoveHead();
					}
					break;
#endif // NEO_SA // NEO: NSA END
#endif // NEO_SS // NEO: NSS END
#ifdef NEO_SK // NEO: NSK - [NeoSourceKeeper]
#endif // NEO_SK // NEO: NSK END
				default:
					DebugLogError(_T("Unknown Extended Instruction: INST_REASK; %u"),(uint32)uNeoCmdW);
			}
			break;

		case INST_STORAGE:
			switch(uNeoCmdW)
			{
#ifdef NEO_SS // NEO: NSS - [NeoSourceStorage]
				case INST_STORAGE_LOAD:
					while (!selectedList.IsEmpty()){
						selectedList.GetHead()->LoadSources();
						selectedList.RemoveHead();
					}
					break;
				case INST_STORAGE_SAVE:
					while (!selectedList.IsEmpty()){
						selectedList.GetHead()->SaveSources();
						selectedList.RemoveHead();
					}
					break;
				case INST_STORAGE_IMPORT:{
					CFileDialog dlg(TRUE, _T(".met.src"), NULL, OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST, _T("Source Files (*.met.src)|*.met.src||"), NULL, 0);
					if(dlg.DoModal()!=IDOK)
						break;
					CString PathName=dlg.GetPathName();

					theApp.downloadqueue->ImportSources(selectedList, PathName);
					break;
				}
				case INST_STORAGE_EXPORT:{
					CFileDialog dlg(FALSE, _T(".met.src"), NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, _T("Source Files (*.met.src)|*.met.src||"), NULL, 0);
					if(dlg.DoModal()!=IDOK)
						break;
					CString PathName=dlg.GetPathName();

					theApp.downloadqueue->ExportSources(selectedList, PathName);
					break;
				}
 #ifdef NEO_CD // NEO: NCD - [NeoClientDatabase] // NEO: SFL - [SourceFileList]
				case INST_STORAGE_FIND:
					while (!selectedList.IsEmpty()){
						theApp.sourcelist->FindSources(selectedList.GetHead());
						selectedList.RemoveHead();
					}
					break;
 #endif // NEO_CD // NEO: NCD END // NEO: SFL END
#endif // NEO_SS // NEO: NSS END
				default:
					DebugLogError(_T("Unknown Extended Instruction: INST_REASK; %u"),(uint32)uNeoCmdW);
			}
			break;

		case INST_OTHER:
			switch(uNeoCmdW)
			{
				// NEO: PP - [PasswordProtection]
				case INST_OTHER_PROTECT_SHOW:{
					InputBox inputbox;
					inputbox.SetLabels(GetResString(IDS_X_PWPROT_SHOWIBTITLE),GetResString(IDS_X_PWPROT_SHOWIBDESC), _T(""));
					inputbox.SetPassword(true);	
					inputbox.DoModal();
					CString pass=inputbox.GetInput();
					if (!inputbox.WasCancelled() /*&& pass.GetLength()>0*/){ // for voodoo 
						MD5Sum makeMD5Sum( pass );
						pass = makeMD5Sum.GetHash();

						while (!selectedList.IsEmpty()){
							if ( selectedList.GetHead()->GetPWProt() == pass){
								selectedList.GetHead()->SetPWProtShow(true);
								theApp.emuledlg->transferwnd->downloadlistctrl.ShowFile(selectedList.GetHead());
								//if (selectedList.GetHead()->GetStatus(true) == PS_READY) // NEO: SEF - [ShareAlsoEmptyFiles] // || (thePrefs.IsShareAlsoEmptyFiles() && selectedList.GetHead()->GetStatus(true) == PS_EMPTY)) 
								theApp.emuledlg->sharedfileswnd->sharedfilesctrl.AddFile((CKnownFile*)selectedList.GetHead());
							}
							selectedList.RemoveHead();
						}

					}
					break;
				}
				case INST_OTHER_PROTECT_HIDE:
					while (!selectedList.IsEmpty()){
						if ( selectedList.GetHead()->IsPWProt()){
							selectedList.GetHead()->SetPWProtShow(false);
							theApp.emuledlg->transferwnd->downloadlistctrl.HideFile(selectedList.GetHead());
							theApp.emuledlg->sharedfileswnd->sharedfilesctrl.RemoveFile((CKnownFile*)selectedList.GetHead());
						}
						selectedList.RemoveHead();
					}
					break;
				case INST_OTHER_PROTECT_SET:{
					InputBox inputbox1, inputbox2;
					CString pass1 = _T(""), pass2 = _T("");
					MD5Sum makeMD5Sum;

					inputbox1.SetLabels(GetResString(IDS_X_PWPROT_SETIBTITLE),GetResString(IDS_X_PWPROT_SETIBDESC), _T(""));
					//inputbox1.SetPassword(true);
					if (inputbox1.DoModal() == IDOK)
						pass1=inputbox1.GetInput();
					else 
						break;

					while(!selectedList.IsEmpty()) { 
						CPartFile *file = selectedList.GetHead();
						if ( !file->IsPWProt() ) {
							file->SetPWProtShow(true);
							file->SetPWProt( (pass1.GetLength() == 0) ? _T("") : makeMD5Sum.Calculate( pass1 ) );
						}
						selectedList.RemoveHead();
					}
					break;
				}
				case INST_OTHER_PROTECT_CHANGE:{
					InputBox inputbox1, inputbox2;
					CString pass1 = _T(""), pass2 = _T("");
					MD5Sum makeMD5Sum;

					inputbox1.SetLabels(GetResString(IDS_X_PWPROT_OLDPWIBTITLE),GetResString(IDS_X_PWPROT_OLDPWIBDESC), _T(""));
					inputbox1.SetPassword(true);
					if (inputbox1.DoModal() == IDOK) {
						pass1=inputbox1.GetInput();
						if (pass1.GetLength() == 0)
							break;
					} else 
						break;

					inputbox2.SetLabels(GetResString(IDS_X_PWPROT_SETIBTITLE),GetResString(IDS_X_PWPROT_SETIBDESC), _T(""));
					//inputbox2.SetPassword(true);
					if (inputbox2.DoModal() == IDOK){
						pass2=inputbox2.GetInput();
						if (pass2.GetLength() == 0)
							if(AfxMessageBox(GetResString(IDS_X_PWPROT_CLEARPW), MB_YESNO|MB_ICONQUESTION) == IDNO)
								break;
					}

					while(!selectedList.IsEmpty()) { 
						CPartFile *file = selectedList.GetHead();
						if ( file->IsPWProt() ) {
							if ( makeMD5Sum.Calculate( pass1 ) == file->GetPWProt() ) {
								file->SetPWProtShow(true);
								file->SetPWProt( (pass2.GetLength() == 0) ? _T("") : makeMD5Sum.Calculate( pass2 ) );
							}
						}
						selectedList.RemoveHead();
					}
					break;
				}
				case INST_OTHER_PROTECT_UNSET:{
					InputBox inputbox1;
					CString pass1 = _T("");
					MD5Sum makeMD5Sum;

					inputbox1.SetLabels(GetResString(IDS_X_PWPROT_SHOWIBTITLE),GetResString(IDS_X_PWPROT_UNSETIBDESC), _T(""));
					inputbox1.SetPassword(true);
					if (inputbox1.DoModal() == IDOK) {
						pass1=inputbox1.GetInput();
						if (pass1.GetLength() == 0)
							break;	
					} else 
						break;

					while(!selectedList.IsEmpty()) { 
						CPartFile *file = selectedList.GetHead();
						if ( file->IsPWProt() )
							if ( makeMD5Sum.Calculate( pass1 ) == file->GetPWProt()) {
								file->SetPWProt( _T("") );
							}
						selectedList.RemoveHead();
					}
					break;
				}
				// NEO: FCFG - [FileConfiguration]
				case INST_OTHER_PROPERTIES:
					if(!selectedList.IsEmpty())
						selectedList.GetHead()->GetDetailDialogInterface()->OpenDetailDialog(selectedList, 0, &theApp.emuledlg->transferwnd->downloadlistctrl);
					break;
				case INST_OTHER_PREFERENCES:
					if(!selectedList.IsEmpty())
						selectedList.GetHead()->GetPreferencesDialogInterface()->OpenPreferencesDialog(selectedList, 0, &theApp.emuledlg->transferwnd->downloadlistctrl);
					break;
				// NEO: FCFG END
#ifdef VOODOO // NEO: VOODOOn - [VoodooExtensionForNeo]
				case INST_OTHER_VOODOO_LIST:{
						CVoodooListDlg* dlg = new CVoodooListDlg(); 
						dlg->OpenDialog(); 
					}
					break;
#endif // VOODOO // NEO: VOODOOn END
				default:
					DebugLogError(_T("Unknown Extended Instruction: INST_REASK; %u"),(uint32)uNeoCmdW);
			}
			break;

		default:
			DebugLogError(_T("Unknown Download Command: %u %u"),(uint32)uNeoCmdL,(uint32)uNeoCmdW);
	}
}
// NEO: NDC END <-- Xanatos --

// NEO: NTT - [NewToolTips] -- Xanatos -->
void CDownloadQueue::GetTipInfoByCat(uint8 cat, CString &info)
{
	int count,dwl,err,compl,paus;
	count=dwl=err=compl=paus=0;
	float speed = 0;
	uint64 size = 0;
	uint64 trsize = 0;
	uint64 disksize = 0;

	for (POSITION pos = filelist.GetHeadPosition(); pos; )
	{
		CPartFile* cur_file = filelist.GetNext(pos);
		//if (CheckShowItemInGivenCat(cur_file,cat)) // modified by rayita
		if (cur_file && cur_file->CheckShowItemInGivenCat(cat))
		{
			count++;
			if (cur_file->GetTransferringSrcCount()>0) ++dwl;
			speed += cur_file->GetDatarate()/1024.0f;
			size += (uint64)cur_file->GetFileSize();
			trsize += (uint64)cur_file->GetCompletedSize();
			disksize += (uint64)cur_file->GetRealFileSize();
			if (cur_file->GetStatus()==PS_ERROR) ++err;
			if (cur_file->GetStatus()==PS_PAUSED) ++paus;
		}
	}

	int total;
	compl=theApp.emuledlg->transferwnd->downloadlistctrl.GetCompleteDownloads(cat,total);

	// ZZ:DownloadManager -->
	CString prio = GetResString(IDS_PRIONORMAL);
	if (thePrefs.GetCategory(cat) != NULL)
	{
		switch(thePrefs.GetCategory(cat)->prio) {
			case PR_LOW:
				prio = GetResString(IDS_PRIOLOW);
				break;

			case PR_HIGH:
				prio = GetResString(IDS_PRIOHIGH);
				break;		
		}
	}
// ZZ:DownloadManager <--

	info.Format(GetResString(IDS_X_DLTAB_TL), thePrefs.GetCategory(cat) ? thePrefs.GetCategory(cat)->title : _T(""));
	info.AppendFormat(GetResString(IDS_X_FILES), count);
	info.AppendFormat(GetResString(IDS_X_DOWNLOADING), dwl);
	info.AppendFormat(GetResString(IDS_X_PAUSED), paus);
	info.AppendFormat(GetResString(IDS_X_ERRONEOUS), err);
	info.AppendFormat(GetResString(IDS_X_COMPLETED), compl);
	info.AppendFormat(GetResString(IDS_X_DLTAB_SP), speed);
	info.AppendFormat(GetResString(IDS_X_DLTAB_SZ), CastItoXBytes(trsize, false, false),CastItoXBytes(size, false, false));
	info.AppendFormat(GetResString(IDS_X_DLTAB_DS), CastItoXBytes(disksize, false, false));
	info.AppendFormat(GetResString(IDS_X_DLTAB_PR), prio); // ZZ:DownloadManager
}

void CDownloadQueue::GetTransferTipInfo(CString &info)
{
	int count = 0;
	int dwl = 0;	
	uint64 size = 0;
	uint64 trsize = 0;
	uint64 disksize = 0;

	for (POSITION pos = filelist.GetHeadPosition(); pos; )
	{
		CPartFile* cur_file = filelist.GetNext(pos);
		count++;
		if (cur_file->GetTransferringSrcCount() > 0) ++dwl;		
		size += (uint64)cur_file->GetFileSize();
		trsize += (uint64)cur_file->GetCompletedSize();
		disksize += (uint64)cur_file->GetRealFileSize();
	}

	info.Format(GetResString(IDS_X_DL_SP), theApp.bandwidthControl->GetCurrStatsDataDownload(), theApp.bandwidthControl->GetCurrStatsDataDownload());
	info.AppendFormat(GetResString(IDS_X_DLTAB_DL), dwl, count);
	info.AppendFormat(GetResString(IDS_X_DLTAB_SZ), CastItoXBytes(trsize, false, false), CastItoXBytes(size, false, false));
	info.AppendFormat(GetResString(IDS_X_DLTAB_DS), CastItoXBytes(disksize, false, false));

#ifdef NEO_BC // NEO: NMFS - [NiceMultiFriendSlots]
	UINT ActivatedRatioReason = theApp.bandwidthControl->IsSessionRatio();
	info.AppendFormat(GetResString(IDS_X_SESSION_RATIO2),ActivatedRatioReason ? (ActivatedRatioReason > 1 ? GetResString(IDS_X_SESSION_RATIO_ENABLED_ZZ) : GetResString(IDS_X_SESSION_RATIO_ENABLED)):GetResString(IDS_X_SESSION_RATIO_DISABLED));
	info.AppendFormat(GetResString(IDS_X_ZZRATIO_CHECK02),(theApp.bandwidthControl->IsSessionRatioWorking())?GetResString(IDS_YES):GetResString(IDS_NO));
	info.AppendFormat(GetResString(IDS_X_ZZRATIO_CHECK12),(ActivatedRatioReason & 1)?GetResString(IDS_YES):GetResString(IDS_NO));
	info.AppendFormat(GetResString(IDS_X_ZZRATIO_CHECK22),(ActivatedRatioReason & 2)?GetResString(IDS_YES):GetResString(IDS_NO));
#endif // NEO_BC // NEO: NMFS END

}
// NEO: NTT END <-- Xanatos --

#ifdef WEBCACHE // NEO: WC - [WebCache] -- Xanatos -->
void CDownloadQueue::ChangeGlobalWebCacheEnable(bool bEnable)
{
	for (POSITION pos = filelist.GetHeadPosition(); pos != 0; )
	{
		CPartFile* pPartFile = filelist.GetNext(pos);
		if (pPartFile->IsPartFile()){
			if(bEnable)
				pPartFile->ResumeProxyDownloads();
			else
				pPartFile->CancelProxyDownloads();
		}
	}
}
#endif // NEO: WC END <-- Xanatos --