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

#include "StdAfx.h"
#include "partfile.h"
#include "emule.h"
#include "updownclient.h"
#include <math.h>
#include "ED2KLink.h"
#include "Preview.h"
#include "ini2.h"
#include "ArchiveRecovery.h"

// Barry - use this constant for both places
#define PROGRESS_HEIGHT 3

CBarShader CPartFile::s_LoadBar(PROGRESS_HEIGHT); // Barry - was 5
CBarShader CPartFile::s_ChunkBar(16); 

CPartFile::CPartFile(){
	Init();
}

CPartFile::CPartFile(CSearchFile* searchresult){
	Init();
	memcpy(filehash,searchresult->GetFileHash(),16);
	for (int i = 0; i != searchresult->taglist.GetCount();i++){
		switch (searchresult->taglist[i]->tag->specialtag){
			case FT_FILENAME:{
				filename = nstrdup(searchresult->taglist[i]->tag->stringvalue);
				break;
			}
			case FT_FILESIZE:{
				filesize = searchresult->taglist[i]->tag->intvalue;
				break;
			}
			default:
				CTag* newtag = new CTag(searchresult->taglist[i]->tag);
				taglist.Add(newtag);
		}
	}
	CreatePartFile();
}

CPartFile::CPartFile(CString edonkeylink)
{
	CED2KLink* pLink = 0;
	try {
		pLink = CED2KLink::CreateLinkFromUrl(edonkeylink);
		_ASSERT( pLink != 0 );
		CED2KFileLink* pFileLink = pLink->GetFileLink();
		if (pFileLink==0) 
			throw GetResString(IDS_ERR_NOTAFILELINK);
		InitializeFromLink(pFileLink);
	} catch (CString error) {
		OUTPUT_DEBUG_TRACE();
		char buffer[200];
		sprintf(buffer,GetResString(IDS_ERR_INVALIDLINK),error.GetBuffer());
		theApp.emuledlg->AddLogLine(true, GetResString(IDS_ERR_LINKERROR), buffer);
		status = PS_ERROR;
	}
	delete pLink;
}

void
CPartFile::InitializeFromLink(CED2KFileLink* fileLink)
{
	Init();
	try{
		filename = nstrdup( fileLink->GetName() );
		filesize = fileLink->GetSize();
		memcpy(filehash,fileLink->GetHashKey(),sizeof(filehash));
		if (!theApp.downloadqueue->IsFileExisting(filehash))
			CreatePartFile();
		else
			status = PS_ERROR;
		}
	catch(CString error){
		OUTPUT_DEBUG_TRACE();
		char buffer[200];
		sprintf(buffer,GetResString(IDS_ERR_INVALIDLINK),error.GetBuffer());
		theApp.emuledlg->AddLogLine(true, GetResString(IDS_ERR_LINKERROR), buffer);
		status = PS_ERROR;
	}
}

CPartFile::CPartFile(CED2KFileLink* fileLink)
{
	InitializeFromLink(fileLink);
}

void CPartFile::Init(){
	fullname = 0;
	newdate = true;
	lastsearchtime = 0;
	lastpurgetime = ::GetTickCount();
	paused = false;
	status = PS_EMPTY;
	transfered = 0;
	priority = PR_NORMAL;
	srcarevisible = false;
	transferingsrc = 0;
	datarate = 0;
	hashsetneeded = true;
	count = 0;
	percentcompleted = 0;
	partmetfilename = 0;
	completedsize=0;

	/////////////////////////////////////
	//for saving sources:
	
	this->m_bLoadedSourcesCompletely = true;//do load sources only on a call of LoadAndAddSourceFromFile(...)
	this->m_iSourceIndex					= 0;
	this->m_iSourceIDIndex					= -1;
	this->m_bWasConnectedToServerLastTime	= false;
	this->m_iLastTimeSourcesLoaded			= ::GetTickCount();	//I'm not sure, maybe it should be 0
	this->m_iLastTimeSourcesSaved			= ::GetTickCount();	//I'm not sure, maybe it should be 0
	this->m_iLastTimeSourcesLoadedPartial	= ::GetTickCount();	//I'm not sure, maybe it should be 0
	this->m_iLoadSourcesAtOnceLimit			= RESTORE_SOURCES_AT_ONCE;
	this->m_iLoadingSourcesTimeInterval		= RESTORE_SOURCES_TIME;
	this->m_iLoadingSourcesPartialTimeInterval= RESTORE_SOURCES_SLOWLY_TIME;
	this->m_iSavingSourcesTimeInterval		= STORE_SOURCES_TIME;
	this->m_stscrStoringSources				= NULL;
	this->m_bLoadSavedSources				= true;

	m_bPreviewing = false;
	lastseencomplete=NULL;
	availablePartsCount=0;
	m_ClientSrcAnswered = 0;
	m_LastNoNeededCheck = 0;
	m_bAutoPriority = false ; 
	m_iRate=0;
	m_strComment="";
	m_nTotalBufferData = 0;
	m_nLastBufferFlushTime = 0;
	m_bPercentUpdated = false;
	m_bRecoveringArchive = false;
	m_iGainDueToCompression = 0;
	m_iLostDueToCorruption = 0;
	m_iTotalPacketsSavedDueToICH = 0;
	m_is_A4AF_auto = false; // Added by Tarod
}

CPartFile::~CPartFile(){
	// Barry - Ensure all buffered data is written
	FlushBuffer();

	if (fullname)
		delete[] fullname;
	if (partmetfilename)
		delete[] partmetfilename;
	m_SrcpartFrequency.RemoveAll();
	for (POSITION pos = gaplist.GetHeadPosition();pos != 0;gaplist.GetNext(pos))
		delete gaplist.GetAt(pos);
	/////////////////////////////////////
	//for saving sources: mr fry
	if(this->m_stscrStoringSources != NULL) {
		delete this->m_stscrStoringSources;
		this->m_stscrStoringSources = NULL;
	}
}

void CPartFile::CreatePartFile(){
	// use lowest free partfilenumber for free file (InterCeptor)
	int i = 0; 
	CString filename; 
	do 
	{ 
		i++; 
		filename.Format("%s\\%03i.part", theApp.glob_prefs->GetTempDir(), i); 

	}while(PathFileExists(filename)); 
	partmetfilename = new char[15]; 
	sprintf(partmetfilename,"%03i.part.met",i); 

	fullname = new char[strlen(theApp.glob_prefs->GetTempDir())+strlen(partmetfilename)+MAX_PATH];
	sprintf(fullname,"%s\\%s",theApp.glob_prefs->GetTempDir(),partmetfilename);
	char* buffer = nstrdup(partmetfilename);
	buffer[strlen(buffer)-4] = 0;
	CTag* partnametag = new CTag(FT_PARTFILENAME,buffer);
	delete[] buffer;
	taglist.Add(partnametag);
	
	Gap_Struct* gap = new Gap_Struct;
	gap->start = 0;
	gap->end = filesize-1;
	gaplist.AddTail(gap);

	char* partfull = nstrdup(fullname);
	partfull[strlen(partfull)-4] = 0;
	
	
	if (!m_hpartfile.Open(partfull,CFile::modeCreate|CFile::modeReadWrite|CFile::shareDenyWrite|CFile::osSequentialScan)){
		theApp.emuledlg->AddLogLine(false,GetResString(IDS_ERR_CREATEPARTFILE));
		status = PS_ERROR;
	}
	delete[] partfull;
	m_SrcpartFrequency.SetSize(GetPartCount());
	for (uint32 i = 0; i != GetPartCount();i++)
		m_SrcpartFrequency.Add(0);
	paused = false;
	// Before we save file we need to stablish UAP by Tarod
	if (theApp.glob_prefs->IsUAPEnabled()) {
		((CKnownFile*)this)->SetAutoPriority(true) ;
		((CKnownFile*)this)->UpdateUploadAutoPriority() ;
		theApp.emuledlg->sharedfileswnd.sharedfilesctrl.UpdateItem((CKnownFile*)this);
	}
	// End Tarod
	SavePartFile();
}

bool CPartFile::LoadPartFile(char* in_directory,char* in_filename){
	CMap<uint16,uint16,Gap_Struct*,Gap_Struct*> gap_map; // Slugfiller
	transfered = 0;
	partmetfilename = nstrdup(in_filename);
	directory = nstrdup(in_directory);
	char* buffer = new char[strlen(directory)+strlen(partmetfilename)+2];
	sprintf(buffer,"%s\\%s",directory,partmetfilename);
	fullname = buffer;
	CSafeFile file;
	try{
		// readfile data form part.met file
		if (!file.Open(fullname,CFile::modeRead)){
			theApp.emuledlg->AddLogLine(false,GetResString(IDS_ERR_OPENMET),partmetfilename,filename);
			return false;
		}
		uint8 version;
		file.Read(&version,1);
		if (version != PARTFILE_VERSION){
			if (file!= INVALID_HANDLE_VALUE) file.Close();
			theApp.emuledlg->AddLogLine(false,GetResString(IDS_ERR_BADMETVERSION),partmetfilename,filename);
			return false;
		}
		LoadDateFromFile(&file);
		LoadHashsetFromFile(&file,false);

		uint32 tagcount;
		file.Read(&tagcount,4);
		for (uint32 j = 0; j != tagcount;j++){
			CTag* newtag = new CTag(&file);
			switch(newtag->tag->specialtag){
				case FT_FILENAME:{
					if(newtag->tag->stringvalue == NULL) {
						theApp.emuledlg->AddLogLine(true,GetResString(IDS_ERR_METCORRUPT),partmetfilename,filename);
						delete newtag;
						return false;
					}
					filename = nstrdup(newtag->tag->stringvalue);
					delete newtag;
					break;
				}
				case FT_LASTSEENCOMPLETE: {
					lastseencomplete = newtag->tag->intvalue;
					delete newtag;
					break;
				}
				case FT_FILESIZE:{
					filesize = newtag->tag->intvalue;
					delete newtag;
					break;
				}
				case FT_TRANSFERED:{
					transfered = newtag->tag->intvalue;
					delete newtag;
					break;
				}
				case FT_PRIORITY:{
					priority = newtag->tag->intvalue;
					if ((priority==PR_AUTO)&&(!theApp.glob_prefs->IsUAPEnabled())) priority=PR_NORMAL;
					delete newtag;
					if (priority == PR_AUTO) {
						this->SetAutoPriority(true);
						((CKnownFile*)this)->UpdateUploadAutoPriority();
					}
					else
						this->SetAutoPriority(false);
					break;
				}
				case FT_STATUS:{
					paused = newtag->tag->intvalue;
					delete newtag;
					break;
				}
				case FT_ULPRIORITY:{ 
					if ((priority==PR_AUTO)&&(!theApp.glob_prefs->IsUAPEnabled())) priority=PR_NORMAL;
					if ((uint8)newtag->tag->intvalue == PR_AUTO) {
						((CKnownFile*)this)->SetAutoPriority(true);
						((CKnownFile*)this)->UpdateUploadAutoPriority();
					} else {
						((CKnownFile*)this)->SetAutoPriority(false);
						((CKnownFile*)this)->SetPriority((uint8)newtag->tag->intvalue);
					}
					delete newtag;
					break; 
				}
				default:{
					// Start Changes by Slugfiller for better exception handling
					if ((!newtag->tag->specialtag) &&
						(newtag->tag->tagname[0] == FT_GAPSTART ||
							newtag->tag->tagname[0] == FT_GAPEND)){
						Gap_Struct* gap;
						uint16 gapkey = atoi(&newtag->tag->tagname[1]);
						if (!gap_map.Lookup(gapkey, gap))
						{
							gap = new Gap_Struct;
							gap_map.SetAt(gapkey, gap);
							gap->start = -1;
							gap->end = -1;
						}
						if (newtag->tag->tagname[0] == FT_GAPSTART)
							gap->start = newtag->tag->intvalue;
						if (newtag->tag->tagname[0] == FT_GAPEND)
							gap->end = newtag->tag->intvalue-1;
						delete newtag;
					// End Changes by Slugfiller for better exception handling
					}
					else
						taglist.Add(newtag);
				}
					
			}

		}
		if (file != INVALID_HANDLE_VALUE) file.Close();
	}
	catch(CFileException* error){
		OUTPUT_DEBUG_TRACE();
		if (error->m_cause == CFileException::endOfFile)
			theApp.emuledlg->AddLogLine(true,GetResString(IDS_ERR_METCORRUPT),partmetfilename,filename);
		else{
			char buffer[150];
			error->GetErrorMessage(buffer,150);
			theApp.emuledlg->AddLogLine(true,GetResString(IDS_ERR_FILEERROR),partmetfilename,filename,error);
		}
		error->Delete();
		return false;
	}

	// Now to flush the map into the list (Slugfiller)
	for (POSITION pos = gap_map.GetStartPosition(); pos != NULL; ){
		Gap_Struct* gap;
		uint16 gapkey;
		gap_map.GetNextAssoc(pos, gapkey, gap);
		if (gap->start >= 0 && gap->end >=0 && gap->start <= gap->end)
			gaplist.AddTail(gap); // All tags accounted for
		else
			delete gap; // Some of the tags were missing
	}

	//check if this is a backup
	if(stricmp(strrchr(fullname, '.'), ".backup") == 0) {
		char *shorten = strrchr(fullname, '.');
		*shorten = 0;
		fullname = (char*)realloc(fullname, strlen(fullname) + 1);
	}

	// open permanent handle
	char* searchpath = nstrdup(fullname);
	searchpath[strlen(fullname)-4] = 0;
	if (!m_hpartfile.Open(searchpath,CFile::modeReadWrite|CFile::shareDenyWrite|CFile::osSequentialScan)){
		theApp.emuledlg->AddLogLine(false,GetResString(IDS_ERR_FILEOPEN),fullname,filename);
		delete[] searchpath;
		return false;
	}
	delete[] searchpath;
			

	m_SrcpartFrequency.SetSize(GetPartCount());
	for (uint32 i = 0; i != GetPartCount();i++)
		m_SrcpartFrequency.Add(0);
	status = PS_READY;
	// check hashcount, filesatus etc
	if (hashlist.GetCount() < GetPartCount()){
		status = PS_EMPTY;
		hashsetneeded = true;
		return true;
	}
	else {
		hashsetneeded = false;
		bool empty = true;
		for (int i = 0; i != hashlist.GetSize(); i++){
			if (IsComplete(i*PARTSIZE,((i+1)*PARTSIZE)-1))
				empty = false;
		}
		if (empty)
			status = PS_EMPTY;
	}

	if (gaplist.IsEmpty()){	// is this file complete already?
		CompleteFile(false);
		return true;
	}

	// check date of .part file - if its wrong, rehash file
	CFileStatus filestatus;
	m_hpartfile.GetStatus(filestatus);
	if (date != mktime(filestatus.m_mtime.GetLocalTm())){
		theApp.emuledlg->AddLogLine(false,GetResString(IDS_ERR_REHASH),buffer,filename);
		// rehash
		status = PS_WAITINGFORHASH;
		CAddFileThread* addfilethread = (CAddFileThread*) AfxBeginThread(RUNTIME_CLASS(CAddFileThread), THREAD_PRIORITY_NORMAL,0, CREATE_SUSPENDED);
		addfilethread->SetValues(0,directory,m_hpartfile.GetFileName().GetBuffer(),this);
		addfilethread->ResumeThread();
	}

	UpdateCompletedInfos();
	if ( completedsize > transfered )
		m_iGainDueToCompression = completedsize - transfered;
	else if ( completedsize != transfered )
		m_iLostDueToCorruption = transfered - completedsize;
	return true;
}

bool CPartFile::SavePartFile(){
	switch (status){
		case PS_WAITINGFORHASH:
		case PS_HASHING:
			return false;
	}
	FILE* file = 0;
	try{
		//get filedate
		CFileFind ff;
		char* searchpath = nstrdup(fullname);
		searchpath[strlen(fullname)-4] = 0;
		bool end = !ff.FindFile(searchpath,0);
		delete[] searchpath;
		if (!end)
				ff.FindNextFile();
		if (end || ff.IsDirectory())
			throw GetResString(IDS_ERR_PART_FNF);
		CTime lwtime;
		ff.GetLastWriteTime(lwtime);
		date = mktime(lwtime.GetLocalTm());
		ff.Close();
		uint32 lsc = mktime(lastseencomplete.GetLocalTm());
		CString backupName(fullname);
		backupName.Append(".backup");

		remove(backupName);
		rename(fullname, backupName);

		// readfile data form part.met file
		file = fopen(fullname,"wbS");
		if (!file)
			throw GetResString(IDS_ERR_OPENMETFILE);
		//version
		uint8 version = PARTFILE_VERSION;
		fwrite(&version,1,1,file);
		//date
		fwrite(&date,4,1,file);
		//hash
		fwrite(&filehash,16,1,file);
		uint16 parts = hashlist.GetCount();
		fwrite(&parts,2,1,file);
		for (int x = 0; x != parts; x++)
			fwrite(hashlist[x],16,1,file);
		//tags
		uint32 tagcount = taglist.GetCount()+7+(gaplist.GetCount()*2);
		fwrite(&tagcount,4,1,file);
		CTag* nametag = new CTag(FT_FILENAME,filename);
		nametag->WriteTagToFile(file);
		delete nametag;
		CTag* sizetag = new CTag(FT_FILESIZE,filesize);
		sizetag->WriteTagToFile(file);
		delete sizetag;
		CTag* transtag = new CTag(FT_TRANSFERED,transfered);
		transtag->WriteTagToFile(file);
		delete transtag;
		CTag* statustag = new CTag(FT_STATUS,(paused)? 1:0);
		statustag->WriteTagToFile(file);
		delete statustag;
		//UAP Hunter
		CTag* prioritytag ;
		if (IsAutoPrioritized()) {
			uint8 aux_priority = PR_AUTO ;
			prioritytag = new CTag(FT_PRIORITY,aux_priority);
		}
		else
			prioritytag = new CTag(FT_PRIORITY,priority);
		prioritytag->WriteTagToFile(file);
		delete prioritytag;
		//end UAP
		CTag* lsctag = new CTag(FT_LASTSEENCOMPLETE,lsc);
		lsctag->WriteTagToFile(file);
		delete lsctag;

		// Modified by Tarod for UAP
		CTag* ulprioritytag;
		if (((CKnownFile*)this)->IsAutoPrioritized())
			ulprioritytag = new CTag(FT_ULPRIORITY, PR_AUTO);
		else
			ulprioritytag = new CTag(FT_ULPRIORITY,m_iPriority);
		ulprioritytag->WriteTagToFile(file);
		delete ulprioritytag;
		// End by Tarod
		for (uint32 j = 0; j != taglist.GetCount();j++)
			taglist[j]->WriteTagToFile(file);
		//gaps
		char* namebuffer = new char[10];
		char* number = &namebuffer[1];
		uint16 i_pos = 0;
		for (POSITION pos = gaplist.GetHeadPosition();pos != 0;gaplist.GetNext(pos)){
			itoa(i_pos,number,10);
			namebuffer[0] = FT_GAPSTART;
			CTag* gapstarttag = new CTag(namebuffer,gaplist.GetAt(pos)->start);
			gapstarttag->WriteTagToFile(file);
			// gap start = first missing byte but gap ends = first non-missing byte in edonkey
			// but I think its easier to user the real limits
			namebuffer[0] = FT_GAPEND;
			CTag* gapendtag = new CTag(namebuffer,(gaplist.GetAt(pos)->end)+1);
			gapendtag->WriteTagToFile(file);
			delete gapstarttag;
			delete gapendtag;
			i_pos++;
		}
		delete[] namebuffer;

		if (ferror(file))
			throw CString("unexpected write error");

		remove(backupName);
	}
	catch(char* error){
		OUTPUT_DEBUG_TRACE();
		if (file)
			fclose(file);
		theApp.emuledlg->AddLogLine(false,GetResString(IDS_ERR_SAVEMET),error,partmetfilename,filename);
		return false;
	}
	catch(CString error){
		OUTPUT_DEBUG_TRACE();
		if (file)
			fclose(file);
		theApp.emuledlg->AddLogLine(false,GetResString(IDS_ERR_SAVEMET),error.GetBuffer(),partmetfilename,filename);
		return false;
	}
	fclose(file);
	return true;
}

void CPartFile::PartFileHashFinished(CKnownFile* result){
	newdate = true;
	bool errorfound = false;
	for (uint32 i = 0; i != hashlist.GetSize(); i++){
		if (IsComplete(i*PARTSIZE,((i+1)*PARTSIZE)-1)){
			if (!(result->GetPartHash(i) && !memcmp(result->GetPartHash(i),this->GetPartHash(i),16))){
				theApp.emuledlg->AddDebugLogLine(false,GetResString(IDS_ERR_FOUNDCORRUPTION),i+1,filename);		
				AddGap(i*PARTSIZE,((((i+1)*PARTSIZE)-1) >= filesize) ? filesize-1 : ((i+1)*PARTSIZE)-1);
				errorfound = true;
			}
		}
	}
	delete result;
	if (!errorfound){
		if (status == PS_COMPLETING){
			theApp.emuledlg->AddLogLine(true,"DEBUG FCB: Completing file-hashing ok (%s)", this->GetFileName());
			CompleteFile(true);
			return;
		}
		else
			theApp.emuledlg->AddLogLine(false,GetResString(IDS_HASHINGDONE),filename);
	}
	else{
		status = PS_READY;
		theApp.emuledlg->AddLogLine(true,"DEBUG FCB: Completing file-hashing Failed (%s)", this->GetFileName());
		SavePartFile();
		return;
	}
	theApp.emuledlg->AddLogLine(true,"DEBUG FCB: Completing file-hashing (Should never have gotten here) (%s)", this->GetFileName());
	status = PS_READY;
	SavePartFile();
	theApp.sharedfiles->SafeAddKFile(this);
}

void CPartFile::AddGap(uint32 start, uint32 end){
	POSITION pos1, pos2;
	for (pos1 = gaplist.GetHeadPosition();(pos2 = pos1) != NULL;){
		Gap_Struct* cur_gap = gaplist.GetNext(pos1);
		if (cur_gap->start >= start && cur_gap->end <= end){ // this gap is inside the new gap - delete
			gaplist.RemoveAt(pos2);
			delete cur_gap;
			continue;
		}
		else if (cur_gap->start >= start && cur_gap->start <= end){// a part of this gap is in the new gap - extend limit and delete
			end = cur_gap->end;
			gaplist.RemoveAt(pos2);
			delete cur_gap;
			continue;
		}
		else if (cur_gap->end <= end && cur_gap->end >= start){// a part of this gap is in the new gap - extend limit and delete
			start = cur_gap->start;
			gaplist.RemoveAt(pos2);
			delete cur_gap;
			continue;
		}
		else if (start >= cur_gap->start && end <= cur_gap->end){// new gap is already inside this gap - return
			return;
		}
	}
	Gap_Struct* new_gap = new Gap_Struct;
	new_gap->start = start;
	new_gap->end = end;
	gaplist.AddTail(new_gap);
	theApp.emuledlg->transferwnd.downloadlistctrl.UpdateItem(this);
	newdate = true;
}

bool CPartFile::IsComplete(uint32 start, uint32 end){
	if (end >= filesize)
		end = filesize-1;
	for (POSITION pos = gaplist.GetHeadPosition();pos != 0;gaplist.GetNext(pos)){
		Gap_Struct* cur_gap = gaplist.GetAt(pos);
		if ((cur_gap->start >= start && cur_gap->end <= end)||(cur_gap->start >= start 
			&& cur_gap->start <= end)||(cur_gap->end <= end && cur_gap->end >= start)
			||(start >= cur_gap->start && end <= cur_gap->end)){
				return false;	
			}
	}
	return true;
}

bool CPartFile::IsPureGap(uint32 start, uint32 end){
	if (end >= filesize)
		end = filesize-1;
	for (POSITION pos = gaplist.GetHeadPosition();pos != 0;gaplist.GetNext(pos)){
		Gap_Struct* cur_gap = gaplist.GetAt(pos);
		if (start >= cur_gap->start  && end <= cur_gap->end ){
			return true;
		}
	}
	return false;
}

bool CPartFile::IsAlreadyRequested(uint32 start, uint32 end){
	for (POSITION pos =  requestedblocks_list.GetHeadPosition();pos != 0; requestedblocks_list.GetNext(pos)){
		Requested_Block_Struct* cur_block =  requestedblocks_list.GetAt(pos);
		if ((start <= cur_block->EndOffset) && (end >= cur_block->StartOffset))
			return true;
	}
	return false;
}

/* rewritten
bool CPartFile::GetNextEmptyBlockInPart(uint16 partnumber,Requested_Block_Struct* result){
	for (uint32 i = 0; i != ceil((float)PARTSIZE/BLOCKSIZE);i++){
		uint32 start = (PARTSIZE*partnumber) + i*BLOCKSIZE;
		uint32 end	 = (PARTSIZE*partnumber) + ((i+1)*BLOCKSIZE)-1;
		if (end >= (uint32)PARTSIZE*(partnumber+1))
			end = (PARTSIZE*(partnumber+1))-1;
		if (start >= GetFileSize())
			break;
		if (end >= GetFileSize())
			end = GetFileSize()-1;
		if ( (!IsComplete(start,end)) && !IsAlreadyRequested(start,end)){
			if (result){
				result->StartOffset = start;
				result->EndOffset = end;
				memcpy(result->FileID,GetFileHash(),16);
			}
			return true;
		}
	}
	return false;
}
*/
bool CPartFile::GetNextEmptyBlockInPart(uint16 partNumber, Requested_Block_Struct *result)
{
	Gap_Struct *firstGap;
	Gap_Struct *currentGap;
	uint32 end;
	uint32 blockLimit;

	// Find start of this part
	uint32 partStart = (PARTSIZE * partNumber);
	uint32 start = partStart;

	// What is the end limit of this block, i.e. can't go outside part (or filesize)
	uint32 partEnd = (PARTSIZE * (partNumber + 1)) - 1;
	if (partEnd >= GetFileSize())
		partEnd = GetFileSize() - 1;

	// Loop until find a suitable gap and return true, or no more gaps and return false
	while (true)
	{
		firstGap = NULL;

		// Find the first gap from the start position
		for (POSITION pos = gaplist.GetHeadPosition(); pos != 0; gaplist.GetNext(pos))
		{
			currentGap = gaplist.GetAt(pos);
			// Want gaps that overlap start<->partEnd
			if ((currentGap->start <= partEnd) && (currentGap->end >= start))
			{
				// Is this the first gap?
				if ((firstGap == NULL) || (currentGap->start < firstGap->start))
					firstGap = currentGap;
			}
		}

		// If no gaps after start, exit
		if (firstGap == NULL)
			return false;

		// Update start position if gap starts after current pos
		if (start < firstGap->start)
			start = firstGap->start;

		// If this is not within part, exit
		if (start > partEnd)
			return false;

		// Find end, keeping within the max block size and the part limit
		end = firstGap->end;
		blockLimit = partStart + (BLOCKSIZE * (((start - partStart) / BLOCKSIZE) + 1)) - 1;
		if (end > blockLimit)
			end = blockLimit;
		if (end > partEnd)
			end = partEnd;
    
		// If this gap has not already been requested, we have found a valid entry
		if (!IsAlreadyRequested(start, end))
		{
			// Was this block to be returned
			if (result != NULL)
			{
				result->StartOffset = start;
				result->EndOffset = end;
				memcpy(result->FileID, GetFileHash(), 16);
				result->transferred = 0;
			}
			return true;
		}
		else
		{
			// Reposition to end of that gap
			start = end + 1;
		}

		// If tried all gaps then break out of the loop
		if (end == partEnd)
			break;
	}

	// No suitable gap found
	return false;
}

void CPartFile::FillGap(uint32 start, uint32 end){
	POSITION pos1, pos2;
	for (pos1 = gaplist.GetHeadPosition();(pos2 = pos1) != NULL;){
		Gap_Struct* cur_gap = gaplist.GetNext(pos1);
		if (cur_gap->start >= start && cur_gap->end <= end){ // our part fills this gap completly
			gaplist.RemoveAt(pos2);
			delete cur_gap;
			continue;
		}
		else if (cur_gap->start >= start && cur_gap->start <= end){// a part of this gap is in the part - set limit
			cur_gap->start = end+1;
		}
		else if (cur_gap->end <= end && cur_gap->end >= start){// a part of this gap is in the part - set limit
			cur_gap->end = start-1;
		}
		else if (start >= cur_gap->start && end <= cur_gap->end){
			uint32 buffer = cur_gap->end;
			cur_gap->end = start-1;
			cur_gap = new Gap_Struct;
			cur_gap->start = end+1;
			cur_gap->end = buffer;
			gaplist.InsertAfter(pos1,cur_gap);
			break; // [Lord KiRon]
		}
	}

	UpdateCompletedInfos();
	theApp.emuledlg->transferwnd.downloadlistctrl.UpdateItem(this);
	newdate = true;
	// Barry - The met file is now updated in FlushBuffer()
	//SavePartFile();
}

void CPartFile::UpdateCompletedInfos() {

   	uint32 allgaps = 0; 

	for (POSITION pos = gaplist.GetHeadPosition();pos !=  0;gaplist.GetNext(pos)){ 
		Gap_Struct* cur_gap = gaplist.GetAt(pos); 
		allgaps += cur_gap->end - cur_gap->start;
	}

	if ((gaplist.GetCount() || requestedblocks_list.GetCount()))
	{ 
			percentcompleted = ((1.0f-(float)allgaps/filesize)) * 100; 
			completedsize = (uint32)((filesize-(allgaps+1))); 
	} else {
		percentcompleted = 100; completedsize=filesize;
	}
}

void CPartFile::DrawStatusBar(CDC* dc, RECT* rect, bool bFlat){ 
	COLORREF crProgress;
	COLORREF crHave;
	COLORREF crPending;
	COLORREF crMissing = RGB(255, 0, 0);

	if(bFlat) {
		crProgress = RGB(0, 150, 0);
		crHave = RGB(0, 0, 0);
		crPending = RGB(255,208,0);
	} else {
		crProgress = RGB(0, 224, 0);
		crHave = RGB(104, 104, 104);
		crPending = RGB(255, 208, 0);
	}

	s_ChunkBar.SetHeight(rect->bottom - rect->top);
	s_ChunkBar.SetWidth(rect->right - rect->left);
	s_ChunkBar.SetFileSize(filesize);
	s_ChunkBar.Fill(crHave);

	uint32 allgaps = 0;

	if(status == PS_COMPLETE || status == PS_COMPLETING) {
		s_ChunkBar.FillRange(0, filesize, crProgress);
		s_ChunkBar.Draw(dc, rect->left, rect->top, bFlat);
		percentcompleted = 100; completedsize=filesize;
		return;
	}

	// red gaps
	for (POSITION pos = gaplist.GetHeadPosition();pos !=  0;gaplist.GetNext(pos)){
		Gap_Struct* cur_gap = gaplist.GetAt(pos);
		allgaps += cur_gap->end - cur_gap->start;
		bool gapdone = false;
		uint32 gapstart = cur_gap->start;
		uint32 gapend = cur_gap->end;
		for (uint32 i = 0; i != GetPartCount(); i++){
			if (gapstart >= i*PARTSIZE && gapstart <=  (i+1)*PARTSIZE){ // is in this part?
				if (gapend <= (i+1)*PARTSIZE)
					gapdone = true;
				else{
					gapend = (i+1)*PARTSIZE; // and next part
				}
				// paint
				COLORREF color;
				if (m_SrcpartFrequency.GetCount() >= (INT_PTR)i && m_SrcpartFrequency[i])  // frequency?
					color = RGB(0,
					(210-(22*(m_SrcpartFrequency[i]-1)) <  0)? 0:210-(22*(m_SrcpartFrequency[i]-1))
					,255);
				else
					color = crMissing;

				s_ChunkBar.FillRange(gapstart, gapend + 1,  color);

				if (gapdone) // finished?
					break;
				else{
					gapstart = gapend;
					gapend = cur_gap->end;
				}
			}
		}
	}

	// yellow pending parts
	for (POSITION pos = requestedblocks_list.GetHeadPosition();pos !=  0;requestedblocks_list.GetNext(pos))
	{
		Requested_Block_Struct* block =  requestedblocks_list.GetAt(pos);
		s_ChunkBar.FillRange((block->StartOffset + block->transferred), block->EndOffset,  crPending);
	}

	s_ChunkBar.Draw(dc, rect->left, rect->top, bFlat);

	// green progress
	float blockpixel = (float)(rect->right -  rect->left)/((float)filesize);
	RECT gaprect;
	gaprect.top = rect->top;
	gaprect.bottom = gaprect.top + PROGRESS_HEIGHT; // Barry - was 4
	gaprect.left = rect->left;

	if(!bFlat) {
		s_LoadBar.SetWidth((uint32)((float)((float)((filesize-((allgaps==0)?1:allgaps) )-1))*blockpixel + .5f));
		s_LoadBar.Fill(crProgress);
		s_LoadBar.Draw(dc, gaprect.left, gaprect.top, false);
	} else {
		gaprect.right = rect->left+  (uint32)((float)((float)((filesize-((allgaps==0)?1:allgaps))-1))*blockpixel +  .5f);
		dc->FillRect(&gaprect, &CBrush(crProgress));
		//draw gray progress only if flat
		gaprect.left = gaprect.right;
		gaprect.right = rect->right;
		dc->FillRect(&gaprect, &CBrush(RGB(224,224,224)));
	}

	if (	   (gaplist.GetCount() || requestedblocks_list.GetCount())	   )
	{
		percentcompleted = ((1.0f-(float)allgaps/filesize)) * 100;
		completedsize = (uint32)((filesize-(allgaps+1)));
	} else {percentcompleted = 100; completedsize=filesize;}

}

void CPartFile::WritePartStatus(CFile* file){
	uint16 parts = hashlist.GetCount();
	file->Write(&parts,2);
	uint16 done = 0;
	while (done != parts){
		uint8 towrite = 0;
		for (uint32 i = 0;i != 8;i++){
			if (IsComplete(done*PARTSIZE,((done+1)*PARTSIZE)-1))
				towrite |= (1<<i);
			done++;
			if (done == parts)
				break;
		}
		file->Write(&towrite,1);
	}
}

int CPartFile::GetValidSourcesCount() {
	int counter=0;
	POSITION pos1,pos2;
	for (pos1 = srclist.GetHeadPosition();( pos2 = pos1 ) != NULL;){
		srclist.GetNext(pos1);
		CUpDownClient* cur_src = srclist.GetAt(pos2);
		if (cur_src->GetDownloadState()!=DS_ONQUEUE && cur_src->GetDownloadState()!=DS_DOWNLOADING &&
			cur_src->GetDownloadState()!=DS_NONEEDEDPARTS) counter++;
	}
	return counter;
}

uint16 CPartFile::GetNotCurrentSourcesCount(){
		uint16 counter=0;

		POSITION pos1,pos2;
		for (pos1 = srclist.GetHeadPosition();( pos2 = pos1 ) != NULL;){
			srclist.GetNext(pos1);
			CUpDownClient* cur_src = srclist.GetAt(pos2);
			if (cur_src->GetDownloadState()!=DS_ONQUEUE && cur_src->GetDownloadState()!=DS_DOWNLOADING) counter++;
		}
		return counter;
}

uint8 CPartFile::GetStatus(bool ignorepause){
	if ((!paused) || status == PS_ERROR || ignorepause)
		return status;
	else
		return PS_PAUSED;
}

// Added by Tarod -remove no needed sources from download queue
//
// [In]	number_sources_to_remove: Number of sources to be removed from list or 0xffffffff if we want all removed
void CPartFile::RemoveNoNeededSources(uint32 number_sources_to_remove)
{
	POSITION pos1, pos2;
	uint32 removed = 0;

	try {
		for (pos1 = srclist.GetHeadPosition(); (pos2 = pos1) != NULL && removed < number_sources_to_remove ;) {
			srclist.GetNext(pos1);
			CUpDownClient* cur_src = srclist.GetAt(pos2);
			if (cur_src->GetDownloadState() == DS_NONEEDEDPARTS) {
				theApp.downloadqueue->RemoveSource(cur_src);
				removed ++; // Count removed source
				lastpurgetime = ::GetTickCount();
			}
		}
	} catch(...) {
		lastpurgetime = ::GetTickCount();
	}
}

uint32 CPartFile::Process(sint16 reducedownload/*in percent*/){

	// If buffer size exceeds limit, or if not written within time limit, flush data
	if ((m_nTotalBufferData > BUFFER_SIZE_LIMIT) || (GetTickCount() > (m_nLastBufferFlushTime + BUFFER_TIME_LIMIT)))
	{
		// Avoid flushing while copying preview file
		if (!m_bPreviewing)
			FlushBuffer();
	}

	// check if we want new sources from server
	uint16 test = theApp.glob_prefs->GetMaxSourcePerFileSoft();
	if (( (!lastsearchtime) || (::GetTickCount() - lastsearchtime) > SERVERREASKTIME) && theApp.serverconnect->IsConnected()
		&& theApp.glob_prefs->GetMaxSourcePerFileSoft() > GetSourceCount() ){
		//local server
		lastsearchtime = ::GetTickCount();
		Packet* packet = new Packet(OP_GETSOURCES,16);
		memcpy(packet->pBuffer,filehash,16);
		theApp.serverconnect->SendPacket(packet,true);
	}

	transferingsrc = 0;
	datarate = 0;
	POSITION pos1,pos2;
	for (pos1 = srclist.GetHeadPosition();( pos2 = pos1 ) != NULL;){
		srclist.GetNext(pos1);

		CUpDownClient* cur_src = srclist.GetAt(pos2);

			switch (cur_src->GetDownloadState()){
					case DS_DOWNLOADING:{
						transferingsrc++;
						uint32 cur_datarate = cur_src->CalculateDownloadRate();
						datarate += cur_datarate;
						if (reducedownload && cur_src->GetDownloadState() == DS_DOWNLOADING){
							uint32 limit = (uint32)(((float)reducedownload/100)*cur_datarate)/10;		
								if (limit < 1000 && reducedownload == 200)
									limit += 1000;
								else if (limit < 1)
									limit = 1;
								cur_src->socket->SetDownloadLimit(limit);
						}
						else
							cur_src->socket->DisableDownloadLimit();
						break;
					}
					case DS_BANNED:
					case DS_ERROR:
						break;
					case DS_LOWTOLOWIP:	// if we now have a high ip we can ask
						if( ((::GetTickCount() - lastpurgetime) > PURGE_TIME) && (this->GetSourceCount() >= (theApp.glob_prefs->GetMaxSourcePerFile()*.8 )) ){
							theApp.downloadqueue->RemoveSource( cur_src );
							lastpurgetime = ::GetTickCount();
						}
						if (theApp.serverconnect->IsLowID())
							break;
					case DS_NONEEDEDPARTS:{ 
						// Commented by Tarod -handled in a different way... just when receiving sources
						//if( ((::GetTickCount() - lastpurgetime) > PURGE_TIME) && (this->GetSourceCount() >= (theApp.glob_prefs->GetMaxSourcePerFile()*.8 )) ){
						//	if( !cur_src->SwapToAnotherFile( cur_src ) ){
						//		theApp.downloadqueue->RemoveSource( cur_src );
						//		lastpurgetime = ::GetTickCount();
						//	}
						//}
						
						// doubled reasktime for no needed parts - save connections and traffic
						if (!((!cur_src->GetLastAskedTime()) || (::GetTickCount() - cur_src->GetLastAskedTime()) > FILEREASKTIME*2))
							break;
					}
					case DS_ONQUEUE:{
						if (theApp.serverconnect->IsConnected() && ((!cur_src->GetLastAskedTime()) || (::GetTickCount() - cur_src->GetLastAskedTime()) > FILEREASKTIME-20000)){
							cur_src->UDPReaskForDownload();
						}

						// Check Remote queue position
						if ((!cur_src->IsRemoteQueueFull()) && (cur_src->GetRemoteQueueRank() > theApp.glob_prefs->GetRemoteQueueLimit())) {
							theApp.downloadqueue->RemoveSource(cur_src);
						}
					}
					case DS_CONNECTING:
					case DS_TOOMANYCONNS:
					case DS_CONNECTED:
					case DS_NONE:
					case DS_WAITCALLBACK:
						if (theApp.serverconnect->IsConnected() && ((!cur_src->GetLastAskedTime()) || (::GetTickCount() - cur_src->GetLastAskedTime()) > FILEREASKTIME)){
							cur_src->AskForDownload();
						}
						break;
			}
	}


	// swap No needed partfiles if possible
	// Modified by Tarod -now handles A4AF too keeping usage of CPU less and do not forcing files
	// that has No Needed Parts to download letting them swap to another file
	if (((!m_LastNoNeededCheck) || (::GetTickCount() - m_LastNoNeededCheck) > 10000)){
		m_LastNoNeededCheck = ::GetTickCount();
		POSITION pos1,pos2;
		for (pos1 = srclist.GetHeadPosition();( pos2 = pos1 ) != NULL;){
			srclist.GetNext(pos1);
			CUpDownClient* cur_src = srclist.GetAt(pos2);
			if (cur_src->GetDownloadState() == DS_NONEEDEDPARTS) {
				cur_src->SwapToAnotherFile(false);

			// if this file has activated A4AF auto just hang to another file if we can
			} else if (IsA4AFAuto() && 	(cur_src->GetDownloadDatarate() == DS_ONQUEUE || cur_src->GetDownloadDatarate() == DS_LOWTOLOWIP)
				&& memcmp(cur_src->reqfileid, GetFileHash(), 16) != 0) {

					cur_src->HandoverToAnotherFile(this);
			}
		}

		// Added by Tarod -if is A4AF auto then swith every A4AF source to this file
		//if (IsA4AFAuto()) {
		//	theApp.emuledlg->transferwnd.downloadlistctrl.DownloadAllA4AF(this);
		//}
	}


	// calculate datarate, set limit etc.

	count++;
	if (count == 30){
		count = 0;
		theApp.emuledlg->transferwnd.downloadlistctrl.UpdateItem(this);
		if(m_bPercentUpdated == false)
			UpdateCompletedInfos();
		m_bPercentUpdated = false;
	}
	///////////////////////////////////////////////////////
	//for saving/reloading source to/from disk: mr fry

	if(this->GetStatus() < 2 && theApp.downloadqueue->IsSourcesLoadingEnabled()){
		uint32 currentTime = ::GetTickCount();

		if(!this->m_bWasConnectedToServerLastTime && theApp.serverconnect->IsConnected()){
			//if connection-state has switch from: not connected -> connected...
			TRACE("CPartFile::Process : slow loading of sources down if a connection is established (next time sources are loaded for %s: in %.1f sec)\n"
				,this->GetFileName(),(float(this->m_iLoadingSourcesPartialTimeInterval)/1000));
			this->m_iLastTimeSourcesLoadedPartial = ::GetTickCount();
		}

		//load sources in slow mode (while they weren't completely loaded yet)?
		if(!this->m_bLoadedSourcesCompletely && theApp.serverconnect->IsConnected() && !theApp.downloadqueue->IsMaxClientsExceeded()){
			if(currentTime - this->m_iLastTimeSourcesLoadedPartial > this->m_iLoadingSourcesPartialTimeInterval){
				// makes room for more loaded sources
				int source_count_plus_needed = GetSourceCount() + GetLoadSourcesAtOnceLimit();
				if (source_count_plus_needed > theApp.glob_prefs->GetMaxSourcePerFile()
					&& ((lastpurgetime == 0) || (::GetTickCount() - lastpurgetime) > PURGE_TIME)) {

					RemoveNoNeededSources(source_count_plus_needed - theApp.glob_prefs->GetMaxSourcePerFile());
				}

				//is a permanent opened CStoredSources-object for each part-file wanted?
				if(theApp.downloadqueue->KeepStoredSourcesOpen())
					this->AddFromStoredSources(this->m_stscrStoringSources,false,this->GetLoadSourcesAtOnceLimit());
				else
					this->LoadAndAddSources(false,false,this->GetLoadSourcesAtOnceLimit());
			}
		}
		//(re-) load sources?
		else if((currentTime - this->m_iLastTimeSourcesLoaded) > this->m_iLoadingSourcesTimeInterval  
				&& theApp.serverconnect->IsConnected()
				&& !theApp.downloadqueue->IsMaxClientsExceeded()){
			TRACE("Reloading all sources for %s\n",this->GetFileName());

			// Added by Tarod -before reload purge outdated and no needed sources
			// remember that this only occurs first time -first group of loaded sources
			this->m_stscrStoringSources->DeleteOutdatedSources();
			this->m_stscrStoringSources->SortSourcesByPartsNDate();
			this->m_stscrStoringSources->ReduceSourcesTo(this->m_stscrStoringSources->GetMaxSourceEntries());
			
			// makes room for more loaded sources
			int source_count_plus_needed = GetSourceCount() + GetLoadSourcesAtOnceLimit();
			if (source_count_plus_needed > theApp.glob_prefs->GetMaxSourcePerFile()) {
				RemoveNoNeededSources(source_count_plus_needed - theApp.glob_prefs->GetMaxSourcePerFile());
			}

			//is a permanent opened CStoredSources-object for each part-file wanted?
			if(theApp.downloadqueue->KeepStoredSourcesOpen()) {
				//as in the previous if(), but with AddFrom..(..,true); -> reset=true means, begin again to load all sources from stored-sources
				this->AddFromStoredSources(this->m_stscrStoringSources,false,this->GetLoadSourcesAtOnceLimit(),true);
			} else
				this->LoadAndAddSources(false,false,this->GetLoadSourcesAtOnceLimit(),true);
		}
		//save sources?
		if((currentTime - this->m_iLastTimeSourcesSaved) > this->m_iSavingSourcesTimeInterval
			&& this->m_bLoadedSourcesCompletely){

			//only save to disk now, if there is no permantent opened CStoredSources-object for this part-file
			if(!theApp.downloadqueue->KeepStoredSourcesOpen()){
				TRACE("(automatically) saving sources for %s to disk\n",this->GetFileName());
				this->SaveSources(true);
			}
			else if(this->m_stscrStoringSources != NULL){//sort, so we get the best sources first, the next time they're loaded
				//if (!this->m_stscrStoringSources->IsRareFile()) this->m_stscrStoringSources->DeleteOutdatedSources();
				this->m_stscrStoringSources->SortSourcesByPartsNDate();
				this->SaveSources(false);
				//this->m_stscrStoringSources->ReduceSourcesTo(this->m_stscrStoringSources->GetMaxSourceEntries());
			}

			// Added by Tarod fixed bug of CPU usage (¡¡¡por fin, me cago en la puta hostia, joder!!!)
			this->m_iLastTimeSourcesSaved = ::GetTickCount();
		}
        
		this->m_bWasConnectedToServerLastTime = theApp.serverconnect->IsConnected();
	}//end: if(status)
	////////////////////////////////////////////////////////

	return datarate;
}

void CPartFile::AddSources(CMemFile* sources,uint32 serverip, uint16 serverport){
	uint8 count;
	uint8 debug_lowiddropped = 0;
	uint8 debug_possiblesources = 0;
	sources->Read(&count,1);

	// Added by Tarod - if there is needed place for more sources just remove NNS
	int source_count_plus_new = GetSourceCount() + count;
	if (source_count_plus_new > theApp.glob_prefs->GetMaxSourcePerFile()
		&& ((lastpurgetime == 0) || (::GetTickCount() - lastpurgetime) > PURGE_TIME)) {

		RemoveNoNeededSources(source_count_plus_new - theApp.glob_prefs->GetMaxSourcePerFile());
	}

	for (int i = 0;i != count;i++){
		uint32 userid;
		sources->Read(&userid,4);
		uint16 port;
		sources->Read(&port,2);
		// check first if we are this source

		// MOD Note: Do not change this part - Merkur
		if (theApp.serverconnect->GetClientID() < 16777216 && theApp.serverconnect->IsConnected()){
			if ((theApp.serverconnect->GetClientID() == userid) && inet_addr(theApp.serverconnect->GetCurrentServer()->GetFullIP()) == serverip)
				continue;
		}
		else if (theApp.serverconnect->GetClientID() == userid)
			continue;
		else if (userid < 16777216 && !theApp.serverconnect->IsLocalServer(serverip,serverport)){
			debug_lowiddropped++;
			continue;
		}
		// MOD Note - end

		if( theApp.glob_prefs->GetMaxSourcePerFile() > this->GetSourceCount() ){
			debug_possiblesources++;
			CUpDownClient* newsource = new CUpDownClient(port,userid,serverip,serverport,this);
			theApp.downloadqueue->CheckAndAddSource(this,newsource);
		}
	}
	theApp.emuledlg->AddDebugLogLine(false,"RCV: %i sources from server, %i low id dropped, %i possible sources",count,debug_lowiddropped,debug_possiblesources);
}

void CPartFile::NewSrcPartsInfo(){
	if (m_SrcpartFrequency.GetCount() != GetPartCount()){
		m_SrcpartFrequency.RemoveAll();
		m_SrcpartFrequency.SetSize(0);
		for (int i = 0; i != GetPartCount();i++)
			m_SrcpartFrequency.Add(0);
	}
	else{
		for (int i = 0; i != GetPartCount();i++)
			m_SrcpartFrequency[i] = 0;
	}
	for (POSITION pos = srclist.GetHeadPosition();pos != 0;srclist.GetNext(pos)){
		for (int i = 0; i != GetPartCount(); i++){
			if (srclist.GetAt(pos)->IsPartAvailable(i))
				m_SrcpartFrequency[i] +=1;
		}
	}
	theApp.emuledlg->transferwnd.downloadlistctrl.UpdateItem(this);
}

bool CPartFile::GetNextRequestedBlock(CUpDownClient* sender,Requested_Block_Struct** newblocks,uint16* count){
        uint16 requestedCount = *count;
        uint16 newblockcount = 0;
        uint8* partsav = sender->GetPartStatus();
        *count = 0;
        uint16 randomness;
        CList<int,int> liGoodParts;
        CList<int,int> liPossibleParts;
        bool finished = false;
        while (!finished)
        {
                // Need to empty lists to avoid infinite loop when file is smaller than 180k
                // Otherwise it would keep looping to find 3 blocks, there is only one and it is requested 
                liGoodParts.RemoveAll();
                liPossibleParts.RemoveAll();
                // Barry - Top priority should be to continue downloading from current blocks (if anything left to download)
                bool foundPriorityPart = false;
                CString gettingParts;
                sender->ShowDownloadingParts(&gettingParts);
                
                for (int i=(gettingParts.GetLength()-1); i>=0; i--)
                {
                        if ((gettingParts.GetAt(i) == 'Y') && (GetNextEmptyBlockInPart(i, 0)))
                        {
                                liGoodParts.AddHead(i);
                                foundPriorityPart = true;
                        }
                }
                // Barry - Give priorty to end parts of archives and movies
                if ((!foundPriorityPart) && (IsArchive() || IsMovie()) && theApp.glob_prefs->GetPreviewPrio())
                {
                        uint32 partCount = GetPartCount();
                        // First part
                        if (sender->IsPartAvailable(0) && GetNextEmptyBlockInPart(0, 0))
                        {
                                liGoodParts.AddHead(0);
                                foundPriorityPart = true;
                        }
                        else if ((partCount > 1))
                        {
                                // Last part
                                if (sender->IsPartAvailable(partCount-1) && GetNextEmptyBlockInPart(partCount-1, 0))
                                {
                                        liGoodParts.AddHead(partCount-1);
                                        foundPriorityPart = true;
                                }
                                // Barry - Better to get rarest than these, add to list, but do not exclude all others.
                                // These get priority over other parts with same availability.
                                else if (partCount > 2)
                                {
                                        // Second part
                                        if (sender->IsPartAvailable(1) && GetNextEmptyBlockInPart(1, 0))
                                                liGoodParts.AddHead(1);
                                        // Penultimate part
                                        else if (sender->IsPartAvailable(partCount-2) && GetNextEmptyBlockInPart(partCount-2, 0))
                                                liGoodParts.AddHead(partCount-2);
                                }
                        }
                }
                if (!foundPriorityPart)
                {
                        randomness = (uint16)ROUND(((float)rand()/RAND_MAX)*(GetPartCount()-1));
                        for (uint16 i = 0;i != GetPartCount();i++){
                                if (sender->IsPartAvailable(randomness))
                                {                                        
										if (partsav[randomness] && !IsComplete(randomness*PARTSIZE,((randomness+1)*PARTSIZE)-1)){
                                                /*if (IsCorruptedPart(randomness)){
                                                        if (GetNextEmptyBlockInPart(randomness,0)){
                                                                goodpart = randomness;
                                                                break;
                                                        }
                                                }
                                                else */ 
                                                if (IsPureGap(randomness*PARTSIZE,((randomness+1)*PARTSIZE)-1))
                                                {
                                                        if (GetNextEmptyBlockInPart(randomness,0))
                                                                liPossibleParts.AddHead(randomness);
                                                }
                                                else if (GetNextEmptyBlockInPart(randomness,0))
                                                        liGoodParts.AddTail(randomness); // Barry - Add after archive/movie entries
                                        }
                                }
                                randomness++;
                                if (randomness == GetPartCount())
                                        randomness = 0;
                        }
                }
                CList<int,int>* usedlist;
                if (!liGoodParts.IsEmpty())
                        usedlist = &liGoodParts;
                else if (!liPossibleParts.IsEmpty())
                        usedlist = &liPossibleParts;
                else{
                        if (!newblockcount){
                                return false;
                        }
                        else
                                break;
                }
                uint16 nRarest = 0xFFFF;
                uint16 usedpart = usedlist->GetHead();
                for (POSITION pos = usedlist->GetHeadPosition();pos != 0;usedlist->GetNext(pos)){
                        if (m_SrcpartFrequency.GetCount() >= usedlist->GetAt(pos)
                        && m_SrcpartFrequency[usedlist->GetAt(pos)] < nRarest){
                                nRarest = m_SrcpartFrequency[usedlist->GetAt(pos)];
                                usedpart = usedlist->GetAt(pos);
                        }
                }
                while (true){
                        Requested_Block_Struct* block = new Requested_Block_Struct;
                        if (GetNextEmptyBlockInPart(usedpart,block)){
                                requestedblocks_list.AddTail(block);
                                newblocks[newblockcount] = block;
                                newblockcount++;
                                if (newblockcount == requestedCount){
                                        finished = true;
                                        break;
                                }
                        }
                        else
                        {
                                delete block;
                                break;
                        }
                }
        } //wend
        *count = newblockcount;
        return true;
}

void  CPartFile::RemoveBlockFromList(uint32 start,uint32 end){
	POSITION pos1,pos2;
	for (pos1 = requestedblocks_list.GetHeadPosition(); ( pos2 = pos1 ) != NULL; ){
	   requestedblocks_list.GetNext(pos1);
		if (requestedblocks_list.GetAt(pos2)->StartOffset <= start && requestedblocks_list.GetAt(pos2)->EndOffset >= end)
			requestedblocks_list.RemoveAt(pos2);
	}
}

void CPartFile::RemoveAllRequestedBlocks(void)
{
	requestedblocks_list.RemoveAll();
}

void CPartFile::CompleteFile(bool bIsHashingDone){
	theApp.emuledlg->AddLogLine(true,"DEBUG FCB: Starting to complete file. (Flag:%i) (%s)", bIsHashingDone, this->GetFileName());
	if( this->srcarevisible )
		theApp.emuledlg->transferwnd.downloadlistctrl.HideSources(this);
	status = PS_COMPLETING;
	if (!bIsHashingDone){
		datarate = 0;
		char* partfileb = nstrdup(partmetfilename);
		partfileb[strlen(partmetfilename)-4] = 0;
		CAddFileThread* addfilethread = (CAddFileThread*) AfxBeginThread(RUNTIME_CLASS(CAddFileThread), THREAD_PRIORITY_BELOW_NORMAL,0, CREATE_SUSPENDED);
		addfilethread->SetValues(0,theApp.glob_prefs->GetTempDir(),partfileb,this);
		addfilethread->ResumeThread();	
		delete[] partfileb;
		return;
	}
	else{
		StopFile();
		char* partfilename = nstrdup(fullname);
		partfilename[strlen(fullname)-4] = 0;
		char* newfilename = nstrdup(GetFileName());
		strcpy(newfilename, (LPCTSTR)theApp.StripInvalidFilenameChars(newfilename));
		char* newname = new char[strlen(newfilename)+strlen(theApp.glob_prefs->GetIncomingDir())+MAX_PATH];
		sprintf(newname,"%s\\%s",theApp.glob_prefs->GetIncomingDir(),newfilename);

		// close permanent handle
		//if (m_hpartfile != INVALID_HANDLE_VALUE) m_hpartfile.Close();
		if (m_hpartfile.m_hFile != INVALID_HANDLE_VALUE) m_hpartfile.Close(); // m_hpartfile isn't the handle to the file...

		bool renamed = false;
		if(PathFileExists(newname)) {
			renamed = true;
			int namecount = 0;

			size_t length = strlen(newfilename);
			ASSERT(length != 0); //name should never be 0

			//the file extension
			char *ext = strrchr(newfilename, '.');
			if(ext == NULL)
				ext = newfilename + length;

			char *last = ext;  //new end is the file name before extension
			last[0] = 0;  //truncate file name

			//serch for matching ()s and check if it contains a number
			if((ext != newfilename) && (strrchr(newfilename, ')') + 1 == last)) {
				char *first = strrchr(newfilename, '(');
				if(first != NULL) {
					first++;
					bool found = true;
					for(char *step = first; step < last - 1; step++)
						if(*step < '0' || *step > '9') {
							found = false;
							break;
						}
					if(found) {
						namecount = atoi(first);
						last = first - 1;
						last[0] = 0;  //truncate again
					}
				}
			}

			CString strTestName;
			do {
				namecount++;
				strTestName.Format("%s\\%s(%d).%s", theApp.glob_prefs->GetIncomingDir(),
					newfilename, namecount, min(ext + 1, newfilename + length));
			} while(PathFileExists(strTestName));
			delete[] newname;
			newname = nstrdup(strTestName);
		}
		delete newfilename;

		if (rename(partfilename,newname)){
			delete[] partfilename;
			delete[] newname;
			theApp.emuledlg->AddLogLine(true,GetResString(IDS_ERR_COMPLETIONFAILED),GetFileName());
			paused = true;
			status = PS_ERROR;
			theApp.downloadqueue->StartNextFile();
			return;
		}
		if (remove(fullname))
			theApp.emuledlg->AddLogLine(true,GetResString(IDS_ERR_DELETEFAILED),fullname);
		////////////////////////////////
		//remove *.src file (thx to brotbuexe)
		CString srcName="";
		srcName.Format("%s\\%s.stsrc",theApp.glob_prefs->GetTempDir(),this->GetPartMetFileName());
		if (remove(srcName))
			if (errno != ENOENT) 
				theApp.emuledlg->AddLogLine(true,"Failed to delete %s, you will need to do this by hand",srcName);
		/////////////////////////////////

		delete[] partfilename;
		delete[] fullname;
		fullname = newname;
		delete[] directory;
		directory = nstrdup(theApp.glob_prefs->GetIncomingDir());
		status = PS_COMPLETE;
		paused = false;
		theApp.emuledlg->AddLogLine(true,GetResString(IDS_DOWNLOADDONE),GetFileName());
		theApp.emuledlg->ShowNotifier(GetResString(IDS_TBN_DOWNLOADDONE)+"\n"+GetFileName(), TBN_DLOAD);
		if (renamed)
			theApp.emuledlg->AddLogLine(true, GetResString(IDS_DOWNLOADRENAMED), strrchr(newname, '\\') + 1);
		theApp.knownfiles->SafeAddKFile(this);
		((CKnownFile*)this)->SetAutoPriority(false) ; //UAP
		//((CKnownFile*)this)->SetPriority(PR_NORMAL) ; //UAP
		theApp.downloadqueue->RemoveFile(this);
		theApp.emuledlg->transferwnd.downloadlistctrl.UpdateItem(this);
		SHAddToRecentDocs(SHARD_PATH, fullname); // zegzav (added)

		// Barry - Just in case
		transfered = filesize;
		theApp.downloadqueue->StartNextFile();

	}
}

void  CPartFile::RemoveAllSources(bool bTryToSwap){
	//TODO transfer sources to other downloading files if possible
	POSITION pos1,pos2;
	for( pos1 = srclist.GetHeadPosition(); ( pos2 = pos1 ) != NULL; ){
		srclist.GetNext(pos1);
		if (bTryToSwap){
			if (!srclist.GetAt(pos2)->SwapToAnotherFile(true))
				theApp.downloadqueue->RemoveSource(srclist.GetAt(pos2));
		}
		else
			theApp.downloadqueue->RemoveSource(srclist.GetAt(pos2));
	}
}

void CPartFile::DeleteFile(){
	ASSERT ( !m_bPreviewing );

	// Barry - Need to tell any connected clients to stop sending the file
	PauseFile();

	theApp.sharedfiles->RemoveFile(this);
	theApp.downloadqueue->RemoveFile(this);
	theApp.emuledlg->transferwnd.downloadlistctrl.RemoveFile(this);
	RemoveAllSources(true);

	//if (m_hpartfile != INVALID_HANDLE_VALUE) m_hpartfile.Close();
	if (m_hpartfile.m_hFile != INVALID_HANDLE_VALUE) m_hpartfile.Close(); // m_hpartfile isn't the handle to the file...

	if (remove(fullname))
		theApp.emuledlg->AddLogLine(true,GetResString(IDS_ERR_DELETE),fullname);
	char* partfilename = nstrdup(fullname);
	partfilename[strlen(fullname)-4] = 0;
	if (remove(partfilename))
		theApp.emuledlg->AddLogLine(true,GetResString(IDS_ERR_DELETE),partfilename);

	////////////////////////////////
	//remove *.src file (thx to brotbuexe)
	CString srcName="";
	srcName.Format("%s\\%s.stsrc",theApp.glob_prefs->GetTempDir(),this->GetPartMetFileName());
	if (remove(srcName))
		if (errno != ENOENT) 
			theApp.emuledlg->AddLogLine(true,"Failed to delete %s, you will need to do this by hand",srcName);
	/////////////////////////////////

	delete[] partfilename;
	delete this;
}

bool CPartFile::HashSinglePart(uint16 partnumber)
{
	if ((GetHashCount() <= partnumber) && (GetPartCount() > 1)){
		theApp.emuledlg->AddLogLine(true,GetResString(IDS_ERR_HASHERRORWARNING),GetFileName());
		return true;
	}
	else if(!GetPartHash(partnumber) && GetPartCount() != 1){
		theApp.emuledlg->AddLogLine(true,GetResString(IDS_ERR_INCOMPLETEHASH),GetFileName());
		return true;		
	}
	else{
		uchar hashresult[16];
		m_hpartfile.Seek(PARTSIZE*partnumber,0);
		uint32 length = PARTSIZE;
		if (PARTSIZE*(partnumber+1) > m_hpartfile.GetLength())
			length = (m_hpartfile.GetLength() - (PARTSIZE*partnumber));
		CreateHashFromFile(&m_hpartfile,length,hashresult);

		if (GetPartCount() > 1){
			if (memcmp(hashresult,GetPartHash(partnumber),16))
				return false;
			else
				return true;
		}
		else{
			if (memcmp(hashresult,filehash,16))
				return false;
			else
				return true;
		}
	}
}

bool CPartFile::IsCorruptedPart(uint16 partnumber){
	return corrupted_list.Find(partnumber);
}

bool CPartFile::IsMovie(){
	//TODO use filetags instead ststr
	return (strstr(GetFileName(),".avi") || strstr(GetFileName(),".mpg") || strstr(GetFileName(),".mpeg") || strstr(GetFileName(),".ogm"));
}

// Barry - Also want to preview zip/rar files
bool CPartFile::IsArchive()
{
	CString extension = CString(GetFileName()).Right(4);
	return ((extension.CompareNoCase(".zip") == 0) || (extension.CompareNoCase(".rar") == 0));
}

void CPartFile::SetPriority(uint8 np){
	//UAP
	if (np == PR_AUTO)
		SetAutoPriority(true) ;
	else
	priority = np;
	//end UAP
	theApp.downloadqueue->SortByPriority();
	theApp.emuledlg->transferwnd.downloadlistctrl.UpdateItem(this);
}

void CPartFile::StopFile(){
	// Barry - Need to tell any connected clients to stop sending the file
	PauseFile();

	RemoveAllSources(true);
	paused = true;
	datarate = 0;
	transferingsrc = 0;
	theApp.emuledlg->transferwnd.downloadlistctrl.UpdateItem(this);
}

void CPartFile::PauseFile(){
	if (status==PS_COMPLETE || status==PS_COMPLETING) return;
	Packet* packet = new Packet(OP_CANCELTRANSFER,0);
	POSITION pos1,pos2;
	for( pos1 = srclist.GetHeadPosition(); ( pos2 = pos1 ) != NULL; ){
		srclist.GetNext(pos1);
		CUpDownClient* cur_src = srclist.GetAt(pos2);
		if (cur_src->GetDownloadState() == DS_DOWNLOADING){
			cur_src->socket->SendPacket(packet,false,true);
			cur_src->SetDownloadState(DS_ONQUEUE);
		}
	}
	delete packet;
	paused = true;
	datarate = 0;
	transferingsrc = 0;
	theApp.emuledlg->transferwnd.downloadlistctrl.UpdateItem(this);
}

void CPartFile::ResumeFile(){
	if (status==PS_COMPLETE || status==PS_COMPLETING) return;
	paused = false;
	lastsearchtime = 0;
	theApp.emuledlg->transferwnd.downloadlistctrl.UpdateItem(this);
	
	SetLoadSourcesTimeInterval(2 * 60 * 1000) ; // Added by Tarod (to load exchanged sources)
}

CString CPartFile::getPartfileStatus(){ 
	CString mybuffer=""; 
	if (GetTransferingSrcCount()>0) mybuffer=GetResString(IDS_DOWNLOADING); else mybuffer=GetResString(IDS_WAITING); 
	switch (GetStatus()) {
		case PS_HASHING: 
		case PS_WAITINGFORHASH:
			mybuffer=GetResString(IDS_HASHING);
			break; 
		case PS_COMPLETING:
			mybuffer=GetResString(IDS_COMPLETING);
			break; 
		case PS_COMPLETE:
			mybuffer=GetResString(IDS_COMPLETE);
			break; 
		case PS_PAUSED:
			mybuffer=GetResString(IDS_PAUSED);
			break; 
		case PS_ERROR:
			mybuffer=GetResString(IDS_ERRORLIKE);
			break;
	} 
	return mybuffer; 
} 

int CPartFile::getPartfileStatusRang(){ 
	
	int status=0;
	if (GetTransferingSrcCount()==0) status=1;
	switch (GetStatus()) {
		case PS_HASHING: 
		case PS_WAITINGFORHASH:
			status=3;
			break; 
		case PS_COMPLETING:
			status=4;
			break; 
		case PS_COMPLETE:
			status=5;
			break; 
		case PS_PAUSED:
			status=2;
			break; 
		case PS_ERROR:
			status=6;
			break;
	} 
	return status;
} 

sint32 CPartFile::getTimeRemaining(){ 
	if (GetDatarate()==0) return -1;
	
	return( (GetFileSize()-GetCompletedSize())/ GetDatarate());
} 

void CPartFile::PreviewFile(){
	if (IsArchive())
	{
		if ((!m_bRecoveringArchive) && (!m_bPreviewing))
			CArchiveRecovery::recover(this, true);
		return;
	}

	if (!PreviewAvailable()){
		ASSERT( false );
		return;
	}
	m_bPreviewing = true;
	CPreviewThread* pThread = (CPreviewThread*) AfxBeginThread(RUNTIME_CLASS(CPreviewThread), THREAD_PRIORITY_NORMAL,0, CREATE_SUSPENDED);
	pThread->SetValues(this);
	pThread->ResumeThread();
}

bool CPartFile::PreviewAvailable(){
	uint64 space;
	space = 0;
	space = GetFreeDiskSpaceX(theApp.glob_prefs->GetTempDir());
	// Barry - Allow preview of archives of any length > 1k
	if (IsArchive()) 
		if (GetStatus() != PS_COMPLETE &&  GetStatus() != PS_COMPLETING && ( GetFileSize() > 1024) && (!m_bRecoveringArchive) && ((space + 100000000) > (2*GetFileSize())))
			return true; else return false;

	return !( (GetStatus() != PS_READY && GetStatus() != PS_PAUSED) 
		|| m_bPreviewing || GetPartCount() < 5 || !IsMovie() || (space + 100000000) < GetFileSize() 
		|| ( !IsComplete(0,PARTSIZE-1) || !IsComplete(PARTSIZE*(GetPartCount()-1),GetFileSize()-1)));
}

void CPartFile::UpdateAvailablePartsCount(){
	POSITION pos1,pos2;
	uint8 availablecounter=0;

	for (int ixPart=0;ixPart<GetPartCount();ixPart++) {
		for( pos1 = srclist.GetHeadPosition(); ( pos2 = pos1 ) != NULL; ){
				srclist.GetNext(pos1);
				if (srclist.GetAt(pos2)->IsPartAvailable(ixPart)) { availablecounter++; pos1=NULL;}
		}
	}
	if (GetPartCount()==availablecounter && availablePartsCount<GetPartCount())
		lastseencomplete=CTime::GetCurrentTime();
	availablePartsCount=availablecounter;
}

Packet*	CPartFile::CreateSrcInfoPacket(CUpDownClient* forClient){
	if (srclist.IsEmpty())
		return 0;

	CMemFile data;
	uint16 nCount = 0;

	data.Write(filehash,16);
	data.Write(&nCount,2);

	for (POSITION pos = srclist.GetHeadPosition();pos != 0;srclist.GetNext(pos)){
		CUpDownClient* cur_src = srclist.GetAt(pos);

		// Modified by Tarod -sends sources with LowID that are in same sever
		// that client is requesting sources
		if (cur_src->HasLowID() 
			&& (cur_src->GetServerIP() != forClient->GetServerIP() || cur_src->GetServerPort() != forClient->GetServerPort()))
			continue;

		// Added by Tarod, do not send incomplete established conns. to avoid sending
		// no valid sources
		switch (cur_src->GetDownloadState()) {
			case DS_NONE:
			case DS_CONNECTING:
			case DS_ERROR:
			case DS_TOOMANYCONNS:
				continue;
				break;
		}

		// only send source which have needed parts for this client if possible
		if (forClient->reqfile == this && forClient->GetPartStatus() && cur_src->GetPartStatus()){
			uint8* reqstatus = forClient->GetPartStatus();
			uint8* srcstatus = cur_src->GetPartStatus();
			bool bNeeded = false;
			for (int x = 0; x != GetPartCount(); x++){
				if (srcstatus[x] && !reqstatus[x]){
					bNeeded = true;
					break;
				}
			}
			if (!bNeeded)
				continue;
		}
		nCount++;
		uint32 dwID = cur_src->GetUserID();
		uint16 nPort = cur_src->GetUserPort();
		uint32 dwServerIP = cur_src->GetServerIP();
		uint16 nServerPort = cur_src->GetServerPort();
		data.Write(&dwID, 4);
		data.Write(&nPort, 2);
		data.Write(&dwServerIP, 4);
		data.Write(&nServerPort, 2);
		if (nCount > 500)
			break;
	}
	if (!nCount)
		return 0;
	data.Seek(16, 0);
	data.Write(&nCount, 2);

	Packet* result = new Packet(&data, OP_EMULEPROT);
	result->opcode = OP_ANSWERSOURCES;
	if (nCount > 28)
		result->PackPacket();
	theApp.emuledlg->AddDebugLogLine( false, "Send:Source User(%s) File(%s) Count(%i)", forClient->GetUserName(), GetFileName(), nCount );
	return result;
}

void CPartFile::AddClientSources(CMemFile* sources){
	uint16 nCount;
	sources->Read(&nCount,2);
	theApp.emuledlg->AddDebugLogLine( false, "RCV:Sources File(%s) Count(%i)", GetFileName(), nCount );

	// Added by Tarod - calculate if we need more space than what is disponible
	// if so remove No Needed Sources
	int source_count_plus_new = GetSourceCount() + nCount;
	if (source_count_plus_new > theApp.glob_prefs->GetMaxSourcePerFile()
		&& ((lastpurgetime == 0) || (::GetTickCount() - lastpurgetime) > PURGE_TIME)) {

		RemoveNoNeededSources(source_count_plus_new - theApp.glob_prefs->GetMaxSourcePerFile());
	}

	for (int i = 0;i != nCount;i++){
		uint32 dwID;
		uint16 nPort;
		uint32 dwServerIP;
		uint16 nServerPort;
		sources->Read(&dwID,4);
		sources->Read(&nPort,2);
		sources->Read(&dwServerIP,4);
		sources->Read(&nServerPort,2);
		// check first if we are this source
		if (theApp.serverconnect->GetClientID() < 16777216 && theApp.serverconnect->IsConnected()){
			if ((theApp.serverconnect->GetClientID() == dwID) && theApp.serverconnect->GetCurrentServer()->GetIP() == dwServerIP)
				continue;
		}
		else if (theApp.serverconnect->GetClientID() == dwID)
			continue;
		// Modified by Tarod -use LowID source only if source is in our same server
		else if (dwID < 16777216 && !theApp.serverconnect->IsLocalServer(dwServerIP, nServerPort))
			continue;

		CUpDownClient* newsource = new CUpDownClient(nPort,dwID,dwServerIP,nServerPort,this);
		if( theApp.glob_prefs->GetMaxSourcePerFile() > this->GetSourceCount() 
			&& i < theApp.glob_prefs->GetMaxXSAtOnce()){
			theApp.downloadqueue->CheckAndAddSource(this,newsource);
		} else {
			CString strName;
			strName.Format(EXCHANGEDSOURCEMARK"%u", dwID) ;
			m_stscrStoringSources->UpdateSource(strName, nPort, dwID, dwServerIP, nServerPort, newsource->GetAvailableOfNeededPartsCount()) ;
			delete newsource;
		} 
	}
}

///////////////////////////////////////////////////////////////////////////////////////
//Storing Sources:

bool CPartFile::SaveSourcesToFile(CStoredSources* toSave, bool debug)
{
	//if this part-file is complete/completing no source saving is needed any more
	if(this->GetStatus() > 7)
		return false;

	//save the sources to disk
	if(toSave != NULL){
		CString temp="";
		temp.Format("%s\\%s.stsrc",theApp.glob_prefs->GetTempDir(),this->GetPartMetFileName());
		CFile saveSources;
		if(saveSources.Open(temp,CFile::modeCreate|
								//CFile::modeNoTruncate|
								CFile::shareDenyWrite|
								CFile::modeWrite,NULL)){
			CArchive archive(&saveSources, CArchive::store);
			archive.SetStoreParams(sizeof(CSourceEntry)*toSave->GetCountSourceEntrys()/10,sizeof(CStoredSources));
			try{
				toSave->Serialize(archive);
			}catch(CArchiveException* err){
				err->Delete();
				archive.Close();
				saveSources.Close();
				return false;
			}
			archive.Close();
			saveSources.Close();
			DEBUG_ONLY(theApp.emuledlg->AddLogLine(false,"Debug: Sources for '%s.stsrc' (%s) were found and saved",this->GetPartMetFileName(),this->GetFileName()));
			//TRACE("Saved sources for %s (%s.src)\n",storedSources->GetFileName(), storedSources->GetPartFileName());
		}
		else{
			TRACE("Could not open file for saving sources of %s (%s.stsrc)\n",toSave->GetFileName(), toSave->GetPartFileName());
			return false;
		}
	}
	else{
		if(debug) theApp.emuledlg->AddLogLine(false,"Due to the fact no sources were found for '%s', nothing was written",this->GetPartMetFileName());
		TRACE("No sources found for %s (%s)\n",toSave->GetFileName(), toSave->GetPartFileName());
	}

	TRACE("Saving sources for %s: all sources were saved once again since %.02f min. ago\n"
					,this->GetFileName()
					,(float(::GetTickCount() - this->m_iLastTimeSourcesSaved)/60000));

	this->m_iLastTimeSourcesSaved = ::GetTickCount();

	return true;
}

CStoredSources*	CPartFile::LoadSourcesFromFile(bool debug)
{
	CString temp="";
	temp.Format("%s\\%s.stsrc",theApp.glob_prefs->GetTempDir(),this->GetPartMetFileName());
	CFile loadSources;
	CStoredSources* toLoad = new CStoredSources(this->GetPartMetFileName()
												, this->GetFileName()
												, this->GetFileHash()
												, theApp.glob_prefs->GetMaxSourcePerFile());
	if(loadSources.Open(temp,CFile::modeRead|CFile::shareDenyWrite,NULL)){
		CArchive archive(&loadSources, CArchive::load);
		CFileStatus fileStatus;
		loadSources.GetStatus(fileStatus);
		long size = fileStatus.m_size;
		archive.SetLoadParams((2*size)/1000);
		try{
			toLoad->Serialize(archive);
		}catch(CException* err){
			char errorMsg[254];
			err->GetErrorMessage(errorMsg,254);
			if(debug) theApp.emuledlg->AddLogLine(false,"Could not load *.stsrc file for %s (%s)\r\n\t(ErrorMessage: %s)",this->GetFileName(),this->GetPartMetFileName(),errorMsg);
			err->Delete();
			archive.Close();
			loadSources.Close();
			delete toLoad;
			return NULL;
		}
		if(debug) theApp.emuledlg->AddLogLine(false,"Found %i Sources for %s in %s.stsrc",toLoad->GetCountSourceEntrys()
									,this->GetFileName(),toLoad->GetPartFileName());
		archive.Close();
		loadSources.Close();
	}
	else {
		if(debug) theApp.emuledlg->AddLogLine(false,"Could not open *.stsrc file for %s (%s)",this->GetFileName(),this->GetPartMetFileName());
		delete toLoad;
		return NULL;
	}

	//set the stored-sources preferences from file
	this->m_iLoadingSourcesTimeInterval			= toLoad->GetLoadSourcesTime();
	this->m_iLoadingSourcesPartialTimeInterval	= toLoad->GetLoadSourcesSlowTime();
	this->m_iLoadSourcesAtOnceLimit				= toLoad->GetLoadSourcesLimit();
	this->m_iSavingSourcesTimeInterval			= toLoad->GetSaveSourcesTime();
	this->m_bLoadSavedSources					= toLoad->GetLoadSources();
	
	return toLoad;
}

bool CPartFile::AddFromStoredSources(CStoredSources* storedSources, bool sendUDPRequest
									, int limit, bool reset, bool debug)
{
	//if this part-file is complete/completing no source-loading is needed any more
	if(this->GetStatus() > 7)
		return false;

	if(storedSources != NULL){
		
		//if limit == -1 (unlimited), the index should not set to 0, because
		//the sources from index 0 to this->m_iSourceIndex where already
		//'slow-loaded' a short time ago (so no need to to it again)
		if(reset){
			storedSources->SetSourceIndex(0);
			this->m_iSourceIndex	= 0;
		}
		else 
			storedSources->SetSourceIndex(this->m_iSourceIndex);

		CSourceEntry* sourceEntry = storedSources->GetNextSourceEntry();//storedSources->GetFirstSourceEntry();
		CServerEntry* serverEntry;
		int countAddedSources=0;//count the added Sources
		int countUDP=0;			//count how many UDP-Requests were send (for debugging)
		uint32 userID;			//temporal variable for a User-ID

		//if sourceEntry is valid and no-limit (for limit equal -1)
		//or loop-counter i < limit, do...
		for(int i=0 ; (sourceEntry != NULL) && ((limit == -1)||(i < limit)); i++){
			serverEntry = sourceEntry->GetLastServerEntry();
			//TRACE("CDownloadQueue::LoadAndAddSourcesFromFile(...): UDP-Source-Request is %s!\n",(sendUDPRequest == true? "ON":"OFF"));
			
			////ask the known servers (of this client/source) for new sources
			//if(sendUDPRequest){
			//	//limited UDP-Requests to 5
			//	for(int i=0; (i < 5) && serverEntry != NULL ; i++){
			//	//while(serverEntry != NULL){
			//		if(theApp.serverconnect->IsConnected()){
			//			if (!theApp.listensocket->TooManySockets()){
			//				//is the server (for the request) already existing?
			//				CServer* server;
			//				char* serverIP = serverEntry->GetFullServerIP();
			//				server = theApp.serverlist->GetServerByAddress(
			//									serverIP
			//									,serverEntry->GetServerPort());
			//				delete serverIP;
			//				
			//				if(server != NULL){
			//					//if not, add the server
			//					//server = new CServer(serverEntry->GetServerPort(),serverEntry->GetFullServerIP());
			//					//theApp.emuledlg->serverwnd.serverlistctrl.AddServer(server);
			//				
			//					Packet packet(OP_GLOBGETSOURCES,16);
			//					memcpy(packet.pBuffer,this->GetFileHash(),16);
			//					theApp.serverconnect->SendUDPPacket(&packet,server,false);
			//					if(debug) theApp.emuledlg->AddLogLine(false,"sended an UDP-Source-Request to %s for %s (%s)\n"
			//														,server->GetFullIP()
			//														,this->GetFileName()
			//														,this->GetPartMetFileName());
			//					countUDP++;
			//				}//end: if(server != NULL)
			//			}//end: if(TooManySockets)
			//		}//end: if(isConnected)
			//		serverEntry = sourceEntry->GetPreviousServerEntry();
			//	}//end for-loop
			//}//end: if(sendUDPRequest)
			
			//generate CUpDownClients for known sources
			serverEntry = sourceEntry->GetLastServerEntry();			
			if(serverEntry !=  NULL){//you need a server for creating a CUpDownClient
				userID=sourceEntry->GetFirstUserID();
				if(this->m_iSourceIDIndex > 0 && limit != -1){
					for(int j=0; j < this->m_iSourceIDIndex; j++)
						userID = sourceEntry->GetNextUserID();
				}
				else this->m_iSourceIDIndex = 1;
				//TRACE("(%s)first source-index: %s:%i (ID: %u)\n",this->GetFileName()
				//	,sourceEntry->GetUserName(),this->m_iSourceIDIndex,userID);

				while(userID > 0){
					//check, if the sources-per-file-limit (glob_prefs) is exceeded
					if(this->srclist.GetCount() >= theApp.glob_prefs->GetMaxSourcePerFile()
						||  ((limit != -1)&&(countAddedSources > limit)) ){
#ifdef _DEBUG
						if((this->srclist.GetCount() >= theApp.glob_prefs->GetMaxSourcePerFile())){
							TRACE("(LoadNAdd): break because of too many sources (\r\n\tsources for this file: %i, MaxSourcesPerFile: %u\n"
									,this->srclist.GetCount()
									,theApp.glob_prefs->GetMaxSourcePerFile()
							);
						}
						if(((limit != -1)&&(countAddedSources > limit))){
							TRACE("(LoadNAdd): break because of loaded all sources for one loop (->source-at-once-limit reached) (\r\n\tsources loaded %i, sources at once limit: %i\n"
									,countAddedSources
									,limit
							);
						}
#endif
						//break;
						goto After_Loading;
					}
					
					// Added by Tarod (LS tweak)
					// HACK by Md5Chap 2002-11-26 (Moved to CUpDownClient::~CUpDownClient)
					//sourceEntry->IncrementFailedConnections();
					CUpDownClient* newSource = new CUpDownClient(sourceEntry->GetUserPort()
														, userID, serverEntry->GetServerIP()
														, serverEntry->GetServerPort(), this, sourceEntry);
					
					CString strUserName = sourceEntry->GetUserName() ;
					if (strUserName.Find(EXCHANGEDSOURCEMARK) == 0) {
						strUserName = IDS_EXCHANGEDSOURCE ;
					} else {
						strUserName = IDS_LOADEDSOURCE ;
					}
					char* pName = new char [strUserName.GetLength() + 2] ;
					memset(pName, '\0', (strUserName.GetLength() + 2)) ;
					strncpy(pName, strUserName.GetBuffer(), strUserName.GetLength()) ;
					newSource->SetUserName(pName);
					delete [] pName; // Added by Tarod -leak fix because SetUserName uses nstrdup()

					theApp.downloadqueue->CheckAndAddSource(this, newSource, false);
					//TRACE("(LoadNAdd) new source (count: %i, index: %i) for %s's sended to CheckNAdd\n"
					//		//, (newSource->GetUserName()!=NULL?newSource->GetUserName():NONAME)
					//		, countAddedSources
					//		, this->m_iSourceIDIndex++
					//		, this->GetFileName());
					countAddedSources++;
					this->m_iSourceIDIndex++;
					if(debug){ theApp.emuledlg->AddLogLine(false,"added Source (%i) of %i Sources for %s (%s)"
							,newSource->GetUserID()
							,storedSources->GetCountSourceEntrys(),this->GetFileName()
							,storedSources->GetFileID());
						theApp.emuledlg->AddLogLine(false,"The new Source is %s and the SourceEntry is %s",(newSource == NULL ? "NULL" : "not NULL"),(sourceEntry == NULL ? "NULL" : "not NULL"));
						theApp.emuledlg->AddLogLine(false,"New source No. %i added for %s",countAddedSources,this->GetFileName());
					}
					 userID = sourceEntry->GetNextUserID();
				}//end while-loop
			}//end if(serverEntry != NULL)
			sourceEntry = storedSources->GetNextSourceEntry();//limit);
			this->m_iSourceIDIndex = -1;
//#ifdef _DEBUG
//            if(sourceEntry != NULL) TRACE("(%s)last source-index: %s:%i (ID: %u)\n",this->GetFileName()
//					,sourceEntry->GetUserName(),this->m_iSourceIDIndex,userID);
//#endif
		}//end while-loop
#ifdef _DEBUG
		if(sendUDPRequest && countUDP > 0)TRACE("###############################################\n%i UDP-Source-Requests for %s were sended\n###############################################\n",countUDP,this->GetFileName());
#endif
	}//end if(storedSources != NULL)
After_Loading:
	//for 'slow-loading' the sources
	bool isCompletelyLoaded = true;
	if(storedSources != NULL){

		this->m_iSourceIndex = storedSources->GetSourceIndex();

		//are all sources from the CStoredSources-object loaded?
		if(this->m_iSourceIndex +1 >= storedSources->GetCountSourceEntrys())
			isCompletelyLoaded = true;
		else
			isCompletelyLoaded = false;

		this->m_bLoadedSourcesCompletely = isCompletelyLoaded;

		//TRACE("Added the next %i sources up to source-index %u for %s\n"
		//		, (limit != -1?limit: (-1))
		//		, this->m_iSourceIndex
		//		, this->GetFileName());
	}

	//set timers
	if(limit != -1){
#ifdef _DEBUG
		if(storedSources != NULL) TRACE("Slow-loading %s (No. %u of %u): %u secs since last call\n"
				, this->GetFileName()
				, this->m_iSourceIndex
				, storedSources->GetCountSourceEntrys()
				,(::GetTickCount() - this->m_iLastTimeSourcesLoadedPartial)/1000);
#endif
		
		this->m_iLastTimeSourcesLoadedPartial = ::GetTickCount();
		if(isCompletelyLoaded){
#ifdef _DEBUG
			if(storedSources != NULL) TRACE("Slow-loading %s: all sources (%u of %u) were loaded once again since %.02f min. ago\n"
					, this->GetFileName()
					, this->m_iSourceIndex
					, storedSources->GetCountSourceEntrys()
					, (float(::GetTickCount() - this->m_iLastTimeSourcesLoaded)/60000));
#endif
		
			this->m_iLastTimeSourcesLoaded = ::GetTickCount();
		}
	}
	else{
		TRACE("Normal-loading %s: all sources (%u) were loaded once again since %.02f min. ago\n"
					, this->GetFileName()
					, this->m_iSourceIndex
					, (float(::GetTickCount() - this->m_iLastTimeSourcesLoaded)/60000));

		this->m_iLastTimeSourcesLoaded = ::GetTickCount();
	}
	
	//TRACE("End of trying to add sources from StoredSource (AddFromStoredSources)\n");
	if (isCompletelyLoaded) m_iLoadingSourcesTimeInterval = RESTORE_SOURCES_TIME ; // Added by Tarod

	return isCompletelyLoaded;
}

bool CPartFile::LoadAndAddSources(bool keepSourcesOpen, bool sendUDPRequest
									, int limit, bool reset, bool debug,bool addImmediately)
{
	CStoredSources* storedSources = NULL;
	if((storedSources = this->LoadSourcesFromFile(debug)) == NULL){
		if(this->m_stscrStoringSources == NULL){
				this->m_stscrStoringSources = new CStoredSources(this->GetPartMetFileName()
													, this->GetFileName()
													, this->GetFileHash()
													, theApp.glob_prefs->GetMaxSourcePerFileSoft());
				//set the preferences for the StoredSources
				this->m_stscrStoringSources->SetPreferences(theApp.glob_prefs->GetMaxSourcePerFileSoft()
												, this->m_iLoadSourcesAtOnceLimit
												, this->m_iLoadingSourcesTimeInterval
												, this->m_iLoadingSourcesPartialTimeInterval
												, this->m_iSavingSourcesTimeInterval);
		}
		return false;
	}
	//if successfully loaded:
	else{
		if(addImmediately)this->AddFromStoredSources(storedSources, sendUDPRequest, limit, reset, debug);

		TRACE("There were %u sources loaded from disk for %s are %s\n"
				,storedSources->GetCountOfSources()
				, this->GetFileName()
				,((keepSourcesOpen&& theApp.downloadqueue->KeepStoredSourcesOpen())?"kept open!":"not kept open!"));
		if(keepSourcesOpen && theApp.downloadqueue->KeepStoredSourcesOpen()){
			if(this->m_stscrStoringSources == NULL){
				this->m_stscrStoringSources = new CStoredSources(this->GetPartMetFileName()
													, this->GetFileName()
													, this->GetFileHash()
													, theApp.glob_prefs->GetMaxSourcePerFileSoft());
				//set the preferences for the StoredSources
				this->m_stscrStoringSources->SetPreferences(theApp.glob_prefs->GetMaxSourcePerFileSoft()
												, this->m_iLoadSourcesAtOnceLimit
												, this->m_iLoadingSourcesTimeInterval
												, this->m_iLoadingSourcesPartialTimeInterval
												, this->m_iSavingSourcesTimeInterval);
			}
			this->m_stscrStoringSources->UpdateSource(storedSources);
		}
		delete storedSources;
	}
    return true;
}

bool CPartFile::SaveToStoredSources(CUpDownClient* toSave)
{
	if(!theApp.downloadqueue->IsSourcesLoadingEnabled())
		return false;

	if(this->m_stscrStoringSources == NULL){
		this->m_stscrStoringSources = new CStoredSources(this->GetPartMetFileName()
											, this->GetFileName()
											, this->GetFileHash()
											, theApp.glob_prefs->GetMaxSourcePerFileSoft());
		//set the preferences for the StoredSources
		this->m_stscrStoringSources->SetPreferences(theApp.glob_prefs->GetMaxSourcePerFileSoft()
										, this->m_iLoadSourcesAtOnceLimit
										, this->m_iLoadingSourcesTimeInterval
										, this->m_iLoadingSourcesPartialTimeInterval
										, this->m_iSavingSourcesTimeInterval);
	}
#ifdef _DEBUG
	uint32 oldCount = this->m_stscrStoringSources->GetCountOfSources();
#endif
	uint16 partsCount = 0;
	CString userName;
	partsCount	= toSave->GetAvailableOfNeededPartsCount();	//get the count of parts, this user has and we want to download
	//userName	= (toSave->GetUserName() != NULL ? toSave->GetUserName(): NONAME);//check, if the user-name of this user is set/known
	userName.Format(LOADEDSOURCEMARK"%u", toSave->GetUserID());
	this->m_stscrStoringSources->UpdateSource(userName
													, toSave->GetUserPort()
													, toSave->GetUserID()
													, toSave->GetServerIP()
													, toSave->GetServerPort()
													, partsCount);
#ifdef _DEBUG
	TRACE("CPartFile::SaveToStoredSources(...) : source was %s to %s's StoredSources (count (old/new): %u/%u\n\n"
		, (this->m_stscrStoringSources->FindSource(userName,toSave->GetServerPort())?"saved":"not saved")
		, this->GetFileName()
		, oldCount
		, this->m_stscrStoringSources->GetCountOfSources());
#endif
	return true;
}

bool CPartFile::SaveToStoredSources()
{
	if(!theApp.downloadqueue->KeepStoredSourcesOpen())
		return false;

	bool successful=false;
	if(this->m_stscrStoringSources == NULL){
		this->m_stscrStoringSources = new CStoredSources(this->GetPartMetFileName()
										, this->GetFileName()
										, this->GetFileHash()
										, theApp.glob_prefs->GetMaxSourcePerFileSoft());
		//set the preferences for the StoredSources
		this->m_stscrStoringSources->SetPreferences(theApp.glob_prefs->GetMaxSourcePerFileSoft()
											, this->m_iLoadSourcesAtOnceLimit
											, this->m_iLoadingSourcesTimeInterval
											, this->m_iLoadingSourcesPartialTimeInterval
											, this->m_iSavingSourcesTimeInterval);
	}
#ifdef _DEBUG
	uint32 oldCount = this->m_stscrStoringSources->GetCountOfSources();
#endif
	uint16 partsCount = 0;
	CString userName;
	CUpDownClient* source;
	successful = false;	//just to be sure
	for(POSITION posOfSource=this->srclist.GetHeadPosition() ; posOfSource != NULL;){
		source = this->srclist.GetNext(posOfSource);
		partsCount = source->GetAvailableOfNeededPartsCount();	//get the count of parts, this user has and we want to download
		//userName = (source->GetUserName() != NULL ? source->GetUserName(): NONAME);//check, if the user-name of this user is set/known
		userName.Format(LOADEDSOURCEMARK"%u", source->GetUserID());
		this->m_stscrStoringSources->UpdateSource(userName, source->GetUserPort(), 
									source->GetUserID(), source->GetServerIP(), 
									source->GetServerPort(), partsCount);
		successful=true;
	}
#ifdef _DEBUG
	TRACE("CPartFile::SaveToStoredSources() : all current sources of %s were %s to its StoredSources (count (old/new): %u/%u\n\n"
		, this->GetFileName()
		, successful?"saved":"not saved"
		, oldCount
		, this->m_stscrStoringSources->GetCountOfSources());
#endif
	return successful;
}

bool CPartFile::SaveSources(bool loadSourcesFirst)
{
	bool successful=false;
	if(theApp.downloadqueue->KeepStoredSourcesOpen() && theApp.downloadqueue->IsSourcesLoadingEnabled()){
		if(!this->SaveToStoredSources() && this->m_stscrStoringSources->GetCountOfSources() < 1)
			return false;

		//if(this->m_stscrStoringSources == NULL)
		//	return false;

		TRACE("%u sources for %s are about to be saved to disk\n"
				,this->m_stscrStoringSources->GetCountOfSources()
				,this->GetFileName());

		//no need to load the file again, because all its informations
		//are already in this->m_stscrStoringSources
		//no need to gather all sources from this->scrlist, because everytime
		//a source is added there, it is also added to this->m_stscrStoringSources
		//(see CDownloadQueue::CheckAndAdd[Known]Source(...))
		this->SaveSourcesToFile(this->m_stscrStoringSources);
		successful = true;
	}
	else if(theApp.downloadqueue->IsSourcesLoadingEnabled()){
		CStoredSources* storedSources = NULL;

		if(loadSourcesFirst){
			storedSources = this->LoadSourcesFromFile();
		}
		if(storedSources == NULL){
			storedSources = new CStoredSources(this->GetPartMetFileName()
											, this->GetFileName()
											, this->GetFileHash()
											, theApp.glob_prefs->GetMaxSourcePerFileSoft());
			//set the preferences for the StoredSources
			storedSources->SetPreferences(theApp.glob_prefs->GetMaxSourcePerFileSoft()
												, this->m_iLoadSourcesAtOnceLimit
												, this->m_iLoadingSourcesTimeInterval
												, this->m_iLoadingSourcesPartialTimeInterval
												, this->m_iSavingSourcesTimeInterval);
		}
		uint16 partsCount = 0;
		CString userName;
		CUpDownClient* source;
		successful = false;	//just to be sure
		for(POSITION posOfSource=this->srclist.GetHeadPosition() ; posOfSource != NULL;){
			source = this->srclist.GetNext(posOfSource);
			partsCount = source->GetAvailableOfNeededPartsCount();	//get the count of parts, this user has and we want to download
			//userName = (source->GetUserName() != NULL ? source->GetUserName(): NONAME);//check, if the user-name of this user is set/known
			userName.Format(LOADEDSOURCEMARK"%u", source->GetUserID());
			storedSources->UpdateSource(userName, source->GetUserPort(), 
										source->GetUserID(), source->GetServerIP(), 
										source->GetServerPort(), partsCount);
			successful=true;
		}
		
		//if(debug) theApp.emuledlg->AddLogLine(false,"All Sources of '%s' (%s) were gathered for saving to disk",this->GetFileName(),this->GetPartMetFileName());

		if(successful){
			this->SaveSourcesToFile(storedSources);
			TRACE("%u sources for %s were saved to disk with no permanent open StoredSources-object\n"
					,storedSources->GetCountSourceEntrys()
					,this->GetFileName());
		}
		delete storedSources;
	}
	else if(!theApp.downloadqueue->IsSourcesLoadingEnabled()){
		CStoredSources* storedSources = this->LoadSourcesFromFile();

		if(storedSources == NULL){
			storedSources = new CStoredSources(this->GetPartMetFileName()
											, this->GetFileName()
											, this->GetFileHash()
											, theApp.glob_prefs->GetMaxSourcePerFileSoft());
			//set the preferences for the StoredSources
			storedSources->SetPreferences(theApp.glob_prefs->GetMaxSourcePerFileSoft()
												, this->m_iLoadSourcesAtOnceLimit
												, this->m_iLoadingSourcesTimeInterval
												, this->m_iLoadingSourcesPartialTimeInterval
												, this->m_iSavingSourcesTimeInterval);
		}
		uint16 partsCount = 0;
		CString userName;
		CUpDownClient* source;
		successful = false;	//just to be sure
		for(POSITION posOfSource=this->srclist.GetHeadPosition() ; posOfSource != NULL;){
			source = this->srclist.GetNext(posOfSource);
			partsCount = source->GetAvailableOfNeededPartsCount();	//get the count of parts, this user has and we want to download
			//userName = (source->GetUserName() != NULL ? source->GetUserName(): NONAME);//check, if the user-name of this user is set/known
			userName.Format(LOADEDSOURCEMARK"%u", source->GetUserID());
			storedSources->UpdateSource(userName, source->GetUserPort(), 
										source->GetUserID(), source->GetServerIP(), 
										source->GetServerPort(), partsCount);
			successful=true;
		}
		
		//if(debug) theApp.emuledlg->AddLogLine(false,"All Sources of '%s' (%s) were gathered for saving to disk",this->GetFileName(),this->GetPartMetFileName());

		if(successful){
			this->SaveSourcesToFile(storedSources);
			TRACE("%u sources for %s were saved to disk while loading of sources is disabled\n"
					,storedSources->GetCountSourceEntrys()
					,this->GetFileName());
		}
		delete storedSources;
	}
	return successful;
}

// Sets this file as an A4AF auto
//
// [In]
//   in_A4AF_auto: true -> Set this file as A4AF auto
//                 false-> Unset this file as A4AF auto
//
// [Global]
//   in_A4AF_auto: true -> File hash is copied into prefs->A4AF_file_hash
//                 false-> "Null" hash is copied into prefs->A4AF_file_hash
void CPartFile::SetA4AFAuto(bool in_A4AF_auto)
{
	m_is_A4AF_auto = in_A4AF_auto;

	// Set file hash
	if (in_A4AF_auto) {
		theApp.glob_prefs->SetA4AFHash(this->GetFileHash());
	} else {
		// Set null hash
		uchar null_hash[16];
		memset(null_hash, 0, sizeof(null_hash));
		theApp.glob_prefs->SetA4AFHash(null_hash);
	}
}

// Updates priority of file if autopriority is activated
void CPartFile::UpdateDownloadAutoPriority(void)
{
	if (this->IsAutoPrioritized()) {
		CPartFile* partfile = theApp.downloadqueue->GetFileByID(this->GetFileHash()) ;
		
		if (partfile != NULL && partfile->IsPartFile()) {
			int valid_sources = partfile->GetSourceCount() - partfile->GetNotCurrentSourcesCount();
			if (valid_sources < RARE_FILE && priority != PR_HIGH)
				this->SetPriority(PR_HIGH) ;
			else if (valid_sources >= RARE_FILE && valid_sources < 300 && priority != PR_NORMAL)
				this->SetPriority(PR_NORMAL) ;
			else if (valid_sources >= 300 && priority != PR_LOW)
				this->SetPriority(PR_LOW) ;
		} 
	}
}

// making this function return a higher when more sources have the extended
// protocol will force you to ask a larger variety of people for sources
int CPartFile::GetCommonFilePenalty() {
	//TODO: implement, but never return less than MINCOMMONPENALTY!
	return MINCOMMONPENALTY;
}

/* Barry - Replaces BlockReceived() 

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

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

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

		   Data is also requested only where gaps are, not in 180k blocks.
		   The requests will still not exceed 180k, but may be smaller to
		   fill a gap.
*/
uint32 CPartFile::WriteToBuffer(uint32 transize, BYTE *data, uint32 start, uint32 end, Requested_Block_Struct *block)
{
	if( status == PS_COMPLETING || status == PS_COMPLETE )
		theApp.emuledlg->AddLogLine(true,"DEBUG FCB: Block received while/after completing (Writebuffer) (%s)", this->GetFileName());

	// Increment transfered bytes counter for this file
	transfered += transize;

	// This is needed a few times
	uint32 lenData = end - start + 1;

	if( lenData > transize )
		m_iGainDueToCompression += lenData-transize;

	// Occasionally packets are duplicated, no point writing it twice
	if (IsComplete(start, end))
	{
		theApp.emuledlg->AddDebugLogLine(false, "File '%s' has already been written from %ld to %ld\n", GetFileName(), start, end);
		return 0;
	}

	// Create copy of data as new buffer
	BYTE *buffer = new BYTE[lenData];
	memcpy(buffer, data, lenData);

	// Create a new buffered queue entry
	PartFileBufferedData *item = new PartFileBufferedData;
	item->data = buffer;
	item->start = start;
	item->end = end;
	item->block = block;

	// Add to the queue in the correct position (most likely the end)
	PartFileBufferedData *queueItem;
	bool added = false;
	POSITION pos = m_BufferedData_list.GetTailPosition();
	while (pos != NULL)
	{	
		queueItem = m_BufferedData_list.GetPrev(pos);
		if (item->end > queueItem->end)
		{
			added = true;
			m_BufferedData_list.InsertAfter(pos, item);
			break;
		}
	}
	if (!added)
		m_BufferedData_list.AddHead(item);

	// Increment buffer size marker
	m_nTotalBufferData += lenData;

	// Mark this small section of the file as filled
	FillGap(item->start, item->end);

	// Update the flushed mark on the requested block 
	// The loop here is unfortunate but necessary to detect deleted blocks.
	pos = requestedblocks_list.GetHeadPosition();
	while (pos != NULL)
	{	
		if (requestedblocks_list.GetNext(pos) == item->block)
			item->block->transferred += lenData;
	}

	if (gaplist.IsEmpty()) FlushBuffer();

	// Return the length of data written to the buffer
	return lenData;
}

void CPartFile::FlushBuffer(void)
{
	m_nLastBufferFlushTime = GetTickCount();
	if (m_BufferedData_list.IsEmpty())
		return;

	if( status == PS_COMPLETING || status == PS_COMPLETE )
		theApp.emuledlg->AddLogLine(true,"DEBUG FCB: Block received while/after completing (Flush buffer) (%s)", this->GetFileName());

	//int partCount = filesize / PARTSIZE;
	// there are a couple of ways of doing this, this is one... 
	int partCount = (filesize/PARTSIZE)+((filesize%PARTSIZE)? 1 : 0);
	
	bool *changedPart = new bool[partCount];

//	theApp.emuledlg->AddDebugLogLine(false, "Flushing file %s - buffer size = %ld bytes (%ld queued items) transfered = %ld [time = %ld]\n", GetFileName(), m_nTotalBufferData, m_BufferedData_list.GetCount(), transfered, m_nLastBufferFlushTime);

	try
	{
		// Remember which parts need to be checked at the end of the flush
		for (int partNumber=0; partNumber<partCount; partNumber++)
		{
			changedPart[partNumber] = false;
		}

		// Ensure file is big enough to write data to (the last item will be the furthest from the start)
		PartFileBufferedData *item = m_BufferedData_list.GetTail();
		if (m_hpartfile.GetLength() <= item->end)
			m_hpartfile.SetLength(item->end + 1);

		// Loop through queue
		for (int i = m_BufferedData_list.GetCount(); i>0; i--)
		{
			// Get top item
			item = m_BufferedData_list.GetHead();

			// This is needed a few times
			uint32 lenData = item->end - item->start + 1;

			int curpart = item->start/PARTSIZE;
			changedPart[curpart] = true;

			// Go to the correct position in file and write block of data
			m_hpartfile.Seek(item->start, CFile::begin);
			m_hpartfile.Write(item->data, lenData);

			// Remove item from queue
			m_BufferedData_list.RemoveHead();

			// Decrease buffer size
			m_nTotalBufferData -= lenData;

			// Release memory used by this item
			delete [] item->data;
			delete item;
		}

		// Flush to disk
		m_hpartfile.Flush();

		// Check each part of the file
		uint32 partRange = (m_hpartfile.GetLength() % PARTSIZE) - 1;
		for (int partNumber = partCount-1; partNumber >= 0; partNumber--)
		{
			if (changedPart[partNumber] == false)
			{
				// Any parts other than last must be full size
				partRange = PARTSIZE - 1;
				continue;
			}

			// Is this 9MB part complete
			if ( IsComplete(PARTSIZE * partNumber, (PARTSIZE * (partNumber + 1)) - 1 ) )
			{
				// Is part corrupt
				if (!HashSinglePart(partNumber))
				{
					theApp.emuledlg->AddDebugLogLine(true, GetResString(IDS_ERR_PARTCORRUPT), partNumber, GetFileName());
					AddGap(PARTSIZE*partNumber, (PARTSIZE*partNumber + partRange));
					corrupted_list.AddTail(partNumber);
					// Reduce transfered amount by corrupt amount
					this->m_iLostDueToCorruption += (partRange + 1);				}
				else
				{
					// Successfully completed part, make it available for sharing
					if (status == PS_EMPTY)
					{
						status = PS_READY;
						theApp.sharedfiles->SafeAddKFile(this);
					}
				}
			}
			else if ( IsCorruptedPart(partNumber) && theApp.glob_prefs->IsICHEnabled())
			{
				// Try to recover with minimal loss
				if (HashSinglePart(partNumber))
				{
					m_iTotalPacketsSavedDueToICH++;
					FillGap(PARTSIZE*partNumber,(PARTSIZE*partNumber+partRange));
					RemoveBlockFromList(PARTSIZE*partNumber,(PARTSIZE*partNumber + partRange));
					theApp.emuledlg->AddLogLine(true,GetResString(IDS_ICHWORKED),partNumber,GetFileName());
				}
			}
			// Any parts other than last must be full size
			partRange = PARTSIZE - 1;
		}

		// Update met file
		SavePartFile();

		// Is this file finished?
		if (gaplist.IsEmpty())
			CompleteFile(false);
	}
	catch (CFileException* error)
	{
		OUTPUT_DEBUG_TRACE();

		if (theApp.glob_prefs->IsErrorBeepEnabled()) Beep(800,200);

		if (error->m_cause == CFileException::diskFull) 
		{
			theApp.emuledlg->AddLogLine(true, GetResString(IDS_ERR_OUTOFSPACE), this->GetFileName());
			if (theApp.glob_prefs->GetNotifierPopOnImportantError()) 
			{
				CString msg;
				msg.Format(GetResString(IDS_ERR_OUTOFSPACE), this->GetFileName());
				theApp.emuledlg->ShowNotifier(msg, TBN_IMPORTANTEVENT, false);
			}
		}
		else
		{
			char buffer[150];
			error->GetErrorMessage(buffer,150);
			theApp.emuledlg->AddLogLine(true, GetResString(IDS_ERR_WRITEERROR), GetFileName(), buffer);
			status = PS_ERROR;
		}
		paused = true;
		datarate = 0;
		transferingsrc = 0;
	
		theApp.emuledlg->transferwnd.downloadlistctrl.UpdateItem(this);
		error->Delete();
	}
	catch(...)
	{
		theApp.emuledlg->AddLogLine(true, GetResString(IDS_ERR_WRITEERROR), GetFileName(), GetResString(IDS_UNKNOWN));
		status = PS_ERROR;
		paused = true;
		datarate = 0;
		transferingsrc = 0;
		theApp.emuledlg->transferwnd.downloadlistctrl.UpdateItem(this);
	}
	delete[] changedPart;

}
// Barry - This will invert the gap list, up to caller to delete gaps when done
// 'Gaps' returned are really the filled areas, and guaranteed to be in order
void CPartFile::GetFilledList(CTypedPtrList<CPtrList, Gap_Struct*> *filled)
{
	Gap_Struct *gap;
	Gap_Struct *best;
	POSITION pos;
	uint32 start = 0;
	uint32 bestEnd = 0;

	// Loop until done
	bool finished = false;
	while (!finished)
	{
		finished = true;
		// Find first gap after current start pos
		bestEnd = filesize;
		pos = gaplist.GetHeadPosition();
		while (pos != NULL)
		{
			gap = gaplist.GetNext(pos);
			if ((gap->start > start) && (gap->end < bestEnd))
			{
				best = gap;
				bestEnd = best->end;
				finished = false;
			}
		}

		if (!finished)
		{
			// Invert this gap
			gap = new Gap_Struct;
			gap->start = start;
			gap->end = best->start - 1;
			start = best->end + 1;
			filled->AddTail(gap);
		}
		else if (best->end < filesize)
		{
			gap = new Gap_Struct;
			gap->start = best->end + 1;
			gap->end = filesize;
			filled->AddTail(gap);
		}
	}
}

