#include "StdAfx.h"
#include "BKDNCdos.h"

CBKDNCdos::CBKDNCdos(const PARSE_RESULT &image) : CBKDImage(image)
{
	m_pDiskMap = new BYTE[DISKMAP_SIZE];
}

CBKDNCdos::~CBKDNCdos(void)
{
	if (m_pDiskMap)
	{
		delete [] m_pDiskMap;
	}
}

BOOL CBKDNCdos::ReadCatalog()
{
	if (!CBKDImage::ReadCatalog())
	{
		return FALSE;
	}

	NCdosFileRecord curr_record;
	FileRecord curr_fr;

	if (m_pDiskMap == NULL)
	{
		m_nLastErrorNumber = IMAGE_ERROR_NOT_ENOUGHT_MEMORY;
		return FALSE;
	}

	memset(m_pDiskMap, 0, DISKMAP_SIZE);

	//первым делом надо прочесть карту диска.
	//Перемещаемся к 1му сектору
	if (m_sParseImgResult.nBaseOffset + 512 != m_FloppyImgFile.Seek(m_sParseImgResult.nBaseOffset + 512, CFile::begin))
	{
		m_nLastErrorNumber = IMAGE_ERROR_CANNOT_SEEK_FILE;
		return FALSE;
	}

	if (DISKMAP_SIZE != m_FloppyImgFile.Read(m_pDiskMap, DISKMAP_SIZE))
	{
		m_nLastErrorNumber = IMAGE_ERROR_CANNOT_READ_FILE;
		return FALSE;
	}

	//сразу за картой идёт каталог, будем читать его.
	//прочтём первую запись, она нам не нужна
	if (m_FloppyImgFile.Read(&curr_record, sizeof(NCdosFileRecord)) != sizeof(NCdosFileRecord))
	{
		m_nLastErrorNumber = IMAGE_ERROR_CANNOT_READ_FILE;
		return FALSE; //если даже каталог не прочёлся, а файл уже кончился, то образ повреждён,
		//и делать с ним нечего.
	}

	int used_size = 0;
	int files_total = 2;

	while (1)
	{
		if (m_FloppyImgFile.Read(&curr_record, sizeof(NCdosFileRecord)) != sizeof(NCdosFileRecord))
		{
			m_nLastErrorNumber = IMAGE_ERROR_CANNOT_READ_FILE;
			return FALSE; //если даже каталог не прочёлся, а файл уже кончился, то образ повреждён,
			//и делать с ним нечего.
		}

		if (curr_record.name[0] == 0)
		{
			break; //теоретически это вообще конец каталога
		}

		curr_fr.clear();

		if (curr_record.name[12] == '.')
		{
			curr_fr.strName = BKToUNICODE((BYTE *)curr_record.name, 12, koi8tbl10).Trim();
			curr_fr.strName += BKToUNICODE((BYTE *)curr_record.name + 12, 4, koi8tbl10).Trim();
		}
		else
		{
			curr_fr.strName = BKToUNICODE((BYTE *)curr_record.name, 16, koi8tbl10).Trim();
		}

		curr_fr.nRecType = RECORD_TYPE_FILE;
		curr_fr.nDirBelong = 0;
		curr_fr.nDirNum = 0;
		curr_fr.nAddress = curr_record.address;
		curr_fr.nSize = curr_record.length;
		curr_fr.nBlkSize = (curr_fr.nSize) ? ((((curr_fr.nSize - 1) | 0777) + 1) / 01000) : 0;
		used_size += curr_fr.nBlkSize;
		//в качестве начального сектора. будем передавать номер файла
		curr_fr.nStartBlock = files_total;
		int n = GetStartBlock(files_total);

		if (m_pDiskMap[n] & 0x80)
		{
			curr_fr.nAttr |= FR_ATTR_PROTECTED;
		}

		m_sDiskCat.vecFC.push_back(curr_fr);
		files_total++;
	}

	m_sDiskCat.nTotalRecs = ((010000 - 02440) / sizeof(NCdosFileRecord)) - 1;
	m_sDiskCat.nFreeRecs = m_sDiskCat.nTotalRecs - files_total;
	int imgSize = max(1600, (int)m_sParseImgResult.nImageSize / 512); //неизвестно как определяется размер диска в блоках,
	m_sDiskCat.nTotalBlocks = imgSize - 8; //будем высчитывать это по размеру образа.
	m_sDiskCat.nFreeBlocks = m_sDiskCat.nTotalBlocks - used_size;
	return TRUE;
}

BOOL CBKDNCdos::ExtractFile(FileRecord *sFile, BOOL bExtractAsBin, BOOL bExtractLog)
{
	m_nLastErrorNumber = IMAGE_ERROR_OK;
	BOOL bRet = TRUE;

	//если /*плохой или удалённый или*/ директория - ничего не делаем
	if (sFile->nAttr & FR_ATTR_DIR)
	{
		return bRet;
	}

	AnalyseFileStr sAFS;
	sAFS.bUseAsBin = bExtractAsBin;
	sAFS.nAddr = sFile->nAddress;
	sAFS.nLen = sFile->nSize;
	sAFS.strName = sFile->strName;
	SetSafeName(sAFS.strName);
	sAFS.strName += ((bExtractAsBin) ? _T(".bin") : _T(""));

	if (sAFS.file.Open(m_strStorePath + sAFS.strName, CFile::modeCreate | CFile::modeReadWrite))
	{
		m_currBlock = GetStartBlock(sFile->nStartBlock);
		int len = sFile->nSize;
		int out_len;

		if (bExtractAsBin)
		{
			m_mBinHeader[0] = (WORD)sFile->nAddress;
			m_mBinHeader[1] = (WORD)sFile->nSize; //и размер файла, если было больше 64кб, тут усекается.
			sAFS.file.Write(m_mBinHeader, sizeof(m_mBinHeader));
		}

		while ((len > 0) && (m_currBlock != 0))
		{
			SeekToBlock(&m_FloppyImgFile);
			m_FloppyImgFile.Read(m_mBlock, BLOCK_SIZE);

			if (len >= BLOCK_SIZE)
			{
				out_len = BLOCK_SIZE;
			}
			else
			{
				out_len = len;
			}

			sAFS.file.Write(m_mBlock, out_len);
			len -= out_len;
			m_currBlock = GetNextBlock(m_currBlock);
		}

		if (bRet)
		{
			if (bExtractLog)
			{
				bRet = AnalyseExportFile(&sAFS);
			}
		}

		sAFS.file.Close();
	}
	else
	{
		m_nLastErrorNumber = IMAGE_ERROR_FILE_CANNOT_CREATE;
		bRet = FALSE;
	}

	return bRet;
}

BOOL CBKDNCdos::SeekToBlock(CFile *f)
{
	BOOL bRet = TRUE;
	m_nLenBlock = BLOCK_SIZE;

	if (m_currBlock)
	{
		//int sector = m_currBlock * 2;
		m_tPos = f->Seek(m_sParseImgResult.nBaseOffset + m_currBlock * 1024, CFile::begin);

		if (m_tPos != m_sParseImgResult.nBaseOffset + m_currBlock * 1024)
		{
			m_nLastErrorNumber = IMAGE_ERROR_CANNOT_SEEK_FILE;
			bRet = FALSE;
		}
	}

	return bRet;
}

int CBKDNCdos::GetStartBlock(int fn)
{
	int n = 0; //позиция в карте диска
	BOOL bfind = FALSE;

	while (n < DISKMAP_SIZE)
	{
		if ((m_pDiskMap[n] & 0x7f) == fn)
		{
			bfind = TRUE;
			break;
		}

		n++;
	}

	if (bfind)
	{
		return n;
	}

	return 0;
}

int CBKDNCdos::GetNextBlock(int blk)
{
	int fn = m_pDiskMap[blk] & 0x7f;
	int n = blk + 1; //позиция в карте диска
	BOOL bfind = FALSE;

	while (n < DISKMAP_SIZE)
	{
		if ((m_pDiskMap[n] & 0x7f) == fn)
		{
			bfind = TRUE;
			break;
		}

		n++;
	}

	if (bfind)
	{
		return n;
	}

	return 0;
}

int CBKDNCdos::SeekToPos(CFile *f, int pos)
{
	//если позиция больше чем размер кластера, сперва перемещаемся по кластерам
	while (pos > BLOCK_SIZE)
	{
		pos -= BLOCK_SIZE;
		m_currBlock = GetNextBlock(m_currBlock);
		m_tLen -= BLOCK_SIZE;
	}

	SeekToBlock(f);

	//теперь переместимся в пределах кластера
	if (m_tPos + pos != m_FloppyImgFile.Seek(m_tPos + pos, CFile::begin))
	{
		m_nLastErrorNumber = IMAGE_ERROR_CANNOT_SEEK_FILE;
	}

	m_nLenBlock -= pos;
	return m_tLen - pos;
}

BOOL CBKDNCdos::SeekToBeginFile(CFile *f, FileRecord *fr)
{
	m_currBlock = GetStartBlock(fr->nStartBlock);
	return SeekToBlock(f);
}

BYTE CBKDNCdos::GetByte(CFile *f, int *len)
{
	BYTE b = CBKDImage::GetByte(f, len);
	m_nLenBlock -= sizeof(b); //уменьшаем счётчик байтов в кластере

	if (m_nLenBlock <= 0)
	{
		//пора переходить к следующему кластеру
		m_currBlock = GetNextBlock(m_currBlock);
		SeekToBlock(f);
	}

	return b;
}

WORD CBKDNCdos::GetWord(CFile *f, int *len)
{
	WORD w = CBKDImage::GetWord(f, len);
	m_nLenBlock -= sizeof(w); //уменьшаем счётчик байтов в кластере

	if (m_nLenBlock <= 0)
	{
		//пора переходить к следующему кластеру
		m_currBlock = GetNextBlock(m_currBlock);
		SeekToBlock(f);
	}

	return w;
}



