Index: src/ch/task.cpp
===================================================================
diff -u -N -r69b48f0b4d7fad78f95854e95fca166014311474 -r16a61d123d45e60dea731a6620f6f47acccd8c43
--- src/ch/task.cpp	(.../task.cpp)	(revision 69b48f0b4d7fad78f95854e95fca166014311474)
+++ src/ch/task.cpp	(.../task.cpp)	(revision 16a61d123d45e60dea731a6620f6f47acccd8c43)
@@ -31,6 +31,8 @@
 #include "TTaskConfiguration.h"
 #include "TSubTaskContext.h"
 
+#include "Device IO.h"
+
 // assume max sectors of 4kB (for rounding)
 #define MAXSECTORSIZE			4096
 
@@ -417,7 +419,8 @@
 CTask::CTask(chcore::IFeedbackHandler* piFeedbackHandler, size_t stSessionUniqueID) :
 	m_log(),
 	m_piFeedbackHandler(piFeedbackHandler),
-	m_files(m_arrSourcePaths),
+	m_arrSourcePathsInfo(m_tTaskDefinition.GetSourcePaths()),
+	m_files(m_tTaskDefinition.GetSourcePaths()),
 	m_bForce(false),
 	m_bContinue(false),
 	m_bRareStateModified(false),
@@ -440,16 +443,8 @@
 {
 	m_tTaskDefinition = rTaskDefinition;
 
-	m_arrSourcePaths.RemoveAll();
+	m_arrSourcePathsInfo.SetCount(m_tTaskDefinition.GetSourcePathCount());
 	m_files.Clear();
-
-	for(size_t stIndex = 0; stIndex < m_tTaskDefinition.GetSourcePaths().GetCount(); ++stIndex)
-	{
-		CClipboardEntryPtr spEntry(new CClipboardEntry);
-		spEntry->SetPath(m_tTaskDefinition.GetSourcePaths().GetAt(stIndex));
-
-		m_arrSourcePaths.Add(spEntry);
-	}
 }
 
 void CTask::OnRegisterTask(TTasksGlobalStats& rtGlobalStats)
@@ -482,7 +477,7 @@
 			if(!(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
 			{
 				CFileInfoPtr spFileInfo(boost::make_shared<CFileInfo>());
-				spFileInfo->SetClipboard(&m_arrSourcePaths);	// this is the link table (CClipboardArray)
+				spFileInfo->SetClipboard(&m_tTaskDefinition.GetSourcePaths());	// this is the link table (CClipboardArray)
 				
 				spFileInfo->Create(&wfd, pathDirName, stSrcIndex);
 				if(m_afFilters.Match(spFileInfo))
@@ -493,7 +488,7 @@
 				if(bIncludeDirs)
 				{
 					CFileInfoPtr spFileInfo(boost::make_shared<CFileInfo>());
-					spFileInfo->SetClipboard(&m_arrSourcePaths);	// this is the link table (CClipboardArray)
+					spFileInfo->SetClipboard(&m_tTaskDefinition.GetSourcePaths());	// this is the link table (CClipboardArray)
 
 					// Add directory itself
 					spFileInfo->Create(&wfd, pathDirName, stSrcIndex);
@@ -555,7 +550,7 @@
 
 int CTask::GetCurrentBufferIndex()
 {
-	return m_files.GetBufferIndexAt(m_tTaskBasicProgressInfo.GetCurrentIndex(), m_tTaskDefinition.GetDestinationPath());
+	return GetBufferIndex(m_files.GetAt(m_tTaskBasicProgressInfo.GetCurrentIndex()));
 }
 
 // thread
@@ -598,13 +593,16 @@
 	m_tTaskDefinition.Load(strPath);
 	m_strFilePath = strPath;
 
+	// make sure to resize paths info array size to match source paths count
+	m_arrSourcePathsInfo.SetCount(m_tTaskDefinition.GetSourcePathCount());
+
 	////////////////////////////////
 	// now rarely changing task progress data
 	CString strRarelyChangingPath = GetRelatedPathNL(ePathType_TaskRarelyChangingState);
 	std::ifstream ifs(strRarelyChangingPath, ios_base::in | ios_base::binary);
 	boost::archive::binary_iarchive ar(ifs);
 
-	m_arrSourcePaths.Load(ar, 0, true);
+	m_arrSourcePathsInfo.Load(ar, 0, true);
 	m_files.Load(ar, 0, false);
 
 	CalculateTotalSizeNL();
@@ -640,7 +638,7 @@
 	ar2 >> timeElapsed;
 	m_localStats.SetTimeElapsed(timeElapsed);
 
-	m_arrSourcePaths.Load(ar2, 0, false);
+	m_arrSourcePathsInfo.Load(ar2, 0, false);
 	m_files.Load(ar2, 0, true);
 }
 
@@ -668,7 +666,7 @@
 		std::ofstream ofs(GetRelatedPathNL(ePathType_TaskRarelyChangingState), ios_base::out | ios_base::binary);
 		boost::archive::binary_oarchive ar(ofs);
 
-		m_arrSourcePaths.Store(ar, 0, true);
+		m_arrSourcePathsInfo.Store(ar, 0, true);
 		m_files.Store(ar, 0, false);
 
 		ar << m_afFilters;
@@ -691,7 +689,7 @@
 		time_t timeElapsed = m_localStats.GetTimeElapsed();
 		ar << timeElapsed;
 
-		m_arrSourcePaths.Store(ar, 0, false);
+		m_arrSourcePathsInfo.Store(ar, 0, false);
 
 		ESubOperationType eSubOperation = m_tTaskDefinition.GetOperationPlan().GetSubOperationAt(m_tTaskBasicProgressInfo.GetSubOperationIndex());
 		if(eSubOperation != eSubOperation_Scanning)
@@ -788,7 +786,7 @@
 		else
 		{
 			if(m_tTaskDefinition.GetSourcePathCount() > 0)
-				pData->m_strPath = m_arrSourcePaths.GetAt(0)->GetFileName().ToString();
+				pData->m_strPath = m_tTaskDefinition.GetSourcePathAt(0).GetLastComponent().ToString();
 			else
 				pData->m_strPath.Empty();
 		}
@@ -821,8 +819,8 @@
 		{
 			if(m_tTaskDefinition.GetSourcePathCount() > 0)
 			{
-				pData->m_strFullFilePath = m_arrSourcePaths.GetAt(0)->GetPath().ToString();
-				pData->m_strFileName = m_arrSourcePaths.GetAt(0)->GetFileName().ToString();
+				pData->m_strFullFilePath = m_tTaskDefinition.GetSourcePathAt(0).ToString();
+				pData->m_strFileName = m_tTaskDefinition.GetSourcePathAt(0).GetLastComponent().ToString();
 			}
 			else
 			{
@@ -848,9 +846,9 @@
 	pData->m_bCreateEmptyFiles = GetTaskPropValue<eTO_CreateEmptyFiles>(m_tTaskDefinition.GetConfiguration());
 
 	if(m_files.GetSize() > 0)
-		pData->m_iCurrentBufferIndex = GetTaskPropValue<eTO_UseOnlyDefaultBuffer>(m_tTaskDefinition.GetConfiguration()) ? 0 : m_files.GetAt((stCurrentIndex < m_files.GetSize()) ? stCurrentIndex : 0)->GetBufferIndex(m_tTaskDefinition.GetDestinationPath());
+		pData->m_iCurrentBufferIndex = GetTaskPropValue<eTO_UseOnlyDefaultBuffer>(m_tTaskDefinition.GetConfiguration()) ? BI_DEFAULT : GetBufferIndex(m_files.GetAt((stCurrentIndex < m_files.GetSize()) ? stCurrentIndex : 0));
 	else
-		pData->m_iCurrentBufferIndex = 0;
+		pData->m_iCurrentBufferIndex = BI_DEFAULT;
 
 	switch(pData->m_iCurrentBufferIndex)
 	{
@@ -1071,18 +1069,18 @@
 		bSkipInputPath = false;
 
 		spFileInfo.reset(new CFileInfo());
-		spFileInfo->SetClipboard(&m_arrSourcePaths);
+		spFileInfo->SetClipboard(&m_tTaskDefinition.GetSourcePaths());
 
 		// try to get some info about the input path; let user know if the path does not exist.
 		do
 		{
 			bRetry = false;
 
 			// read attributes of src file/folder
-			bool bExists = spFileInfo->Create(m_arrSourcePaths.GetAt(stIndex)->GetPath(), stIndex);
+			bool bExists = spFileInfo->Create(m_tTaskDefinition.GetSourcePathAt(stIndex), stIndex);
 			if(!bExists)
 			{
-				CString strSrcFile = m_arrSourcePaths.GetAt(stIndex)->GetPath().ToString();
+				CString strSrcFile = m_tTaskDefinition.GetSourcePathAt(stIndex).ToString();
 				FEEDBACK_FILEERROR ferr = { (PCTSTR)strSrcFile, NULL, eFastMoveError, ERROR_FILE_NOT_FOUND };
 				CFeedbackHandler::EFeedbackResult frResult = (CFeedbackHandler::EFeedbackResult)m_piFeedbackHandler->RequestFeedback(CFeedbackHandler::eFT_FileError, &ferr);
 				switch(frResult)
@@ -1117,20 +1115,20 @@
 
 		// log
 		fmt.SetFormat(_T("Adding file/folder (clipboard) : %path ..."));
-		fmt.SetParam(_t("%path"), m_arrSourcePaths.GetAt(stIndex)->GetPath().ToString());
+		fmt.SetParam(_t("%path"), m_tTaskDefinition.GetSourcePathAt(stIndex).ToString());
 		m_log.logi(fmt);
 
 		// found file/folder - check if the dest name has been generated
-		if(!m_arrSourcePaths.GetAt(stIndex)->IsDestinationPathSet())
+		if(!m_arrSourcePathsInfo.GetAt(stIndex)->IsDestinationPathSet())
 		{
 			// generate something - if dest folder == src folder - search for copy
 			if(m_tTaskDefinition.GetDestinationPath() == spFileInfo->GetFileRoot())
 			{
 				chcore::TSmartPath pathSubst = FindFreeSubstituteName(spFileInfo->GetFullFilePath(), m_tTaskDefinition.GetDestinationPath());
-				m_arrSourcePaths.GetAt(stIndex)->SetDestinationPath(pathSubst);
+				m_arrSourcePathsInfo.GetAt(stIndex)->SetDestinationPath(pathSubst);
 			}
 			else
-				m_arrSourcePaths.GetAt(stIndex)->SetDestinationPath(spFileInfo->GetFileName());
+				m_arrSourcePathsInfo.GetAt(stIndex)->SetDestinationPath(spFileInfo->GetFileName());
 		}
 
 		// add if needed
@@ -1149,7 +1147,7 @@
 			}
 
 			// don't add folder contents when moving inside one disk boundary
-			if(bIgnoreDirs || !bMove || iDestDrvNumber == -1 || iDestDrvNumber != spFileInfo->GetDriveNumber() ||
+			if(bIgnoreDirs || !bMove || iDestDrvNumber == -1 || iDestDrvNumber != GetDriveNumber(spFileInfo) ||
 				CFileInfo::Exist(GetDestinationPath(spFileInfo, m_tTaskDefinition.GetDestinationPath(), ((int)bForceDirectories) << 1)) )
 			{
 				// log
@@ -1158,7 +1156,7 @@
 				m_log.logi(fmt);
 
 				// no movefile possibility - use CustomCopyFileFB
-				m_arrSourcePaths.GetAt(stIndex)->SetMove(false);
+				m_arrSourcePathsInfo.GetAt(stIndex)->SetMove(false);
 
 				ScanDirectory(spFileInfo->GetFullFilePath(), stIndex, true, !bIgnoreDirs || bForceDirectories);
 			}
@@ -1174,15 +1172,15 @@
 		}
 		else
 		{
-			if(bMove && iDestDrvNumber != -1 && iDestDrvNumber == spFileInfo->GetDriveNumber() &&
+			if(bMove && iDestDrvNumber != -1 && iDestDrvNumber == GetDriveNumber(spFileInfo) &&
 				!CFileInfo::Exist(GetDestinationPath(spFileInfo, m_tTaskDefinition.GetDestinationPath(), ((int)bForceDirectories) << 1)) )
 			{
 				// if moving within one partition boundary set the file size to 0 so the overall size will
 				// be ok
 				spFileInfo->SetLength64(0);
 			}
 			else
-				m_arrSourcePaths.GetAt(stIndex)->SetMove(false);	// no MoveFile
+				m_arrSourcePathsInfo.GetAt(stIndex)->SetMove(false);	// no MoveFile
 
 			// add file info if passes filters
 			if(m_afFilters.Match(spFileInfo))
@@ -1928,7 +1926,7 @@
 			if(GetTaskPropValue<eTO_UseOnlyDefaultBuffer>(m_tTaskDefinition.GetConfiguration()))
 				iBufferIndex = BI_DEFAULT;
 			else
-				iBufferIndex = pData->spSrcFile->GetBufferIndex(m_tTaskDefinition.GetDestinationPath());
+				iBufferIndex = GetBufferIndex(pData->spSrcFile);
 
 			ulToRead = bNoBuffer ? ROUNDUP(pData->dbBuffer.GetSizes()->m_auiSizes[iBufferIndex], MAXSECTORSIZE) : pData->dbBuffer.GetSizes()->m_auiSizes[iBufferIndex];
 
@@ -2118,7 +2116,7 @@
 		bool bMove = m_tTaskDefinition.GetOperationType() == eOperation_Move;
 		if(bMove)
 			GetDriveData(m_tTaskDefinition.GetDestinationPath(), &iDstDriveNumber, NULL);
-		if(bMove && iDstDriveNumber != -1 && iDstDriveNumber == spFileInfo->GetDriveNumber() && spFileInfo->GetMove())
+		if(bMove && iDstDriveNumber != -1 && iDstDriveNumber == GetDriveNumber(spFileInfo) && GetMove(spFileInfo))
 		{
 			bool bRetry = true;
 			if(bRetry && !MoveFile(spFileInfo->GetFullFilePath().ToString(), ccp.pathDstFile.ToString()))
@@ -2292,7 +2290,7 @@
 
 			if(m_tTaskDefinition.GetSourcePathCount() > 0)
 			{
-				FEEDBACK_NOTENOUGHSPACE feedStruct = { ullNeededSize, m_arrSourcePaths.GetAt(0)->GetPath().ToString(), m_tTaskDefinition.GetDestinationPath().ToString() };
+				FEEDBACK_NOTENOUGHSPACE feedStruct = { ullNeededSize, m_tTaskDefinition.GetSourcePathAt(0).ToString(), m_tTaskDefinition.GetDestinationPath().ToString() };
 				CFeedbackHandler::EFeedbackResult frResult = (CFeedbackHandler::EFeedbackResult)m_piFeedbackHandler->RequestFeedback(CFeedbackHandler::eFT_NotEnoughSpace, &feedStruct);
 
 				// default
@@ -2646,19 +2644,106 @@
 		if (!(iFlags & 0x01) && stSrcIndex != std::numeric_limits<size_t>::max())
 		{
 			// generate new dest name
-			if(!m_arrSourcePaths.GetAt(stSrcIndex)->IsDestinationPathSet())
+			if(!m_arrSourcePathsInfo.GetAt(stSrcIndex)->IsDestinationPathSet())
 			{
 				chcore::TSmartPath pathSubst = FindFreeSubstituteName(spFileInfo->GetFullFilePath(), pathDst);
-				m_arrSourcePaths.GetAt(stSrcIndex)->SetDestinationPath(pathSubst);
+				m_arrSourcePathsInfo.GetAt(stSrcIndex)->SetDestinationPath(pathSubst);
 			}
 
-			return pathDst + m_arrSourcePaths.GetAt(stSrcIndex)->GetDestinationPath() + spFileInfo->GetFilePath();
+			return pathDst + m_arrSourcePathsInfo.GetAt(stSrcIndex)->GetDestinationPath() + spFileInfo->GetFilePath();
 		}
 		else
 			return pathDst + spFileInfo->GetFileName();
 	}
 }
 
+int CTask::GetBufferIndex(const CFileInfoPtr& spFileInfo)
+{
+	if(!spFileInfo)
+		THROW(_T("Invalid pointer"), 0, 0, 0);
+	if(spFileInfo->GetSrcIndex() == std::numeric_limits<size_t>::max())
+		THROW(_T("Received non-relative (standalone) path info"), 0, 0, 0);
+
+	// check if this information has already been stored
+	size_t stBaseIndex = spFileInfo->GetSrcIndex();
+	if(stBaseIndex >= m_arrSourcePathsInfo.GetCount())
+		THROW(_T("Index out of bounds"), 0, 0, 0);
+
+	TBasePathDataPtr spPathData = m_arrSourcePathsInfo.GetAt(stBaseIndex);
+	if(spPathData->IsBufferIndexSet())
+		return spPathData->GetBufferIndex();
+
+	// buffer index wasn't cached previously - read it now
+	int iDriveNumber = 0;
+	UINT uiDriveType = 0;
+	int iDstDriveNumber = 0;
+	UINT uiDstDriveType = 0;
+	GetDriveData(m_tTaskDefinition.GetSourcePathAt(stBaseIndex), &iDriveNumber, &uiDriveType);
+	GetDriveData(m_tTaskDefinition.GetDestinationPath(), &iDstDriveNumber, &uiDstDriveType);
+
+	// what kind of buffer
+	int iBufferIndex = BI_DEFAULT;
+	if(uiDriveType == DRIVE_REMOTE || uiDstDriveType == DRIVE_REMOTE)
+		iBufferIndex = BI_LAN;
+	else if(uiDriveType == DRIVE_CDROM || uiDstDriveType == DRIVE_CDROM)
+		iBufferIndex = BI_CD;
+	else if(uiDriveType == DRIVE_FIXED && uiDstDriveType == DRIVE_FIXED)
+	{
+		// two hdd's - is this the same physical disk ?
+		if(iDriveNumber == iDstDriveNumber || IsSamePhysicalDisk(iDriveNumber, iDstDriveNumber))
+			iBufferIndex = BI_ONEDISK;
+		else
+			iBufferIndex = BI_TWODISKS;
+	}
+	else
+		iBufferIndex = BI_DEFAULT;
+
+	spPathData->SetBufferIndex(iBufferIndex);
+
+	return iBufferIndex;
+}
+
+int CTask::GetDriveNumber(const CFileInfoPtr& spFileInfo)
+{
+	if(!spFileInfo)
+		THROW(_T("Invalid pointer"), 0, 0, 0);
+	if(spFileInfo->GetSrcIndex() == std::numeric_limits<size_t>::max())
+		THROW(_T("Received non-relative (standalone) path info"), 0, 0, 0);
+
+	// check if this information has already been stored
+	size_t stBaseIndex = spFileInfo->GetSrcIndex();
+	if(stBaseIndex >= m_arrSourcePathsInfo.GetCount())
+		THROW(_T("Index out of bounds"), 0, 0, 0);
+
+	TBasePathDataPtr spPathData = m_arrSourcePathsInfo.GetAt(stBaseIndex);
+	if(spPathData->IsDriveNumberSet())
+		return spPathData->GetDriveNumber();
+
+	// buffer index wasn't cached previously - read it now
+	int iDriveNumber = 0;
+	GetDriveData(m_tTaskDefinition.GetSourcePathAt(stBaseIndex), &iDriveNumber, NULL);
+
+	spPathData->SetDriveNumber(iDriveNumber);
+
+	return iDriveNumber;
+}
+
+bool CTask::GetMove(const CFileInfoPtr& spFileInfo)
+{
+	if(!spFileInfo)
+		THROW(_T("Invalid pointer"), 0, 0, 0);
+	if(spFileInfo->GetSrcIndex() == std::numeric_limits<size_t>::max())
+		THROW(_T("Received non-relative (standalone) path info"), 0, 0, 0);
+
+	// check if this information has already been stored
+	size_t stBaseIndex = spFileInfo->GetSrcIndex();
+	if(stBaseIndex >= m_arrSourcePathsInfo.GetCount())
+		THROW(_T("Index out of bounds"), 0, 0, 0);
+
+	TBasePathDataPtr spPathData = m_arrSourcePathsInfo.GetAt(stBaseIndex);
+	return spPathData->GetMove();
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // CTaskArray members
 CTaskArray::CTaskArray() :