#include "StdAfx.h"
#include "BKDCsidos3.h"

CBKDCsidos3::CBKDCsidos3(const PARSE_RESULT &image) : CBKDImage(image)
{
}

CBKDCsidos3::~CBKDCsidos3(void)
{
}

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

	CsidosFileRecord curr_record;
	FileRecord curr_fr;

	//Перемещаемся ко 2му сектору
	if (m_sPImg.nBaseOffset + 512 * 2 != m_ImgFile.Seek(m_sPImg.nBaseOffset + 512 * 2, CFile::begin))
	{
		m_nLastErrorNumber = IMAGE_ERROR_CANNOT_SEEK_FILE;
		return FALSE;
	}

	//каталог занимает 8 секторов. но записи идут не с начала сектора, в каждом секторе есть заголовок.
	//длина заголовка 12. байтов, длина записи 20. байтов. в секторе получается 25. записей.
	int used_size = 0;
	int recs_count = 0;
	int sect_count = 8;
	int disk_size = 0;
	m_sDiskCat.nTotalRecs = 25 * sect_count; //это у нас объём каталога, из него надо будет вычесть общее количество записей
	BOOL bEoc = FALSE;
	BOOL bFirstRead = TRUE;

	while ((--sect_count >= 0) && !bEoc)
	{
		//цикл по секторам.
		if (m_ImgFile.Read(m_mSector, 12) != 12)  //читаем заголовок блока каталога
		{
			m_nLastErrorNumber = IMAGE_ERROR_CANNOT_READ_FILE;
			return FALSE;
		}

		if (bFirstRead)
		{
			//если прочитали первый блок каталога, из него надо извлечь размер диска в блоках
			bFirstRead = FALSE;
			disk_size = *(WORD *)&m_mSector[2];
		}

		int rec_count = 25;

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

			if (curr_record.type == 0)
			{
				//конец каталога
				bEoc = TRUE;
				break;
			}

			int nDirBelong = 0;
			curr_fr.clear();

			if (curr_record.type == 0376)
			{
				//дырка
				continue; //игнорируем
			}
			else if (curr_record.type == 0312)
			{
				//запись не содержит информации
				continue; //игнорируем
			}
			else if (curr_record.type == 0377)
			{
				//удалённый файл, который можно восстановить
				curr_fr.nAttr |= FR_ATTR_DELETED;
				recs_count--;
			}
			else if (curr_record.type == 0311)
			{
				//плохое место
				curr_fr.nAttr |= FR_ATTR_BAD;

				if (curr_record.name[0] == 0)
				{
					curr_record.name[0] = 'B';
					curr_record.name[1] = 'A';
					curr_record.name[2] = 'D';
				}
			}
			else if ((curr_record.type >= 1) && (curr_record.type <= 0310))
			{
				// номер директории
				nDirBelong = curr_record.type - 1;
			}

			recs_count++;

			if ((curr_record.start_block | curr_record.address | curr_record.length) == 0)
			{
				//обрабатываем директорию
				curr_fr.nAttr |= FR_ATTR_DIR;
				curr_fr.nRecType = RECORD_TYPE_DIR;
				curr_fr.strName = BKToUNICODE((BYTE *)curr_record.name, 11, koi8tbl10).Trim();
				curr_fr.nDirBelong = nDirBelong;
				curr_fr.nDirNum = curr_record.status - 1;
				curr_fr.nBlkSize = 0;
				curr_fr.nSpecificData = 0;
			}
			else
			{
				//обрабатываем файл
				curr_fr.nRecType = RECORD_TYPE_FILE;
				curr_fr.strName = BKToUNICODE((BYTE *)curr_record.name, 8, koi8tbl10).Trim();
				CString ext = BKToUNICODE((BYTE *)curr_record.name + 8, 3, koi8tbl10).Trim();

				if (!ext.IsEmpty())
				{
					curr_fr.strName += _T(".") + ext;
				}

				curr_fr.nDirBelong = nDirBelong;
				curr_fr.nDirNum = 0;
				curr_fr.nSpecificData = curr_record.status & 0177;
			}

			if ((char)curr_record.protection < 0) //можно было  и так (curr_record.protection & 0200)
			{
				//защита от удаления
				curr_fr.nAttr |= FR_ATTR_PROTECTED;
			}

			curr_fr.nAddress = curr_record.address;

			if (curr_record.status & 0200)
			{
				//признак длины в блоках
				curr_fr.nSize = curr_record.length * 512;
			}
			else
			{
				curr_fr.nSize = curr_record.length;
			}

			if (!(curr_fr.nAttr & FR_ATTR_DELETED) && curr_fr.nSize)
			{
				curr_fr.nBlkSize = (curr_fr.nSize) ? ((((curr_fr.nSize - 1) | 0777) + 1) / 01000) : 0;
				used_size += curr_fr.nBlkSize;
			}

			curr_fr.nStartBlock = curr_record.start_block;
			m_sDiskCat.vecFC.push_back(curr_fr);
		}
	}

	m_sDiskCat.nFreeRecs = m_sDiskCat.nTotalRecs - recs_count;
	m_sDiskCat.nTotalBlocks = disk_size - 012;
	m_sDiskCat.nFreeBlocks = m_sDiskCat.nTotalBlocks - used_size;
	return bEoc;
}

//чтение каталога, целиком, без заголовков в начале блоков
FILESAVE_STATUS CBKDCsidos3::ReadCat(CsidosFileRecord *buf)
{
	FILESAVE_STATUS bRet = FS_IO_ERROR;
	memset(buf, 0, 8 * 25 * sizeof(CsidosFileRecord));
	CsidosFileRecord bcfr;

	//Перемещаемся ко 2му сектору
	if (m_sPImg.nBaseOffset + 512 * 2 == m_ImgFile.Seek(m_sPImg.nBaseOffset + 512 * 2, CFile::begin))
	{
		//читаем каталог
		UINT tc = 0;

		for (int sect_count = 0; sect_count < 8; sect_count++)
		{
			if (m_sPImg.nBaseOffset + 512 * (2 + sect_count) + 12 != m_ImgFile.Seek(12, CFile::current))
			{
				m_nLastErrorNumber = IMAGE_ERROR_CANNOT_SEEK_FILE;
				return bRet;
			}

			for (int rec_count = 0; rec_count < 25; rec_count++)
			{
				if (sizeof(CsidosFileRecord) != m_ImgFile.Read(&bcfr, sizeof(CsidosFileRecord)))
				{
					m_nLastErrorNumber = IMAGE_ERROR_CANNOT_READ_FILE;
					return bRet;
				}

				if (bcfr.type == 0) //если каталог кончился, дальше ничего не делаем.
				{
					return FS_NO_ERROR;
				}

				if (bcfr.type != 0312) //пустые записи из каталога уберём, чтобы не усложнять алгоритмы.
				{
					buf[tc++] = bcfr;
				}
			}
		}

		bRet = FS_NO_ERROR;
	}
	else
	{
		m_nLastErrorNumber = IMAGE_ERROR_CANNOT_SEEK_FILE;
	}

	return bRet;
}

//запись каталога
FILESAVE_STATUS CBKDCsidos3::WriteCat(CsidosFileRecord *buf)
{
	FILESAVE_STATUS bRet = FS_IO_ERROR;

	//Перемещаемся ко 2му сектору
	if (m_sPImg.nBaseOffset + 512 * 2 == m_ImgFile.Seek(m_sPImg.nBaseOffset + 512 * 2, CFile::begin))
	{
		//сохраняем каталог
		UINT tc = 0;

		for (int sect_count = 0; sect_count < 8; sect_count++)
		{
			if (m_sPImg.nBaseOffset + 512 * (2 + sect_count) + 12 != m_ImgFile.Seek(12, CFile::current))
			{
				m_nLastErrorNumber = IMAGE_ERROR_CANNOT_SEEK_FILE;
				return bRet;
			}

			m_ImgFile.Write(buf + tc, 25 * sizeof(CsidosFileRecord));
			tc += 25;
		}

		bRet = FS_NO_ERROR;
	}
	else
	{
		m_nLastErrorNumber = IMAGE_ERROR_CANNOT_SEEK_FILE;
	}

	return bRet;
}
//сравниваем размер найденной дырки с размером файла.
//выход:
// -1 - дырка меньше файла
// 0 - дырка равна файлу
// 1 - дырка больше файла
int CBKDCsidos3::CompareSize(CsidosFileRecord *cfr, UINT fileSize)
{
	UINT fl = (cfr->status & 0200) ? ((fileSize) ? (((fileSize - 1) | 0777) + 1) : 0) / 01000 : fileSize;

	if (cfr->length > fl)
	{
		return 1;
	}
	else if (cfr->length == fl)
	{
		return 0;
	}
	else
	{
		return -1;
	}
}

int CBKDCsidos3::GetLength(CsidosFileRecord *cfr)
{
	if (cfr->status & 0200)
	{
		//длина в блоках
		return cfr->length * 01000;
	}
	else
	{
		return cfr->length;
	}
}

void CBKDCsidos3::SetLength(CsidosFileRecord *cfr, UINT fileSize)
{
	if (fileSize > 65535)
	{
		int nBlkLen = (fileSize) ? (((fileSize - 1) | 0777) + 1) : 0; //размер проги в блоках
		cfr->length = nBlkLen / 01000; //размер в блоках
		cfr->status |= 0200;
	}
	else
	{
		cfr->status &= ~0200;
		cfr->length = fileSize;
	}
}

FILESAVE_STATUS CBKDCsidos3::AddFile(const CString &str, int nDirBelong, BOOL bAddAsBin)
{
	if (m_bFileROMode)
	{
		//Если файл открылся только для чтения,
		return FS_IMAGE_RO; //то записать в него мы ничего не сможем.
	}

	FILESAVE_STATUS bRet = FS_IO_ERROR;
	AnalyseFileStr sAFS;
	sAFS.bUseAsBin = bAddAsBin;
	UINT nCatSize = 8 * 25;
	UINT CatOffs = 0; //смещение в массиве каталога, до первой найденной свободной записи.
	CsidosFileRecord cFR;

	if (sAFS.file.Open(str, CFile::modeRead | CFile::shareDenyWrite))
	{
		if (!AnalyseImportFile(&sAFS))
		{
			return FS_FILE_OPENERR;
		}

		UNICODEtoBK(sAFS.strName, cFR.name, 8, TRUE);
		UNICODEtoBK(sAFS.strExt, &cFR.name[8], 3, TRUE);
		int nBlkLen = (sAFS.nLen) ? (((sAFS.nLen - 1) | 0777) + 1) : 0; //размер проги в блоках
		cFR.type = nDirBelong + 1; //номер текущего открытого подкаталога
		cFR.address = sAFS.nAddr; //возможно, если мы сохраняем бин файл, адрес будет браться оттуда
		cFR.status = 044; //стр 2 в окно 0, стр 4 в окно 1
		SetLength(&cFR, sAFS.nLen);

		if (m_sDiskCat.nFreeBlocks * 01000 < nBlkLen)
		{
			sAFS.file.Close();
			return FS_DISK_FULL;
		}
	}
	else
	{
		m_nLastErrorNumber = IMAGE_ERROR_FILE_CANNOT_OPEN;
		return FS_FILE_OPENERR;
	}

	CsidosFileRecord *pCat = new CsidosFileRecord[nCatSize];

	if (pCat == NULL)
	{
		m_nLastErrorNumber = IMAGE_ERROR_NOT_ENOUGHT_MEMORY;
		return bRet;
	}

	/*
	1. прочитать каталог.
	2. найти в каталоге свободное место
	3. если нашлось, убедиться, что на диске достаточно свободного места
	4. если всё нормально, сохранить файл
	5. создать запись о файле, в текущей директории
	6. поправить константы.
	7. сохранить каталог.
	*/
	//читаем каталог
	if (FS_NO_ERROR == ReadCat(pCat))
	{
		//поищем, вдруг такое имя файла уже существует.
		UINT tc = 0;
		BOOL bFound = FALSE;

		for (int rec_count = 0; rec_count < 8 * 25; rec_count++)
		{
			if (pCat[tc].type == 0)
			{
				break;
			}

			if (!strncmp((char *)cFR.name, (char *)pCat[tc].name, 11))
			{
				bFound = TRUE;
				break;
			}

			tc++;
		}

		if (bFound)
		{
			bRet = FS_FILE_EXIST;
		}
		else
		{
			//найдём свободное место в каталоге
			UINT r377 = 0; //смещение для записи 0377
			CatOffs = 0;
			bFound = FALSE;
			UINT nFirstFreeBlock = 0;

			for (int i = 0; i < 8 * 25; i++)
			{
				if (pCat[CatOffs].type == 0)
				{
					break;    //конец каталога
				}
				else if (pCat[CatOffs].type == 0377 && CompareSize(&pCat[CatOffs], sAFS.nLen) >= 0)
				{
					if (r377 == 0)
					{
						r377 = CatOffs; //используем только первую найденную
					}
				}
				else if (pCat[CatOffs].type == 0376 && CompareSize(&pCat[CatOffs], sAFS.nLen) >= 0)
				{
					bFound = TRUE;
					break;
				}

				CatOffs++;
			}

			//если дырок не нашлось, но нашёлся файл, который можно восстановить,
			//то файл - фтопку, вместо него будет наш новый файл.
			if (!bFound && (r377 != 0))
			{
				bFound = TRUE;
				CatOffs = r377;
			}

			if (bFound)
			{
				nFirstFreeBlock = pCat[CatOffs].start_block;
				//можно сохранять
				cFR.start_block = nFirstFreeBlock;

				//перемещаемся к месту
				if (m_sPImg.nBaseOffset + nFirstFreeBlock * 512 != m_ImgFile.Seek(m_sPImg.nBaseOffset + nFirstFreeBlock * 512, CFile::begin))
				{
					m_nLastErrorNumber = IMAGE_ERROR_CANNOT_SEEK_FILE;
				}

				int nReaded;

				do
				{
					memset(m_mBlock, 0, COPY_BLOCK_SIZE);
					nReaded = sAFS.file.Read(m_mBlock, COPY_BLOCK_SIZE);
					//пишем с выравниванием по секторам.
					m_ImgFile.Write(m_mBlock, ((nReaded) ? (((nReaded - 1) | 0777) + 1) : 0));
					sAFS.nLen -= nReaded;
				}
				while (sAFS.nLen > 0);

				bRet = FS_NO_ERROR;
			}
			else
			{
				bRet = FS_CAT_FULL;
			}
		}
	}

	sAFS.file.Close();

	if (bRet == FS_NO_ERROR)
	{
		//если ошибок не произошло, сохраним результаты
		UINT nHoleSize = GetLength(&pCat[CatOffs]);
		UINT len = GetLength(&cFR);

		if (nHoleSize > len)
		{
			//раздвинем каталог
			UINT tc = 0;

			//найдём конец каталога
			for (int i = 0; i < 8 * 25; i++)
			{
				if (pCat[i].type == 0)
				{
					tc = i;
					break;
				}
			}

			if ((tc == 0) || (tc >= 8 * 25 - 1))
			{
				bRet = FS_CAT_FULL;
			}
			else
			{
				//раздвигаем
				while (tc > CatOffs)
				{
					pCat[tc] = pCat[tc - 1];
					tc--;
				}

				//сохраняем нашу запись
				pCat[CatOffs] = cFR;
				CsidosFileRecord hole;
				hole.type = 0376;
				hole.start_block = cFR.start_block + ((len) ? (((len - 1) | 0777) + 1) : 0) / 01000;
				SetLength(&hole, nHoleSize - len);
				//и запись с инфой о дырке
				pCat[CatOffs + 1] = hole;
			}
		}
		else
		{
			//ничего раздвигать не надо, просто
			//сохраняем нашу запись
			pCat[CatOffs] = cFR;
		}

		if (bRet == FS_NO_ERROR)
		{
			//наконец сохраняем каталог
			bRet = WriteCat(pCat);
		}
	}

	delete [] pCat;
	return bRet;
}

CString CBKDCsidos3::GetSpecificData(DWORD dd)
{
	CString str;

	if (dd)
	{
		if (dd & 010)
		{
			int p0 = (dd >> 4) & 7;
			int p1 = dd & 7;
			str.Format(_T("БК10; %d:%d"), p0, p1);
		}
		else
		{
			int p0 = (dd >> 4) & 7;
			int p1 = dd & 7;
			str.Format(_T("БК11; %d:%d"), p0, p1);
		}
	}
	else
	{
		str = _T("");
	}

	return str;
}

