//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"
#include "otherfunctions.h"
#include <sys/stat.h>

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


// 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();
	md4cpy(m_abyFileHash, searchresult->GetFileHash());
	for (int i = 0; i < searchresult->taglist.GetCount();i++){
		switch (searchresult->taglist[i]->tag.specialtag){
			case FT_FILENAME:{
				m_pszFileName = nstrdup(searchresult->taglist[i]->tag.stringvalue);
				break;
			}
			case FT_FILESIZE:{
				m_nFileSize = searchresult->taglist[i]->tag.intvalue;
				break;
			}
			default:
				CTag* newtag = new CTag(searchresult->taglist[i]->tag);
				taglist.Add(newtag);
		}
	}
	if(m_nFileSize < PARTSIZE )
		hashsetneeded = false;
	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);
		SetStatus(PS_ERROR);
	}
	delete pLink;
}

void CPartFile::InitializeFromLink(CED2KFileLink* fileLink)
{
	Init();
	try{
		m_pszFileName = nstrdup( fileLink->GetName() );
		m_nFileSize = fileLink->GetSize();
		if(m_nFileSize < PARTSIZE )
			hashsetneeded = false;
		md4cpy(m_abyFileHash, fileLink->GetHashKey());
		if (!theApp.downloadqueue->IsFileExisting(m_abyFileHash))
			CreatePartFile();
		else
			SetStatus(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);
		SetStatus(PS_ERROR);
	}
}

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

void CPartFile::Init(){
	fullname = 0;
	newdate = true;
	lastsearchtime = 0;
	lastpurgetime = ::GetTickCount();
	paused = false;
	stopped= false;
	status = PS_EMPTY;
	transfered = 0;

	if(theApp.glob_prefs->GetNewAutoDown()){
		m_iDownPriority = PR_HIGH;
		m_bAutoDownPriority = true;
	}
	else{
		m_iDownPriority = PR_NORMAL;
		m_bAutoDownPriority = false;
	}
	srcarevisible = false;
	// -khaos--+++> Initialize our stat vars to 0
	//transferingsrc = 0;
	m_iSrcTransferring =	0;
	m_iSrcNNP =				0;
	m_iSrcOnQueue =			0;
	m_iSrcConnecting =		0;
	m_iSrcTooManyConns =	0;
	m_iSrcConnected =		0;
	m_iSrcConnViaServer =	0;
	m_iSrcUnknown =			0;
	m_iSrcRecHash =			0;
	m_iSrcLowToLow =		0;
	m_iSrcQueueFull =		0;
	m_iSrcBanned =			0;
	m_iSrcError =			0;
	m_iSrcA4AF =			0;
	// <-----khaos-
	datarate = 0;
	hashsetneeded = true;
	count = 0;
	percentcompleted = 0;
	partmetfilename = 0;
	completedsize=0;
	m_bPreviewing = false;
	lastseencomplete = NULL;
	availablePartsCount=0;
	m_ClientSrcAnswered = 0;
	m_LastNoNeededCheck = 0;
	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;
	hasRating	= false;
	hasComment	= false;
	hasBadRating= false;
	m_lastdatetimecheck=0;
	m_category=0;
	updatemystatus=true;
	m_bWriteError = false;
    m_random_update_wait = (uint32)(rand()/(RAND_MAX/1000));
}


CPartFile::~CPartFile(){
	// Barry - Ensure all buffered data is written
	FlushBuffer();
	
	if (m_hpartfile.m_hFile != INVALID_HANDLE_VALUE){
		// commit file and directory entry
		m_hpartfile.Close();
		// Update met file (with current directory entry)
		SavePartFile();
	}

	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);
}

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); 

	directory = nstrdup(theApp.glob_prefs->GetIncomingDir());

	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 = m_nFileSize-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));
		SetStatus(PS_ERROR);
	}
	delete[] partfull;

	m_SrcpartFrequency.SetSize(GetPartCount());
	for (uint32 i = 0; i < GetPartCount();i++)
		m_SrcpartFrequency.Add(0);
	paused = false;

	if (theApp.glob_prefs->AutoFilenameCleanup()) SetFileName( CleanupFilename( m_pszFileName ).GetBuffer() );

	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;

	// readfile data form part.met file
	CSafeBufferedFile metFile;
	CFileException fexpMet;
	if (!metFile.Open(fullname, CFile::modeRead|CFile::osSequentialScan|CFile::typeBinary, &fexpMet)){
		CString strError;
		strError.Format(GetResString(IDS_ERR_OPENMET), partmetfilename, _T(""));
		TCHAR szError[MAX_CFEXP_ERRORMSG];
		if (fexpMet.GetErrorMessage(szError, ELEMENT_COUNT(szError))){
			strError += _T(" - ");
			strError += szError;
		}
		theApp.emuledlg->AddLogLine(false, strError);
		return false;
	}
	setvbuf(metFile.m_pStream, NULL, _IOFBF, 16384);

	try{
		uint8 version;
		metFile.Read(&version,1);
		if (version != PARTFILE_VERSION){
			metFile.Close();
			theApp.emuledlg->AddLogLine(false, GetResString(IDS_ERR_BADMETVERSION), partmetfilename, m_pszFileName);
			return false;
		}
		LoadDateFromFile(&metFile);
		LoadHashsetFromFile(&metFile, false);

		uint32 tagcount;
		metFile.Read(&tagcount, 4);

		for (uint32 j = 0; j != tagcount; j++){
			CTag* newtag = new CTag(&metFile);
			switch(newtag->tag.specialtag){
				case FT_FILENAME:{
					if(newtag->tag.stringvalue == NULL) {
						theApp.emuledlg->AddLogLine(true, GetResString(IDS_ERR_METCORRUPT), partmetfilename, m_pszFileName);
						delete newtag;
						return false;
					}
					m_pszFileName = nstrdup(newtag->tag.stringvalue);
					delete newtag;
					break;
				}
				case FT_LASTSEENCOMPLETE: {
					lastseencomplete = newtag->tag.intvalue;
					delete newtag;
					break;
				}
				case FT_FILESIZE:{
					m_nFileSize = newtag->tag.intvalue;
					delete newtag;
					break;
				}
				case FT_TRANSFERED:{
					transfered = newtag->tag.intvalue;
					delete newtag;
					break;
				}
				case FT_CATEGORY:{
					m_category = newtag->tag.intvalue;
					delete newtag;
					break;
				}
				case FT_DLPRIORITY:{
					m_iDownPriority = newtag->tag.intvalue;
					delete newtag;
					if( m_iDownPriority == PR_AUTO ){
						m_iDownPriority = PR_HIGH;
						SetAutoDownPriority(true);
					}
					else
						SetAutoDownPriority(false);
					break;
				}
				case FT_STATUS:{
					paused = newtag->tag.intvalue;
					stopped=paused;
					delete newtag;
					break;
				}
				case FT_ULPRIORITY:{
					SetUpPriority(newtag->tag.intvalue, false);
					delete newtag;
					if( GetUpPriority() == PR_AUTO ){
						SetUpPriority(PR_HIGH, false);
						SetAutoUpPriority(true);
					}
					else
						SetAutoUpPriority(false);
					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);
				}
			}
		}
		metFile.Close();
	}
	catch(CFileException* error){
		OUTPUT_DEBUG_TRACE();
		if (error->m_cause == CFileException::endOfFile)
			theApp.emuledlg->AddLogLine(true, GetResString(IDS_ERR_METCORRUPT), partmetfilename, m_pszFileName ? m_pszFileName : _T(""));
		else{
			char buffer[MAX_CFEXP_ERRORMSG];
			error->GetErrorMessage(buffer,MAX_CFEXP_ERRORMSG);
			theApp.emuledlg->AddLogLine(true, GetResString(IDS_ERR_FILEERROR), partmetfilename, m_pszFileName ? m_pszFileName : _T(""), error);
		}
		error->Delete();
		return false;
	}
	catch(...){
		OUTPUT_DEBUG_TRACE();
		theApp.emuledlg->AddLogLine(true, GetResString(IDS_ERR_METCORRUPT), partmetfilename, m_pszFileName ? m_pszFileName : _T(""));
		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*/

		// SLUGFILLER: SafeHash - revised code, and extra safety
		if (gap->start != -1 && gap->end != -1 && gap->start <= gap->end && gap->start < m_nFileSize){
			if (gap->end >= m_nFileSize)
				gap->end = m_nFileSize-1; // Clipping
			AddGap(gap->start, gap->end); // All tags accounted for, use safe adding
		}
		delete gap;
		// SLUGFILLER: SafeHash
	}

	//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;
	CFileException fexpPart;
	if (!m_hpartfile.Open(searchpath, CFile::modeReadWrite|CFile::shareDenyWrite|CFile::osSequentialScan, &fexpPart)){
		CString strError;
		strError.Format(GetResString(IDS_ERR_FILEOPEN), searchpath, m_pszFileName ? m_pszFileName : _T(""));
		TCHAR szError[MAX_CFEXP_ERRORMSG];
		if (fexpPart.GetErrorMessage(szError, ELEMENT_COUNT(szError))){
			strError += _T(" - ");
			strError += szError;
		}
		theApp.emuledlg->AddLogLine(false, strError);
		delete[] searchpath;
		return false;
	}
	delete[] searchpath;
			
	// SLUGFILLER: SafeHash - final safety, make sure any missing part of the file is gap
	if (m_hpartfile.GetLength() < m_nFileSize)
		AddGap(m_hpartfile.GetLength(), m_nFileSize-1);
	// Goes both ways - Partfile should never be too large
	if (m_hpartfile.GetLength() > m_nFileSize){
		TRACE("Partfile \"%s\" is too large! Truncating %I64u bytes.\n", GetFileName(), m_hpartfile.GetLength() - m_nFileSize);
		m_hpartfile.SetLength(m_nFileSize);
	}
	// SLUGFILLER: SafeHash

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

	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); // this; "...returns m_attribute without high-order flags" indicates a known MFC bug, wonder how many unknown there are... :)
	time_t fdate = mktime(filestatus.m_mtime.GetLocalTm());
	if (fdate == -1)
		theApp.emuledlg->AddDebugLogLine(false, "Failed to convert file date of %s (%s)", filestatus.m_szFullName, m_pszFileName);
	if (date != fdate){

//uint32 datenow=mktime(filestatus.m_mtime.GetLocalTm());
//CTime t1=CTime(date);
//CTime t2=CTime(datenow);
//CString d1=t1.Format( theApp.glob_prefs->GetDateTimeFormat());
//CString d2=t2.Format( theApp.glob_prefs->GetDateTimeFormat());
//theApp.emuledlg->AddLogLine(true, "Name=%s; saved=%s; current=%s",m_pszFileName,d1,d2);

		theApp.emuledlg->AddLogLine(false, GetResString(IDS_ERR_REHASH), buffer, m_pszFileName ? m_pszFileName : _T(""));
		// rehash
		SetStatus(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;
	}

	// search part file
	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()){
		theApp.emuledlg->AddLogLine(false, GetResString(IDS_ERR_SAVEMET)+" ("+GetResString(IDS_ERR_PART_FNF)+") ", partmetfilename, m_pszFileName);
		return false;
	}

	//get filedate
	CTime lwtime;
	if (!ff.GetLastWriteTime(lwtime))
		theApp.emuledlg->AddDebugLogLine(false, "Failed to get file date of %s (%s) - %s", partmetfilename, m_pszFileName, GetErrorMessage(GetLastError()));
	date = mktime(lwtime.GetLocalTm());
	if (date == -1)
		theApp.emuledlg->AddDebugLogLine(false, "Failed to convert file date of %s (%s)", partmetfilename, m_pszFileName);
	ff.Close();
	uint32 lsc = mktime(lastseencomplete.GetLocalTm());

	// TODO: Change the part.met file writing to writing to a temp file, and after that temp file
	// was successfully written, replace the part.met file with it..

	// rename already available part.met file temporary to part.met.backup
	CString backupName(fullname);
	backupName.Append(".backup");
	if (!m_bWriteError){
		remove(backupName);
		rename(fullname, backupName);
	}

	// save file data to part.met file
	CSafeBufferedFile file;
	CFileException fexp;
	if (!file.Open(fullname, CFile::modeWrite|CFile::modeCreate|CFile::typeBinary, &fexp)){
		CString strError;
		strError.Format(GetResString(IDS_ERR_SAVEMET), partmetfilename, m_pszFileName);
		TCHAR szError[MAX_CFEXP_ERRORMSG];
		if (fexp.GetErrorMessage(szError, ELEMENT_COUNT(szError))){
			strError += _T(" - ");
			strError += szError;
		}
		theApp.emuledlg->AddLogLine(false, strError);
		m_bWriteError = true;
		return false;
	}
	setvbuf(file.m_pStream, NULL, _IOFBF, 16384);

	try{
		//version
		uint8 version = PARTFILE_VERSION;
		file.Write(&version,1);
		//date
		file.Write(&date,4);
		//hash
		file.Write(&m_abyFileHash,16);
		uint16 parts = hashlist.GetCount();
		file.Write(&parts,2);
		for (int x = 0; x != parts; x++)
			file.Write(hashlist[x],16);
		//tags
		uint32 tagcount = 8+(gaplist.GetCount()*2);
		// Float meta tags are currently not written. All older eMule versions < 0.28b have 
		// a bug in the meta tag reading+writing code. To achive maximum backward 
		// compatibility for met files with older eMule versions we just don't write float 
		// tags. This is OK, because we (eMule) do not use float tags. The only float tags 
		// we may have to handle is the '# Sent' tag from the Hybrid, which is pretty 
		// useless but may be received from us via the servers.
		// 
		// The code for writing the float tags SHOULD BE ENABLED in SOME MONTHS (after most 
		// people are using the newer eMule versions which do not write broken float tags).	
		for (int j = 0; j < taglist.GetCount(); j++){
			if (taglist[j]->tag.type == 2 || taglist[j]->tag.type == 3)
				tagcount++;
		}
		file.Write(&tagcount,4);

		CTag nametag(FT_FILENAME, m_pszFileName);
		nametag.WriteTagToFile(&file);

		CTag sizetag(FT_FILESIZE, m_nFileSize);
		sizetag.WriteTagToFile(&file);

		CTag transtag(FT_TRANSFERED, transfered);
		transtag.WriteTagToFile(&file);

		CTag statustag(FT_STATUS, (paused)? 1: 0);
		statustag.WriteTagToFile(&file);

		CTag prioritytag(FT_DLPRIORITY, IsAutoDownPriority() ? PR_AUTO : m_iDownPriority);
		prioritytag.WriteTagToFile(&file);

		CTag lsctag(FT_LASTSEENCOMPLETE,lsc);
		lsctag.WriteTagToFile(&file);

		CTag ulprioritytag(FT_ULPRIORITY, IsAutoUpPriority() ? PR_AUTO : GetUpPriority());
		ulprioritytag.WriteTagToFile(&file);

		CTag categorytag(FT_CATEGORY, m_category);
		categorytag.WriteTagToFile(&file);

		for (int j = 0; j < taglist.GetCount(); j++){
			if (taglist[j]->tag.type == 2 || taglist[j]->tag.type == 3)
				taglist[j]->WriteTagToFile(&file);
		}

		//gaps
		char namebuffer[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(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(namebuffer,(gaplist.GetAt(pos)->end)+1);
			gapendtag.WriteTagToFile(&file);
			i_pos++;
		}

		file.Close();
		m_bWriteError = false; // successfully written

		// after successfully writing the part.met file, remove the temporary backup
		remove(backupName);
	}
	catch(CFileException* error){
		OUTPUT_DEBUG_TRACE();
		CString strError;
		strError.Format(GetResString(IDS_ERR_SAVEMET), partmetfilename, m_pszFileName);
		TCHAR szError[MAX_CFEXP_ERRORMSG];
		if (fexp.GetErrorMessage(szError, ELEMENT_COUNT(szError))){
			strError += _T(" - ");
			strError += szError;
		}
		theApp.emuledlg->AddLogLine(false, strError);
		error->Delete();
		m_bWriteError = true;
		return false;
	}

	// create a backup of the successfully written part.met file
	CString BAKName(fullname);
	BAKName.Append(".BAK");
	::CopyFile(fullname, BAKName, FALSE);

	return true;
}

void CPartFile::PartFileHashFinished(CKnownFile* result){
	newdate = true;
	bool errorfound = false;
	for (uint32 i = 0; i < (uint32)hashlist.GetSize(); i++){
		if (IsComplete(i*PARTSIZE,((i+1)*PARTSIZE)-1)){
			if (!(result->GetPartHash(i) && !md4cmp(result->GetPartHash(i),this->GetPartHash(i)))){
				theApp.emuledlg->AddLogLine(false, GetResString(IDS_ERR_FOUNDCORRUPTION), i+1, m_pszFileName);		
				AddGap(i*PARTSIZE,((((i+1)*PARTSIZE)-1) >= m_nFileSize) ? m_nFileSize-1 : ((i+1)*PARTSIZE)-1);
				errorfound = true;
			}
		}
	}
	delete result;
	if (!errorfound){
		if (status == PS_COMPLETING){
			CompleteFile(true);
			return;
		}
		else
			theApp.emuledlg->AddLogLine(false, GetResString(IDS_HASHINGDONE), m_pszFileName);
	}
	else{
		SetStatus(PS_READY);
		SavePartFile();
		return;
	}
	SetStatus( 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);
	UpdateDisplayedInfo();
	newdate = true;
}

bool CPartFile::IsComplete(uint32 start, uint32 end){
	if (end >= m_nFileSize)
		end = m_nFileSize-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 >= m_nFileSize)
		end = m_nFileSize-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;
}

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;
				md4cpy(result->FileID, GetFileHash());
				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);
	UpdateDisplayedInfo();
	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/m_nFileSize) * 100; 
		completedsize = m_nFileSize - allgaps - 1; 
	} 
	else{
		percentcompleted = 100;
		completedsize = m_nFileSize;
	}
}

void CPartFile::DrawShareStatusBar(CDC* dc, RECT* rect, bool onlygreyrect, 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.SetFileSize(this->GetFileSize()); 
	s_ChunkBar.SetHeight(rect->bottom - rect->top); 
	s_ChunkBar.SetWidth(rect->right - rect->left); 
	s_ChunkBar.Fill(crMissing); 
	COLORREF color;
	if (!onlygreyrect && !m_SrcpartFrequency.IsEmpty()) { 
		for (int i = 0;i < GetPartCount();i++)
			if(m_SrcpartFrequency[i] > 0 ){
				color = RGB(0, (210-(22*(m_SrcpartFrequency[i]-1)) <  0)? 0:210-(22*(m_SrcpartFrequency[i]-1)), 255);
				s_ChunkBar.FillRange(PARTSIZE*(i),PARTSIZE*(i+1),color);
			}
	}
   	s_ChunkBar.Draw(dc, rect->left, rect->top, bFlat); 
} 

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(m_nFileSize);
	s_ChunkBar.Fill(crHave);

	uint32 allgaps = 0;

	if(status == PS_COMPLETE || status == PS_COMPLETING) {
		s_ChunkBar.FillRange(0, m_nFileSize, crProgress);
		s_ChunkBar.Draw(dc, rect->left, rect->top, bFlat);
		percentcompleted = 100;
		completedsize = m_nFileSize;
		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[(uint16)i])  // frequency?
					color = RGB(0,
					(210-(22*(m_SrcpartFrequency[(uint16)i]-1)) <  0)? 0:210-(22*(m_SrcpartFrequency[(uint16)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)m_nFileSize);
	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)((m_nFileSize-((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)((m_nFileSize-((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/m_nFileSize)) * 100;
		completedsize = m_nFileSize - allgaps - 1;
	}
	else{
		percentcompleted = 100;
		completedsize = m_nFileSize;
	}

}

void CPartFile::WritePartStatus(CFile* file){
	uint16 parts = GetPartCount();
	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 (int sl=0;sl<SOURCESSLOTS;sl++) if (!srclists[sl].IsEmpty())
	for (pos1 = srclists[sl].GetHeadPosition();( pos2 = pos1 ) != NULL;){
		srclists[sl].GetNext(pos1);
		CUpDownClient* cur_src = srclists[sl].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 (int sl=0;sl<SOURCESSLOTS;sl++) if (!srclists[sl].IsEmpty())
		for (pos1 = srclists[sl].GetHeadPosition();( pos2 = pos1 ) != NULL;){
			srclists[sl].GetNext(pos1);
			CUpDownClient* cur_src = srclists[sl].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 || status == PS_COMPLETING || status == PS_COMPLETE || ignorepause)
		return status;
	else
		return PS_PAUSED;
}
void CPartFile::AddDownloadingSource(CUpDownClient* client){
	POSITION pos = m_downloadingSourceList.Find(client); // to be sure
	if(pos == NULL){
		m_downloadingSourceList.AddTail(client);
	}
}

void CPartFile::RemoveDownloadingSource(CUpDownClient* client){
	POSITION pos = m_downloadingSourceList.Find(client); // to be sure
	if(pos != NULL){
		m_downloadingSourceList.RemoveAt(pos);
	}
}

uint32 CPartFile::Process(uint32 reducedownload, uint8 m_icounter/*in percent*/, uint32 friendReduceddownload)
{
	uint16	i_TcntTransferring = 0; // -khaos--+++> Renamed var (One line)
	DWORD dwCurTick = ::GetTickCount();
	CUpDownClient* cur_src;

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

	datarate = 0;

	// calculate datarate, set limit etc.
	if(m_icounter < 10){
		uint32 cur_datarate;
		for(POSITION pos = m_downloadingSourceList.GetHeadPosition();pos!=0;){
			cur_src = m_downloadingSourceList.GetNext(pos);
			if(cur_src){
				i_TcntTransferring++;
				cur_datarate = cur_src->CalculateDownloadRate();
				datarate+=cur_datarate;
				if(reducedownload){
					uint32 limit = reducedownload*cur_datarate/1000;
					if(limit<1000 && reducedownload == 200)
						limit +=1000;
					else if(limit<1)
						limit = 1;
					cur_src->socket->SetDownloadLimit(limit);
				}
			}
		}
	}
	else{

		// -khaos--+++> Moved this here, otherwise we were setting our permanent variables to 0 every tenth of a second...
		//	No point in doing that!
		bool	alreadyCounted =		false;
		uint16	i_TcntNNP =				0;
		uint16	i_TcntOnQueue =			0;
		uint16	i_TcntConnecting =		0;
		uint16	i_TcntTooManyConns =	0;
		uint16	i_TcntConnected =		0;
		uint16	i_TcntConnViaSrv =		0;
		uint16	i_TcntUnknown =			0;
		uint16	i_TcntRecHash =			0;
		uint16	i_TcntLowToLow =		0;
		uint16	i_TcntQueueFull =		0;
		uint16	i_TcntBanned =			0;
		uint16	i_TcntErr =				0;
		uint16	i_TcntA4AF =			0;
		// <-----khaos-

		POSITION pos1, pos2;
		for (uint32 sl = 0; sl < SOURCESSLOTS; sl++){
			if (!srclists[sl].IsEmpty()){
				for (pos1 = srclists[sl].GetHeadPosition();( pos2 = pos1 ) != NULL;){
					srclists[sl].GetNext(pos1);
					cur_src = srclists[sl].GetAt(pos2);

					// -khaos--+++> Haven't counted anything yet.
					alreadyCounted = false;

					switch (cur_src->GetDownloadState()){
						case DS_DOWNLOADING:{
                            uint32 curClientReducedDownload = reducedownload;
                            if(cur_src->IsFriend() && cur_src->GetFriendSlot()) {
                                curClientReducedDownload = friendReduceddownload;
                            }

							i_TcntTransferring++; // -khaos--+++> Renamed var.
							uint32 cur_datarate = cur_src->CalculateDownloadRate();
							datarate += cur_datarate;
							if (curClientReducedDownload && cur_src->GetDownloadState() == DS_DOWNLOADING){
								uint32 limit = curClientReducedDownload*cur_datarate/1000; //(uint32)(((float)reducedownload/100)*cur_datarate)/10;		
								if (limit < 1000 && curClientReducedDownload == 200)
									limit += 1000;
								else if (limit < 1)
									limit = 1;
								cur_src->socket->SetDownloadLimit(limit);
							}
							else
								cur_src->socket->DisableDownloadLimit();
							break;
						}
						case DS_BANNED:
							// -khaos--+++> Added counters and a break...
							i_TcntBanned++;
							break;
						case DS_ERROR:
							i_TcntErr++;
							break;	
							// <-----khaos-
						case DS_CONNECTED:
							if(cur_src->socket){
								if( !cur_src->socket->IsConnected() ){
									cur_src->SetDownloadState(DS_NONE);
									i_TcntUnknown++; // -khaos--+++> Unknown status.
									break;
								}
							}
							else{
								cur_src->SetDownloadState(DS_NONE);
								i_TcntUnknown++; // -khaos--+++> Unknown status.
								break;
							}
							i_TcntConnected++; alreadyCounted = true; // -khaos--+++> Still connected...
						case DS_LOWTOLOWIP:	// if we now have a high ip we can ask
							// -khaos--+++>
							if(!alreadyCounted) { i_TcntLowToLow++; alreadyCounted = true; }
							// <-----khaos-
							if( ((dwCurTick - lastpurgetime) > 30000) && (this->GetSourceCount() >= (theApp.glob_prefs->GetMaxSourcePerFile()*.8 )) ){
								theApp.downloadqueue->RemoveSource( cur_src );
								lastpurgetime = dwCurTick;
								break;
							}
							if (theApp.serverconnect->IsLowID())
								break;
						case DS_NONEEDEDPARTS:{ 
							// -khaos--+++>
							if(!alreadyCounted) { i_TcntNNP++; alreadyCounted = true; }
							// <-----khaos-
							if( ((dwCurTick - lastpurgetime) > 40000) && (this->GetSourceCount() >= (theApp.glob_prefs->GetMaxSourcePerFile()*.8 )) )
								if( !cur_src->SwapToAnotherFile( false ) ){
									theApp.downloadqueue->RemoveSource( cur_src );
									lastpurgetime = dwCurTick;
									break; //Johnny-B - nothing more to do here (good eye!)
								}
							// doubled reasktime for no needed parts - save connections and traffic
							if (!((!cur_src->GetLastAskedTime()) || (dwCurTick - cur_src->GetLastAskedTime()) > FILEREASKTIME*2))
								break; 
						}
						case DS_ONQUEUE:{
							if( cur_src->IsRemoteQueueFull() ) {
								// -khaos--+++>
								if(!alreadyCounted) { i_TcntQueueFull++; alreadyCounted = true; }
								// <-----khaos-
								if( ((dwCurTick - lastpurgetime) > 60000) && (this->GetSourceCount() >= (theApp.glob_prefs->GetMaxSourcePerFile()*.8 )) ){
									theApp.downloadqueue->RemoveSource( cur_src );
									lastpurgetime = dwCurTick;
									break; //Johnny-B - nothing more to do here (good eye!)
								}
							}
							// -khaos--+++>
							else if (!alreadyCounted) { i_TcntOnQueue++; alreadyCounted = true; }
							// <-----khaos-

							if (theApp.serverconnect->IsConnected() && ((!cur_src->GetLastAskedTime()) || (dwCurTick - cur_src->GetLastAskedTime()) > FILEREASKTIME-20000))
								cur_src->UDPReaskForDownload();

							// -khaos--+++> Changes continue to closing tag...
							goto DoneCounting;
						}
						case DS_CONNECTING:
							i_TcntConnecting++;		goto DoneCounting;
						case DS_TOOMANYCONNS:
							i_TcntTooManyConns++;	goto DoneCounting;
						case DS_NONE:
							i_TcntUnknown++;		goto DoneCounting;
						case DS_WAITCALLBACK:
							i_TcntConnViaSrv++;
DoneCounting:
							// <-----khaos- Don't forget the label...
							if (theApp.serverconnect->IsConnected() && ((!cur_src->GetLastAskedTime()) || (dwCurTick - cur_src->GetLastAskedTime()) > FILEREASKTIME))
								cur_src->AskForDownload();
							break;
						// -khaos--+++> For stats sake...
						case DS_REQHASHSET:
							i_TcntRecHash++;	break;
						default:
							i_TcntUnknown++;	break;
					}
					// See the definition of this function in updownclient.h for info.
					i_TcntA4AF += cur_src->GetA4AFCount();
					// <-----khaos-
				}
			}
		}

		// -khaos--+++> Set the members using temp vars.  We have
		//				to use temporary vars so that the disp counts
		//				don't get screwed up during processing.
		m_iSrcTransferring=		i_TcntTransferring;
		m_iSrcNNP =				i_TcntNNP;
		m_iSrcOnQueue =			i_TcntOnQueue;
		m_iSrcConnecting =		i_TcntConnecting;
		m_iSrcTooManyConns =	i_TcntTooManyConns;
		m_iSrcConnected =		i_TcntConnected;
		m_iSrcConnViaServer =	i_TcntConnViaSrv;
		m_iSrcUnknown =			i_TcntUnknown;
		m_iSrcRecHash =			i_TcntRecHash;
		m_iSrcLowToLow =		i_TcntLowToLow;
		m_iSrcQueueFull =		i_TcntQueueFull;
		m_iSrcBanned =			i_TcntBanned;
		m_iSrcError =			i_TcntErr;
		m_iSrcA4AF =			i_TcntA4AF;
		// <-----khaos-

		// swap No needed partfiles if possible
		if (((!m_LastNoNeededCheck) || (dwCurTick - m_LastNoNeededCheck) > 10000))
		{
			m_LastNoNeededCheck = dwCurTick;
			POSITION pos1, pos2;
			for (int sl = 0; sl < SOURCESSLOTS; sl++)
				if (!srclists[sl].IsEmpty())
					for (pos1 = srclists[sl].GetHeadPosition();( pos2 = pos1 ) != NULL;)
					{
						srclists[sl].GetNext(pos1);
						CUpDownClient* cur_src = srclists[sl].GetAt(pos2);
						if (cur_src->GetDownloadState() == DS_NONEEDEDPARTS)
							cur_src->SwapToAnotherFile(false);
					}
		}

		// check if we want new sources from server
		//uint16 test = theApp.glob_prefs->GetMaxSourcePerFileSoft();
		if (( (!lastsearchtime) || (dwCurTick - lastsearchtime) > SERVERREASKTIME) && theApp.serverconnect->IsConnected()
			&& theApp.glob_prefs->GetMaxSourcePerFileSoft() > GetSourceCount() && !stopped )
		{
			//local server
			lastsearchtime = dwCurTick;
			Packet* packet = new Packet(OP_GETSOURCES,16);
			md4cpy(packet->pBuffer,m_abyFileHash);
			theApp.uploadqueue->AddUpDataOverheadServer(packet->size);
			theApp.serverconnect->SendPacket(packet,true);
			if ( theApp.glob_prefs->GetDebugSourceExchange() )
				theApp.emuledlg->AddDebugLogLine( false, "Send:Source Request Server File(%s)", GetFileName() );
		}

		count++;
		if (count == 3){
			count = 0;
			UpdateAutoDownPriority();
			UpdateDisplayedInfo();
			if(m_bPercentUpdated == false)
				UpdateCompletedInfos();
			m_bPercentUpdated = false;
		}
	}

	if (m_iSrcTransferring!=i_TcntTransferring ){
		m_iSrcTransferring= i_TcntTransferring;
		if (theApp.emuledlg->transferwnd.downloadlistctrl.curTab==0) theApp.emuledlg->transferwnd.downloadlistctrl.ChangeCategory(0); 
			else UpdateDisplayedInfo(true);
		if (theApp.glob_prefs->ShowCatTabInfos() ) theApp.emuledlg->transferwnd.UpdateCatTabTitles();
	}

	return datarate;
}

bool CPartFile::CanAddSource(uint32 userid, uint16 port, uint32 serverip, uint16 serverport, uint8* pdebug_lowiddropped)
{
	// MOD Note: Do not change this part - Merkur
	// check first if we are this source
	if (theApp.serverconnect->GetClientID() < 16777216 && theApp.serverconnect->IsConnected()){
		if ((theApp.serverconnect->GetClientID() == userid) && inet_addr(theApp.serverconnect->GetCurrentServer()->GetFullIP()) == serverip)
			return false;
	}
	else if (theApp.serverconnect->GetClientID() == userid){
#ifdef _DEBUG
		// bluecow - please do not remove this in the debug version - i need this for testing.
		if (theApp.serverconnect->IsLowID() || theApp.glob_prefs->GetPort() == port)
#endif
		return false;
	}
	else if (userid < 16777216 && !theApp.serverconnect->IsLocalServer(serverip,serverport)){
		if (pdebug_lowiddropped)
			(*pdebug_lowiddropped)++;
		return false;
	}
	// MOD Note - end
	
	return true;
}

void CPartFile::AddSources(CMemFile* sources,uint32 serverip, uint16 serverport){

	if (stopped) return;

	uint8 count;
	uint8 debug_lowiddropped = 0;
	uint8 debug_possiblesources = 0;
	sources->Read(&count,1);
	for (int i = 0;i != count;i++){
		uint32 userid;
		sources->Read(&userid,4);
		uint16 port;
		sources->Read(&port,2);

		if (!CanAddSource(userid, port, serverip, serverport, &debug_lowiddropped))
			continue;

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

void CPartFile::NewSrcPartsInfo(){
	// Cache part count
	uint16 partcount = GetPartCount();

	// Increase size if necessary
	if(m_SrcpartFrequency.GetSize() < partcount){
		m_SrcpartFrequency.SetSize(partcount);
	}
	// Reset part counters
	for(int i = 0; i < partcount; i++){
		m_SrcpartFrequency[i] = 0;
	}
	CUpDownClient* cur_src;
	
	for(int sl=0; sl<SOURCESSLOTS; ++sl) {
		if (!srclists[sl].IsEmpty()) {
			for (POSITION pos = srclists[sl].GetHeadPosition(); pos != 0; ){
				cur_src = srclists[sl].GetNext(pos);
				for (int i = 0; i < partcount; i++){
					if (cur_src->IsPartAvailable(i))
						m_SrcpartFrequency[i] +=1;
				}
			}
		}
	}
	
	UpdateDisplayedInfo();
}

bool CPartFile::GetNextRequestedBlock(CUpDownClient* sender,Requested_Block_Struct** newblocks,uint16* count){
	uint16 requestedCount = *count;
	if (requestedCount == 0)
		return false;
	uint16 newblockcount = 0;
	uint8* partsav = sender->GetPartStatus();
	if (partsav == NULL)
		return false;
	*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;

        if(sender->GetHasPriorPart()) {
            if(GetNextEmptyBlockInPart(sender->GetPriorPartNumber(), 0)) {
                liGoodParts.AddHead(sender->GetPriorPartNumber());
                foundPriorityPart = true;
            } else {
                sender->ClearPriorPartNumber();
            }
        }
		// 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);
				}
		}

        sender->SetPriorPartNumber(usedpart);

		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){
	if( this->srcarevisible )
		theApp.emuledlg->transferwnd.downloadlistctrl.HideSources(this);
	
	if (!bIsHashingDone){
		//m_hpartfile.Flush(); // flush the OS buffer before completing...
		SetStatus(PS_COMPLETING);
		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();
		SetStatus( PS_COMPLETING);

		CWinThread *pThread = AfxBeginThread((AFX_THREADPROC)CompleteThreadProc, this, THREAD_PRIORITY_BELOW_NORMAL, 0, CREATE_SUSPENDED); // Lord KiRon - using threads for file completion
		if (pThread)
			pThread->ResumeThread();
		else
			throw CString(GetResString(IDS_ERR_FILECOMPLETIONTHREAD));
	}
	theApp.emuledlg->transferwnd.downloadlistctrl.ShowFilesCount();
	if (theApp.glob_prefs->ShowCatTabInfos() ) theApp.emuledlg->transferwnd.UpdateCatTabTitles();
	UpdateDisplayedInfo(true);
}

UINT CPartFile::CompleteThreadProc(CPartFile* pFile) 
{ 
	DbgSetThreadName("PartFileComplete");
	if (!pFile)
		return -1; 
   	pFile->PerformFileComplete(); 
   	return 0; 
}

// Lord KiRon - using threads for file completion
BOOL CPartFile::PerformFileComplete() 
{
	// -khaos--+++> Save the lost/gain stats!
	theApp.glob_prefs->Add2LostFromCorruption(m_iLostDueToCorruption);
	theApp.glob_prefs->Add2SavedFromCompression(m_iGainDueToCompression);
	// <-----khaos-

	CSingleLock(&m_FileCompleteMutex,TRUE); // will be unlocked on exit
	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];
	CString indir;

	if (PathFileExists( theApp.glob_prefs->GetCategory(GetCategory())->incomingpath)) {
		indir=theApp.glob_prefs->GetCategory(GetCategory())->incomingpath;
		sprintf(newname,"%s\\%s",indir,newfilename);
	} else{
		indir=theApp.glob_prefs->GetIncomingDir();
		sprintf(newname,"%s\\%s",indir,newfilename);
	}

	// close permanent handle
	if (m_hpartfile.m_hFile != INVALID_HANDLE_VALUE)
		m_hpartfile.Close();

	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;
		if (this) theApp.emuledlg->AddLogLine(true,GetResString(IDS_ERR_COMPLETIONFAILED),GetFileName());
		paused = true;
		SetStatus(PS_ERROR);
		if (theApp.glob_prefs->StartNextFile()) theApp.downloadqueue->StartNextFile(GetCategory());
		return FALSE;
	}

	// to have the accurate date stored in known.met we have to update the 'date' of a just completed file.
	// if we don't update the file date here (after commiting the file and before adding the record to known.met), 
	// that file will be rehashed at next startup and there would also be a duplicate entry (hash+size) in known.met
	// because of different file date!
	ASSERT( m_hpartfile.m_hFile == INVALID_HANDLE_VALUE ); // the file must be closed/commited!
	struct _stat st;
	if (_stat(newname, &st) == 0)
		date = st.st_mtime;

	if (remove(fullname))
		theApp.emuledlg->AddLogLine(true,GetResString(IDS_ERR_DELETEFAILED),fullname);

	CString BAKName(fullname);
	BAKName.Append(".BAK");
	if (::DeleteFile(BAKName) == 0)
		theApp.emuledlg->AddLogLine(true,GetResString(IDS_ERR_DELETE), BAKName);

	BAKName = fullname;
	BAKName.Append(".backup");
	(void)::DeleteFile(BAKName);

	delete[] partfilename;
	delete[] fullname;
	fullname = newname;
	delete[] directory;
	directory = nstrdup(indir);
	SetStatus( 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);
	theApp.downloadqueue->RemoveFile(this);
	UpdateDisplayedInfo(true);
	theApp.emuledlg->transferwnd.downloadlistctrl.ShowFilesCount();
	//SHAddToRecentDocs(SHARD_PATH, fullname); // This is a real nasty call that takes ~110 ms on my 1.4 GHz Athlon and isn't really needed afai see...[ozon]

	// Barry - Just in case
	//		transfered = m_nFileSize;
	if (theApp.glob_prefs->StartNextFile()) theApp.downloadqueue->StartNextFile(GetCategory());

	// -khaos--+++> Extended Statistics Added 2-10-03
		theApp.glob_prefs->Add2DownCompletedFiles();		// Increments cumDownCompletedFiles in prefs struct
		theApp.glob_prefs->Add2DownSessionCompletedFiles(); // Increments sesDownCompletedFiles in prefs struct
		theApp.glob_prefs->SaveCompletedDownloadsStat();	// Saves cumDownCompletedFiles to INI
	// <-----khaos- End Statistics Modifications

	return TRUE;
}

void  CPartFile::RemoveAllSources(bool bTryToSwap){
	POSITION pos1,pos2;
	for (int sl=0;sl<SOURCESSLOTS;sl++) if (!srclists[sl].IsEmpty())
	for( pos1 = srclists[sl].GetHeadPosition(); ( pos2 = pos1 ) != NULL; ){
		srclists[sl].GetNext(pos1);
		if (bTryToSwap){
			if (!srclists[sl].GetAt(pos2)->SwapToAnotherFile(true))
				theApp.downloadqueue->RemoveSource(srclists[sl].GetAt(pos2));
		}
		else
			theApp.downloadqueue->RemoveSource(srclists[sl].GetAt(pos2));
	}
	UpdateFileRatingCommentAvail();
}

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

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

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

	if (m_hpartfile.m_hFile != INVALID_HANDLE_VALUE)
		m_hpartfile.Close();

	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);

	CString BAKName(fullname);
	BAKName.Append(".BAK");
	if (::DeleteFile(BAKName) == 0)
		theApp.emuledlg->AddLogLine(true,GetResString(IDS_ERR_DELETE), BAKName);

	BAKName = fullname;
	BAKName.Append(".backup");
	(void)::DeleteFile(BAKName);

	delete[] partfilename;
	delete this;
}

bool CPartFile::HashSinglePart(uint16 partnumber)
{
	if ((GetHashCount() <= partnumber) && (GetPartCount() > 1)){
		theApp.emuledlg->AddLogLine(true,GetResString(IDS_ERR_HASHERRORWARNING),GetFileName());
		this->hashsetneeded = true;
		return true;
	}
	else if(!GetPartHash(partnumber) && GetPartCount() != 1){
		theApp.emuledlg->AddLogLine(true,GetResString(IDS_ERR_INCOMPLETEHASH),GetFileName());
		this->hashsetneeded = true;
		return true;		
	}
	else{
		uchar hashresult[16];
		m_hpartfile.Seek((LONGLONG)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 (md4cmp(hashresult,GetPartHash(partnumber)))
				return false;
			else
				return true;
		}
		else{
			if (md4cmp(hashresult,m_abyFileHash))
				return false;
			else
				return true;
		}
	}
}

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

bool CPartFile::IsMovie(){
	return (ED2KFT_VIDEO == GetED2KFileTypeID(GetFileName()) );
}

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

void CPartFile::SetDownPriority(uint8 np){
	m_iDownPriority = np;
	UpdateDisplayedInfo(true);
	SavePartFile();
}

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

	RemoveAllSources(true);
	paused = true;
	stopped=true;
	datarate = 0;
	// -khaos--+++> Renamed transferring var and added the other source vars.
	m_iSrcTransferring =	0;
	m_iSrcNNP =				0;
	m_iSrcOnQueue =			0;
	m_iSrcConnecting =		0;
	m_iSrcTooManyConns =	0;
	m_iSrcConnected =		0;
	m_iSrcConnViaServer =	0;
	m_iSrcUnknown =			0;
	m_iSrcRecHash =			0;
	m_iSrcLowToLow =		0;
	m_iSrcQueueFull =		0;
	m_iSrcBanned =			0;
	m_iSrcError =			0;
	m_iSrcA4AF =			0;
	FlushBuffer();
	UpdateDisplayedInfo(true);
}

void CPartFile::PauseFile(){
	if (status==PS_COMPLETE || status==PS_COMPLETING)
		return;
	Packet* packet = new Packet(OP_CANCELTRANSFER,0);
	for (int sl=0;sl<SOURCESSLOTS;sl++) if (!srclists[sl].IsEmpty())
		for( POSITION pos = srclists[sl].GetHeadPosition(); pos != NULL; ){
			CUpDownClient* cur_src = srclists[sl].GetNext(pos);
			if (cur_src->GetDownloadState() == DS_DOWNLOADING){
				theApp.uploadqueue->AddUpDataOverheadOther(packet->size);
				cur_src->socket->SendPacket(packet,false,true);
				cur_src->SetDownloadState(DS_ONQUEUE);
			}
		}
	delete packet;
	paused = true;
	SetStatus(status); // to update item
	datarate = 0;
	m_iSrcTransferring = 0; // -khaos--+++> Renamed var.
	UpdateDisplayedInfo(true);
	SavePartFile();
}

void CPartFile::ResumeFile(){
	if (status==PS_COMPLETE || status==PS_COMPLETING)
		return;
	paused = false;
	stopped = false;
	lastsearchtime = 0;
	SavePartFile();
	//theApp.emuledlg->transferwnd.downloadlistctrl.UpdateItem(this);
	UpdateDisplayedInfo(true);
}

CString CPartFile::getPartfileStatus(){ 

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

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

	return tempstatus;
} 

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

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

	if (!PreviewAvailable()){
		ASSERT( false );
		return;
	}


	if (theApp.glob_prefs->IsMoviePreviewBackup()) {
		m_bPreviewing = true;
		CPreviewThread* pThread = (CPreviewThread*) AfxBeginThread(RUNTIME_CLASS(CPreviewThread), THREAD_PRIORITY_NORMAL,0, CREATE_SUSPENDED);
		pThread->SetValues(this,theApp.glob_prefs->GetVideoPlayer());
		pThread->ResumeThread();
	} else {
		CString strLine = this->GetFullName();
		if (theApp.glob_prefs->GetVideoPlayer().Find("vlc.exe")) strLine.Replace(".met","");

		//char shortPath[512]; //Cax2 short path for vlc
		//GetShortPathName(strLine,shortPath,512);

		CString path=theApp.glob_prefs->GetVideoPlayer();
		int pos=path.ReverseFind('\\');
		if (pos==-1) path=""; else path=path.Left(pos+1);

		if (theApp.glob_prefs->GetVideoPlayer().GetLength()>0) ShellExecute(NULL, "open", theApp.glob_prefs->GetVideoPlayer() ,strLine/* shortPath*/, path, SW_SHOWNORMAL);
			else ShellExecute(NULL, "open", strLine, NULL,NULL, SW_SHOWNORMAL);
	}
}

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

	if (theApp.glob_prefs->IsMoviePreviewBackup())
		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)));
	else 
		return !((GetStatus() != PS_READY && GetStatus() != PS_PAUSED) 
				|| m_bPreviewing || GetPartCount() < 2 || !IsMovie() || !IsComplete(0,PARTSIZE-1)); 
}

void CPartFile::UpdateAvailablePartsCount(){
	uint16 availablecounter = 0;
	bool breakflag = false;
	uint16 iPartCount = GetPartCount();
	for (uint32 ixPart = 0; ixPart < iPartCount; ixPart++){
		breakflag = false;
		for (uint32 sl = 0; sl < SOURCESSLOTS && !breakflag; sl++){
			if (!srclists[sl].IsEmpty()){
				for(POSITION pos = srclists[sl].GetHeadPosition(); pos && !breakflag; ){
					if (srclists[sl].GetNext(pos)->IsPartAvailable(ixPart)){ 
						availablecounter++; 
						breakflag = true;
					}
				}
			}
		}
	}
	if (iPartCount == availablecounter && availablePartsCount < iPartCount)
		lastseencomplete = CTime::GetCurrentTime();
	availablePartsCount = availablecounter;
}

Packet*	CPartFile::CreateSrcInfoPacket(CUpDownClient* forClient){

	if (forClient->reqfile != this)
		return NULL;

	int sl;
	bool empty=true;
	for (sl=0;sl<SOURCESSLOTS;sl++) if (!srclists[sl].IsEmpty()) {empty=false;break;}
	if (empty) return 0;

	CMemFile data;
	uint16 nCount = 0;

	data.Write(m_abyFileHash,16);
	data.Write(&nCount,2);
	bool bNeeded;
	for (sl=0;sl<SOURCESSLOTS;sl++) if (!srclists[sl].IsEmpty())
	for (POSITION pos = srclists[sl].GetHeadPosition();pos != 0;srclists[sl].GetNext(pos)){
		bNeeded = false;
		CUpDownClient* cur_src = srclists[sl].GetAt(pos);
		if (cur_src->HasLowID())
			continue;
		// 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();
			for (int x = 0; x < GetPartCount(); x++){
				if (srcstatus[x] && !reqstatus[x]){
					bNeeded = true;
					break;
				}
			}
		}*/
		uint8* srcstatus = cur_src->GetPartStatus();
		if (srcstatus){
			uint8* reqstatus = forClient->GetPartStatus();
			if (reqstatus){
				// only send sources which have needed parts for this client
				for (int x = 0; x < GetPartCount(); x++){
					if (srcstatus[x] && !reqstatus[x]){
						bNeeded = true;
						break;
					}
				}
			}
			else{
				// if we don't know the need parts for this client, return any source
				// currently a client sends it's file status only after it has at least one complete part,
				for (int x = 0; x < GetPartCount(); x++){
					if (srcstatus[x]){
						bNeeded = true;
						break;
					}
				}
			}
		}
		if( bNeeded ){
			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 (forClient->GetSourceExchangeVersion() > 1)
				data.Write(cur_src->GetUserHash(),16);
			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();
	if ( theApp.glob_prefs->GetDebugSourceExchange() )
		theApp.emuledlg->AddDebugLogLine( false, "Send:Source User(%s) File(%s) Count(%i)", forClient->GetUserName(), GetFileName(), nCount );
	return result;
}

void CPartFile::AddClientSources(CMemFile* sources, uint8 sourceexchangeversion){
	if (stopped) return;
	uint16 nCount;
	sources->Read(&nCount,2);

	// Check if the data size matches the 'nCount' for v1 or v2 and eventually correct the source
	// exchange version while reading the packet data. Otherwise we could experience a higher
	// chance in dealing with wrong source data, userhashs and finally duplicate sources.
	UINT uDataSize = sources->GetLength() - sources->GetPosition();
	if ((UINT)nCount*(4+2+4+2) == uDataSize)
		sourceexchangeversion = 1;
	else if ((UINT)nCount*(4+2+4+2+16) == uDataSize)
		sourceexchangeversion = 2;
	else{
		// If v3 inserts additional data (like v2), the above code will correctly filter those packets.
		// If v3 appends additional data after <count>(<Sources>)[count], we are in trouble with the 
		// above code. Though a client which does not understand v3+ should never receive such a packet.
		theApp.emuledlg->AddDebugLogLine(false, "Received invalid source exchange packet (v%u) of data size %u for %s\n", sourceexchangeversion, uDataSize, GetFileName());
		return;
	}

	if ( theApp.glob_prefs->GetDebugSourceExchange() )
		theApp.emuledlg->AddDebugLogLine( false, "RCV:Sources File(%s) Count(%i)", GetFileName(), nCount );

	for (int i = 0;i != nCount;i++){
		uint32 dwID;
		uint16 nPort;
		uint32 dwServerIP;
		uint16 nServerPort;
		uchar achUserHash[16];
		sources->Read(&dwID,4);
		sources->Read(&nPort,2);
		sources->Read(&dwServerIP,4);
		sources->Read(&nServerPort,2);
		if (sourceexchangeversion > 1)
			sources->Read(achUserHash,16);
		// 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;
			// although we are currently having a LowID, we could have had a HighID before, which
			// that client is sending us now! seems unlikely ... it happend!
			if (dwID == theApp.serverconnect->GetLocalIP())
				continue;
		}
		else if (theApp.serverconnect->GetClientID() == dwID){
			continue;
		}
		else if (dwID < 16777216)
			continue;
		if( theApp.glob_prefs->GetMaxSourcePerFile() > this->GetSourceCount() ){
			CUpDownClient* newsource = new CUpDownClient(nPort,dwID,dwServerIP,nServerPort,this);
			if (sourceexchangeversion > 1)
				newsource->SetUserHash(achUserHash);
			theApp.downloadqueue->CheckAndAddSource(this,newsource);
		} 
		else
			break;
	}
}

// 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)
{
	// 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 %lu to %lu", 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;

	uint32 partCount = GetPartCount();
	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; (uint32)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;*/
			// SLUGFILLER: SafeHash - could be more than one part
			for (uint32 curpart = item->start/PARTSIZE; curpart <= item->end/PARTSIZE; curpart++)
			changedPart[curpart] = true;
			// SLUGFILLER: SafeHash

			// 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;
		}

		// Partfile should never be too large
		if (m_hpartfile.GetLength() > m_nFileSize){
			// it's "last chance" correction. the real bugfix has to be applied 'somewhere' else
			TRACE("Partfile \"%s\" is too large! Truncating %I64u bytes.\n", GetFileName(), m_hpartfile.GetLength() - m_nFileSize);
			m_hpartfile.SetLength(m_nFileSize);
		}

		// 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->AddLogLine(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)
					{
						SetStatus( 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[MAX_CFEXP_ERRORMSG];
			error->GetErrorMessage(buffer,MAX_CFEXP_ERRORMSG);
			theApp.emuledlg->AddLogLine(true, GetResString(IDS_ERR_WRITEERROR), GetFileName(), buffer);
			SetStatus( PS_ERROR);
		}
		paused = true;
		datarate = 0;
		m_iSrcTransferring = 0; // -khaos--+++> Renamed var
	
		//theApp.emuledlg->transferwnd.downloadlistctrl.UpdateItem(this);
		UpdateDisplayedInfo();
		error->Delete();
	}
	catch(...)
	{
		theApp.emuledlg->AddLogLine(true, GetResString(IDS_ERR_WRITEERROR), GetFileName(), GetResString(IDS_UNKNOWN));
		SetStatus( PS_ERROR);
		paused = true;
		datarate = 0;
		m_iSrcTransferring = 0; // -khaos--+++> Renamed var
		//theApp.emuledlg->transferwnd.downloadlistctrl.UpdateItem(this);
		UpdateDisplayedInfo();
	}
	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=NULL;
	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 = m_nFileSize;
		pos = gaplist.GetHeadPosition();
		while (pos != NULL)
		{
			gap = gaplist.GetNext(pos);
			if ((gap->start > start) && (gap->end < bestEnd))
			{
				best = gap;
				bestEnd = best->end;
				finished = false;
			}
		}

		// TODO: here we have a problem - it occured that eMule crashed because of "best==NULL" while
		// recovering an archive which was currently in "completing" state...
		if (best==NULL){
			ASSERT(0);
			return;
		}

		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 < m_nFileSize)
		{
			gap = new Gap_Struct;
			gap->start = best->end + 1;
			gap->end = m_nFileSize;
			filled->AddTail(gap);
		}
	}
}

void CPartFile::UpdateFileRatingCommentAvail() {
	if (!this) return;

	bool prev=(hasComment || hasRating);
	bool prevbad=hasBadRating;

	hasComment=false;
	int badratings=0;
	int ratings=0;

	POSITION pos1,pos2;
	for (int sl=0;sl<SOURCESSLOTS;sl++) if (!srclists[sl].IsEmpty())
	for (pos1 = srclists[sl].GetHeadPosition();( pos2 = pos1 ) != NULL;){
		srclists[sl].GetNext(pos1);
		CUpDownClient* cur_src = srclists[sl].GetAt(pos2);
		if (cur_src->GetFileComment().GetLength()>0) hasComment=true;
		if (cur_src->GetFileRate()>0) ratings++;
		if (cur_src->GetFileRate()==1) badratings++;
		//if (hasComment && hasRating) break;
	}
	hasBadRating=(badratings> (ratings/3));
	hasRating=(ratings>0);
	if ((prev!=(hasComment || hasRating) ) || (prevbad!=hasBadRating) ) UpdateDisplayedInfo(true);
}

uint16 CPartFile::GetSourceCount() {
	uint16 count=0;
	for (int i=0;i<SOURCESSLOTS;i++) count+=srclists[i].GetCount();
	return count;
}

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

    if(force || curTick-m_lastRefreshedDLDisplay > MINWAIT_BEFORE_DLDISPLAY_WINDOWUPDATE+m_random_update_wait) {
        theApp.emuledlg->transferwnd.downloadlistctrl.UpdateItem(this);
        m_lastRefreshedDLDisplay = curTick;
    }
}

void CPartFile::UpdateAutoDownPriority(){
	if( !IsAutoDownPriority() )
		return;
	if ( GetSourceCount() > 100 ){
		if( GetDownPriority() != PR_LOW )
			SetDownPriority( PR_LOW );
		return;
	}
	if ( GetSourceCount() > 20 ){
		if( GetDownPriority() != PR_NORMAL )
			SetDownPriority( PR_NORMAL );
		return;
	}
	if( GetDownPriority() != PR_HIGH )
		SetDownPriority( PR_HIGH );
}

uint8 CPartFile::GetCategory() {
	if(m_category>theApp.glob_prefs->GetCatCount()-1) m_category=0;
	return m_category;
}


// Ornis: Creating progressive presentation of the partfilestatuses - for webdisplay
CString CPartFile::GetProgressString(uint16 size){
		char crProgress = '0';//green
		char crHave = '1';	// black
		char crPending='2';	// yellow
		char crMissing='3';  // red
		
		char crWaiting[6];
		crWaiting[0]='4'; // blue few source
		crWaiting[1]='5';
		crWaiting[2]='6';
		crWaiting[3]='7';
		crWaiting[4]='8';
		crWaiting[5]='9'; // full sources

		CString my_ChunkBar="";
		for (uint16 i=0;i<=size+1;i++) my_ChunkBar.AppendChar(crHave);	// one more for safety

		uint32 m_nFileSize=GetFileSize();
		float unit= (float)size/(float)m_nFileSize;
		uint32 allgaps = 0;

		if(GetStatus() == PS_COMPLETE || GetStatus() == PS_COMPLETING) {
			CharFillRange(&my_ChunkBar,0,(uint32)(m_nFileSize*unit), crProgress);
		} else

		// 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
					uint8 color;
					if (m_SrcpartFrequency.GetCount() >= (INT_PTR)i && m_SrcpartFrequency[i])  // frequency?
						//color = crWaiting;
						color = m_SrcpartFrequency[i] <  10 ? crWaiting[m_SrcpartFrequency[i]/2]:crWaiting[5];
					else
						color = crMissing;

					CharFillRange(&my_ChunkBar,(uint32)(gapstart*unit), (uint32)(gapend*unit + 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);
			CharFillRange(&my_ChunkBar, (uint32)((block->StartOffset + block->transferred)*unit), (uint32)(block->EndOffset*unit),  crPending);
		}

		return my_ChunkBar;
}

void CPartFile::CharFillRange(CString* buffer,uint32 start, uint32 end, char color) {
	for (uint32 i=start;i<=end;i++)
		buffer->SetAt(i,color);
}

void CPartFile::SetCategory(uint8 cat){
	m_category=cat;
	
	// set new prio
	switch (theApp.glob_prefs->GetCategory(GetCategory())->prio) {
		case 0 : break;
		case 1 : SetAutoDownPriority(false); SetDownPriority(PR_LOW);break;
		case 2 : SetAutoDownPriority(false); SetDownPriority(PR_NORMAL);break;
		case 3 : SetAutoDownPriority(false); SetDownPriority(PR_HIGH);break;
		case 4 : SetAutoDownPriority(true); SetDownPriority(PR_HIGH); break;
	}

	SavePartFile();
}

void CPartFile::SetStatus(uint8 in) {
	status=in;
	if (theApp.emuledlg->transferwnd.downloadlistctrl.curTab==0) theApp.emuledlg->transferwnd.downloadlistctrl.ChangeCategory(0); else UpdateDisplayedInfo(true);
	if (theApp.glob_prefs->ShowCatTabInfos() ) theApp.emuledlg->transferwnd.UpdateCatTabTitles();
}
