#include "StdAfx.h"
#include "MyImgWorker.h"
#include "BKDL.h"

using namespace std;

int CMyImgWorker::m_nRecursionCount = 0;
CMyImgWorker::CMyImgWorker(void)
	: m_bkdImage(NULL)
	, m_strCurrPath(_T("/")) //корень диска.
	, m_bOverflow(FALSE)
	, m_bCSV(FALSE)
{
}


CMyImgWorker::~CMyImgWorker(void)
{
	ClearImgVector();
}

void CMyImgWorker::ClearImgVector(void)
{
	if (m_bkdImage)
	{
		delete m_bkdImage;
		m_bkdImage = NULL;
	}

	while (!m_vpImages.empty())
	{
		CBKDImage *bkdi = m_vpImages.back();
		m_vpImages.pop_back();

		if (bkdi)
		{
			delete bkdi;
		}
	}

	m_strCurrPath = _T("/"); //нужно для открытия нового образа
	m_strImageName.Empty();
}

void CMyImgWorker::PushCurrentImg(void)
{
	m_vpImages.push_back(m_bkdImage);
}

BOOL CMyImgWorker::PopCurrentImg(void)
{
	if (m_vpImages.empty())
	{
		return FALSE;
	}

	if (m_bkdImage)
	{
		delete m_bkdImage;
	}

	m_bkdImage = m_vpImages.back();
	m_vpImages.pop_back();
	return TRUE;
}

BOOL CMyImgWorker::SetImgName(const PARSE_RESULT &image, BOOL bLogDisk)
{
	TCHAR pDrive[_MAX_DRIVE];
	TCHAR pDir[_MAX_DIR];
	TCHAR pName[_MAX_FNAME];
	TCHAR pExt[_MAX_EXT];
	//по идее, теперь в функцию попадаем только если это истина.
	m_strOSName.LoadString(GetImgFormatName(image));

	//поэтому оно всегда истинно.
	if (image.imageOSType != IMAGE_TYPE_ERROR && image.imageOSType != IMAGE_TYPE_UNKNOWN)
	{
		_tsplitpath_s(image.strName, pDrive, _MAX_DRIVE, pDir, _MAX_DIR, pName, _MAX_FNAME, pExt, _MAX_EXT);
		m_strImgPath = CString(pDrive) + CString(pDir);
		m_strImageName = CString(pName) + CString(pExt);
		//теперь в зависимости от типа создаём нужный объект
		m_bkdImage = NULL;

		switch (image.imageOSType)
		{
			case IMAGE_TYPE_ANDOS:
				m_bkdImage = new CBKDAndos(image);
				break;

			case IMAGE_TYPE_DXDOS:
				// тоже использует андосный класс. (он теперь универсальный,
				// понимает любой формат фат12 за исключением правильных каталогов)
				// как только понадобится, на его основе можно будет сделать настоящий понимальщик мсдосного формата
				m_bkdImage = new CBKDAndos(image);
				break;

			case IMAGE_TYPE_MKDOS:
				m_bkdImage = new CBKDMkdos(image);
				break;

			case IMAGE_TYPE_AODOS:
				m_bkdImage = new CBKDAodos(image);
				break;

			case IMAGE_TYPE_NORD:
				m_bkdImage = new CBKDNord(image);
				break;

			case IMAGE_TYPE_MIKRODOS:
				m_bkdImage = new CBKDMikrodos(image);
				break;

			case IMAGE_TYPE_CSIDOS3:
				m_bkdImage = new CBKDCsidos3(image);
				break;

			case IMAGE_TYPE_RT11:
				m_bkdImage = new CBKDRT11(image);
				break;

			case IMAGE_TYPE_NCDOS:
				m_bkdImage = new CBKDNCdos(image);
				break;

			default:
				ASSERT(FALSE);
		}

		//читаем каталог
		m_bkdImage->ReadCatalog();

		//а потом, выводим корневой каталог.
		if (bLogDisk)
		{
			m_vSelItems.push_back(m_PaneInfo);
			m_PaneInfo.clear();
		}
		else
		{
			m_PaneInfo.clear();
			m_vSelItems.clear();
			m_vSelItems.push_back(m_PaneInfo);
		}

		return TRUE;
	}

	return FALSE;
}

BOOL CMyImgWorker::ResetImgName()
{
	//объект у нас создан.
	m_bkdImage->Reset();
	//читаем каталог
	m_bkdImage->ReadCatalog();
	//восстановим старое положение курсора.
	m_PaneInfo = m_vSelItems.back();
	m_vSelItems.pop_back();
	return TRUE;
}

void CMyImgWorker::CloseImage(void)
{
	if (m_bkdImage)
	{
		delete m_bkdImage;
		m_bkdImage = NULL;
	}

	m_PaneInfo.clear();
	m_vSelItems.clear();
	m_vSelItems.push_back(m_PaneInfo);
	m_strCurrPath = _T("/"); //корень диска.
}

void CMyImgWorker::SetCSVParams(BOOL bCSV)
{
	m_bCSV = bCSV;
}

void CMyImgWorker::OutDir(int iCurDir)
{
	if (m_bOverflow)
	{
		return;
	}

	m_nRecursionCount++;

	if (m_nRecursionCount < 32)
	{
		FileRecord cur_fr;
		list<BKDir> l_Dirs;
		BKDir dir;

		if (!m_bCSV)
		{
			_ftprintf(file_lst, _T("[+]%s%s\n"), m_strImageName, m_strCurrPath);
		}

		_ftprintf(file_txt, _T("[+]%s%s\n"), m_strImageName, m_strCurrPath);
		BOOL bEnd;

		if (!(bEnd = m_bkdImage->GetStartFileName(&cur_fr, iCurDir)))
		{
			_ftprintf(file_lst, _T("No files in current directory!\n"));
			_ftprintf(file_txt, _T("No files in current directory!\n"));
		}
		else
		{
			do
			{
				//обрабатываем найденную запись
				if (cur_fr.nAttr & FR_ATTR_DIR)
				{
					if (cur_fr.strName != _T(".") && cur_fr.strName != _T(".."))
					{
						if (m_strCurrPath == _T("/"))
						{
							dir.name = m_strCurrPath + cur_fr.strName;
						}
						else
						{
							dir.name = m_strCurrPath + _T("/") + cur_fr.strName;
						}

						dir.num = cur_fr.nDirDir; //если тут будет 0 то мы получим бесконечное зацикливание
						dir.bLogDisk = FALSE;

						if (dir.num)
						{
							l_Dirs.push_back(dir);
						}
					}
				}
				else
				{
					if (cur_fr.nAttr & FR_ATTR_LOGDISK)
					{
						if (m_strCurrPath == _T("/"))
						{
							dir.name = m_strCurrPath + cur_fr.strName;
						}
						else
						{
							dir.name = m_strCurrPath + _T("/") + cur_fr.strName;
						}

						dir.num = 0;
						dir.bLogDisk = TRUE;
						dir.nBaseOffset = (m_bkdImage->GetBaseOffset() + cur_fr.nStartSector * 512);
						dir.nSize = cur_fr.nSize;
						l_Dirs.push_back(dir);
					}
					else
					{
						if (cur_fr.nAttr & FR_ATTR_DELETED || cur_fr.nAttr & FR_ATTR_BAD)
						{
							//можно вывести информацию об удалённых и плохих файлах.
						}
						else
						{
							if (m_bCSV)
							{
								_ftprintf(file_lst, _T("%s; %s; %s; %s; %s; %s; %s; %04d; %06o; %06o\n"),
								          m_strImgPath,
								          m_strImageName,
								          m_strOSName,
								          (m_bkdImage->isImageBootable() ? _T("Yes") : _T("No")),
								          (m_bkdImage->GetBaseOffset() != 0 ? _T("Yes") : _T("No")),
								          m_strCurrPath,
								          cur_fr.strName,
								          cur_fr.nBlkSize,
								          cur_fr.nAddress,
								          cur_fr.nSize
								         );
							}
							else
							{
								_ftprintf(file_lst, _T("   %-14s = Blk: %04d ; Addr: %06o ; Size: %06o\n"),
								          cur_fr.strName,
								          cur_fr.nBlkSize,
								          cur_fr.nAddress,
								          cur_fr.nSize
								         );
							}

							_ftprintf(file_txt, _T("\t%s\n"), cur_fr.strName);
						}
					}
				}

				bEnd = m_bkdImage->GetNextFileName(&cur_fr, iCurDir);
			}
			while (bEnd);

			if (!m_bCSV)
			{
				_ftprintf(file_lst, _T("\n"));
			}

			_ftprintf(file_txt, _T("\n"));

			//сперва выводим все имена в подкаталоге, а потом обработаем вложенные подкаталоги
			while (!l_Dirs.empty())
			{
				dir = l_Dirs.front();
				m_strCurrPath = dir.name;

				if (dir.bLogDisk)
				{
					CloseCurrentImage();
					PARSE_RESULT pres = g_imgParser.ParseImage(m_bkdImage->GetCurrImgName(), dir.nBaseOffset);
					pres.imageSize = dir.nSize; //корректируем размер логического диска

					if (pres.imageOSType == IMAGE_TYPE_ERROR)
					{
						if (m_bCSV)
						{
							_ftprintf(file_lst, _T("%s Logical Disk - IO Error!\n"), m_bkdImage->GetCurrImgName());
						}
						else
						{
							_ftprintf(file_lst, _T("[*]%s Logical Disk - IO Error!\n"), m_bkdImage->GetCurrImgName());
						}
					}
					else
					{
						if (!m_bCSV)
						{
							CString strFormat;
							strFormat.LoadString(GetImgFormatName(pres));
							_ftprintf(file_lst, _T("[*]%s Logical Disk = Size: %d; Format: %s\n"), m_bkdImage->GetCurrImgName(), dir.nSize, strFormat);
						}

						if (pres.imageOSType != IMAGE_TYPE_UNKNOWN)
						{
							//теперь, если образ опознался, надо создать объект, соответствующий ФС и вывести содержимое
							PushCurrentImg();

							if (SetImgName(pres, TRUE))
							{
								SetCSVParams(m_bCSV);
								OutDir(0);
							}

							CloseCurrentImage();
							PopCurrentImg();
							ResetImgName();
						}
					}
				}
				else
				{
					OutDir(dir.num);
				}

				l_Dirs.pop_front();
			}
		}
	}
	else
	{
		_ftprintf(file_lst, _T("Recursion overflow!\n"));
		_ftprintf(file_txt, _T("Recursion overflow!\n"));
		m_bOverflow = TRUE;
	}

	m_nRecursionCount--;
}


UINT CMyImgWorker::GetImgFormatName(const PARSE_RESULT &image)
{
	switch (image.imageOSType)
	{
		case IMAGE_TYPE_ERROR:
			return (IDS_IMGTYPE_ERROR);

		case IMAGE_TYPE_ANDOS:
			return (IDS_IMGTYPE_ANDOS);

		case IMAGE_TYPE_MKDOS:
			return (IDS_IMGTYPE_MKDOS);

		case IMAGE_TYPE_AODOS:
			return (IDS_IMGTYPE_AODOS);

		case IMAGE_TYPE_NORD:
			return (IDS_IMGTYPE_NORD);

		case IMAGE_TYPE_MIKRODOS:
			return (IDS_IMGTYPE_MICRODOS);

		case IMAGE_TYPE_CSIDOS3:
			return (IDS_IMGTYPE_CSIDOS3);

		case IMAGE_TYPE_RT11:
			return (IDS_IMGTYPE_RT11);

		case IMAGE_TYPE_NCDOS:
			return (IDS_IMGTYPE_NCDOS);

		case IMAGE_TYPE_DXDOS:
			return (IDS_IMGTYPE_DXDOS);

		case IMAGE_TYPE_MSDOS:
			return (IDS_IMGTYPE_MSDOS);

		default:
			return (IDS_IMGTYPE_UNKNOWN);
	}
}
