#include "stdafx.h"
#include "BKFloppyImage_NORD.h"


CBKFloppyImage_Nord::CBKFloppyImage_Nord(const PARSE_RESULT &image)
	: CBKFloppyImage_Prototype(image)
{
	m_nVersion = 0;
	m_nCatSize = 10 * m_nBlockSize; //размер каталога - нижняя сторона нулевой дорожки
	m_pCatBuffer = new uint8_t[m_nCatSize]; //необязательно выделять массив во время чтения каталога, размер-то его известен
	m_nMKCatSize = (012000 - FMT_NORD_CAT_BEGIN) / sizeof(NordFileRecord);
	m_nMKLastCatRecord = m_nMKCatSize - 1;
	m_bMakeAdd = true;
	m_bMakeDel = true;
	m_bMakeRename = true;
}


CBKFloppyImage_Nord::~CBKFloppyImage_Nord()
{
	if (m_pCatBuffer)
	{
		delete[] m_pCatBuffer;
	}
}


bool CBKFloppyImage_Nord::IsEndOfCat(NordFileRecord *pNDRec)
{
	//для норд3.5 надо проверку на выход делать как в мкдосе, тут возможен глюк.
	// конец каталога необязательно 0, там может быть мусор, в этом случае будет мусор и прочитан.
	if (pNDRec->status == 0 && pNDRec->name[0] == 0)
	{
		return true; //теоретически это конец каталога
	}

	//вот ещё одна проверка на конец каталога.
	if (pNDRec->status == 0377 && pNDRec->dir_num == 0377 && pNDRec->name[0] == 055 && pNDRec->name[1] == 077)
	{
		return true; //теоретически это конец каталога
	}

	//и ещё одна проверка на конец каталога, в норде с этим бардак
	if (pNDRec->status == 0377 && pNDRec->len_blk == 0177777 && pNDRec->address == 0177777 && pNDRec->length == 0177777)
	{
		return true; //теоретически это конец каталога
	}

	return false;
}


int CBKFloppyImage_Nord::FindRecord(NordFileRecord *pNDRec)
{
	int nIndex = -1;
	auto pDiskCat = reinterpret_cast<NordFileRecord *>(m_pCatBuffer + FMT_NORD_CAT_BEGIN); //каталог диска

	for (int i = 0; i < m_nMKLastCatRecord; ++i) //цикл по всему каталогу
	{
		if (IsEndOfCat(&pDiskCat[i]))
		{
			break;
		}

		if (pDiskCat[i].dir_num == m_sDiskCat.nCurrDirNum) //ищем только в текущей директории
		{
			if (memcmp(&pDiskCat[i], pNDRec, sizeof(NordFileRecord)) == 0)
			{
				nIndex = i;
				break;
			}
		}
	}

	return nIndex;
}

int CBKFloppyImage_Nord::FindRecord2(NordFileRecord *pNDRec, bool bFull)
{
	int nIndex = -1;
	auto pDiskCat = reinterpret_cast<NordFileRecord *>(m_pCatBuffer + FMT_NORD_CAT_BEGIN);//каталог диска

	for (int i = 0; i < m_nMKLastCatRecord; ++i)
	{
		if (IsEndOfCat(&pDiskCat[i]))
		{
			break;
		}

		if (pDiskCat[i].status & 0200)
		{
			//если удалённый файл - дырка, то игнорируем
			continue;
		}
		else if (pDiskCat[i].dir_num == m_sDiskCat.nCurrDirNum) //проверяем только в текущей директории
		{
			if (memcmp(pNDRec->name, pDiskCat[i].name, 14) == 0)  //проверим имя
			{
				if (bFull)
				{
					if (pNDRec->name[0] == 0177) //если директория
					{
						if (pDiskCat[i].status == pNDRec->status) //то проверяем номер директории
						{
							nIndex = i;
							break;
						}
					}
					else //если файл - то проверяем параметры файла
					{
						if (pDiskCat[i].dir_num == pNDRec->dir_num)
						{
							nIndex = i;
							break;
						}
					}
				}
				else
				{
					nIndex = i;
					break;
				}
			}
		}
	}

	return nIndex;
}

void CBKFloppyImage_Nord::ConvertAbstractToRealRecord(AbstractFileRecord *pFR, bool bRenameOnly)
{
	auto pNDRec = reinterpret_cast<NordFileRecord *>(pFR->pSpecificData); //Вот эту запись надо добавить

	//преобразовывать будем только если там ещё не сформирована реальная запись.
	if (pFR->nSpecificDataLength == 0 || bRenameOnly)
	{
		if (!bRenameOnly)
		{
			pFR->nSpecificDataLength = sizeof(NordFileRecord);
			memset(pNDRec, 0, sizeof(NordFileRecord));
		}

		//надо сформировать мкдосную запись из абстрактной
		wchar_t szName[_MAX_PATH];
		wchar_t szExt[_MAX_EXT];
		_tsplitpath_s(pFR->strName.c_str(), nullptr, 0, nullptr, 0, szName, _MAX_PATH, szExt, _MAX_EXT);
		wstring strMKName = wstring(szName);
		wstring strMKExt = wstring(szExt).substr(0, 4); //включая точку
		size_t nNameLen = (14 - strMKExt.length()); //допустимая длина имени

		if (strMKName.length() > nNameLen) //если имя длиньше
		{
			strMKName = strMKName.substr(0, nNameLen); //обрезаем
		}

		strMKName += strMKExt; //прицепляем расширение

		if (pFR->nAttr & FR_ATTR_DIR)
		{
			UNICODEtoBK(pFR->strName, pNDRec->name + 1, 13, true);
			pNDRec->name[0] = 0177; //признак каталога

			if (!bRenameOnly)
			{
				pNDRec->status = pFR->nDirNum;
				pNDRec->dir_num = m_sDiskCat.nCurrDirNum; //номер текущего открытого подкаталога
			}
		}
		else
		{
			UNICODEtoBK(strMKName, pNDRec->name, 14, true);

			if (!bRenameOnly)
			{
				int nBlkLen = (pFR->nSize) ? (((pFR->nSize - 1) | (m_nBlockSize - 1)) + 1) : 0; //размер проги выровняем по границе блока
				pNDRec->status = 0;

				if (pFR->nAttr & FR_ATTR_PROTECTED)
				{
					pNDRec->status |= 1;
				}

				if (pFR->nAttr & FR_ATTR_READONLY)
				{
					pNDRec->status |= 4;
				}

				pNDRec->dir_num = pFR->nDirBelong;
				pNDRec->len_blk = nBlkLen / m_nBlockSize; //размер в блоках
				pNDRec->address = pFR->nAddress; //возможно, если мы сохраняем бин файл, адрес будет браться оттуда
				pNDRec->length = pFR->nSize % 0200000; //остаток от длины по модулю 65535
			}
		}
	}
}

//на входе указатель на абстрактную запись.
//в ней заполнена копия реальной записи, по ней формируем абстрактную
void CBKFloppyImage_Nord::ConvertRealToAbstractRecord(AbstractFileRecord *pFR)
{
	auto pNDRec = reinterpret_cast<NordFileRecord *>(pFR->pSpecificData); //Вот эту запись надо преобразовать

	if (pFR->nSpecificDataLength) //преобразовываем, только если есть реальные данные
	{
		if (pNDRec->name[0] == 0177)
		{
			//если директория
			pFR->nAttr |= FR_ATTR_DIR;
			pFR->nRecType = RECORD_TYPE_DIR;
			pFR->strName = BKToUNICODE(pNDRec->name + 1, 13, m_pKoi8tbl);
			trim(&pFR->strName);
			pFR->nDirBelong = pNDRec->dir_num;
			pFR->nDirNum = pNDRec->status;
			pFR->nBlkSize = 0;
			pFR->nAddress = 0;
		}
		else
		{
			//если файл
			if (pNDRec->status & 0200)
			{
				pFR->nAttr |= FR_ATTR_DELETED;
			}
			else if (pNDRec->status & 2)
			{
				pFR->nAttr |= FR_ATTR_BAD;

				if (pNDRec->name[0] == 0)
				{
					pNDRec->name[0] = 'B';
					pNDRec->name[1] = 'A';
					pNDRec->name[2] = 'D';
				}
			}

			if (pNDRec->status & 0111)
			{
				pFR->nAttr |= FR_ATTR_PROTECTED;
			}

			if (pNDRec->status & 4)
			{
				pFR->nAttr |= FR_ATTR_READONLY;
			}

			wstring name = BKToUNICODE(pNDRec->name, 14, m_pKoi8tbl);
			trim(&name);
			wstring ext = L"";
			size_t l = name.length();
			size_t t = name.rfind(L'.');

			if (t != wstring::npos) //если расширение есть
			{
				ext = name.substr(t, l);
				name = name.substr(0, t);
				trim(&name);
			}

			if (!ext.empty())
			{
				name += ext;
			}

			pFR->strName = name;
			pFR->nDirBelong = pNDRec->dir_num;
			pFR->nDirNum = 0;
			pFR->nBlkSize = pNDRec->len_blk;
			pFR->nAddress = pNDRec->address;
			pFR->nRecType = RECORD_TYPE_FILE;

			if (pFR->nAttr & FR_ATTR_DELETED)
			{
				pFR->nDirBelong = 1; //а то удалённые файлы вообще не видны,
				//а так они будут находиться в специальной директории DEL
				//ну и дырки так же там
			}

			if ((pNDRec->name[1] == ':') && (pNDRec->name[0] >= 'A' && pNDRec->name[0] <= 'Z'))
			{
				pFR->nAttr |= FR_ATTR_LOGDISK;
				pFR->nRecType = RECORD_TYPE_LOGDSK;
			}
		}

		if (pFR->nAttr & FR_ATTR_LOGDISK) //если у нас логический диск, то размер надо считать по блокам.
		{
			pFR->nSize = pNDRec->len_blk * m_nBlockSize;
		}
		else
		{
			/*  правильный расчёт длины файла.
			т.к. в curr_record.length хранится остаток длины по модулю 0200000, нам из длины в блоках надо узнать
			сколько частей по 0200000 т.е. сколько в блоках частей по 128 блоков.(128 блоков == 65536.== 0200000)

			hw = (curr_record.len_blk >>7 ) << 16; //это старшее слово двойного слова
			hw = (curr_record.len_blk / 128) * 65536 = curr_record.len_blk * m_nSectorSize
			*/
			uint32_t hw = (pNDRec->len_blk << 9) & 0xffff0000; //это старшее слово двойного слова
			pFR->nSize = hw + pNDRec->length; //это ст.слово + мл.слово
		}

		pFR->nStartBlock = pNDRec->start_block;
	}
}


bool CBKFloppyImage_Nord::ReadCurrentDir()
{
	if (!CBKFloppyImage_Prototype::ReadCurrentDir())
	{
		return false;
	}

	//если памяти под массив не выделилось - то ошибка, дальше работать невозможно
	if (m_pCatBuffer == nullptr)
	{
		m_nLastErrorNumber = IMAGE_ERROR_NOT_ENOUGHT_MEMORY;
		return false;
	}

	memset(m_pCatBuffer, 0, m_nCatSize);

	if (!SeektoBlkReadData(0, m_pCatBuffer, m_nCatSize)) //читаем каталог
	{
		return false;
	}

	//пробуем определить версию. если по смещениям 012 и 014 будут цифры '3' и '5'
	//то будем считать, что это норд 3.x, иначе - 2.х
	m_nVersion = (m_pCatBuffer[012] == '3') ? 30 : 20;
	int recs_count = 0;
	int files_total = *(uint16_t *)&m_pCatBuffer[FMT_NORD_CAT_RECORD_NUMBER]; //читаем общее кол-во файлов. (НЕ записей!)
	m_sDiskCat.nDataBegin = *(uint16_t *)&m_pCatBuffer[0470]; //блок начала данных

	if (m_sDiskCat.nDataBegin <= 0 || m_sDiskCat.nDataBegin > 40) //если там некорректная информация
	{
		m_sDiskCat.nDataBegin = 024; //берём данные по умолчанию.
	}

	m_sDiskCat.nTotalRecs = m_nMKCatSize; //это у нас объём каталога, из него надо будет вычесть общее количество записей
	int used_size = 0;
	int disk_size = *(WORD *)&m_pCatBuffer[FMT_NORD_DISK_SIZE];

	if ((disk_size == 0) || (disk_size >= 32767))
	{
		//если указан какой-то неправильный размер, то размер берём из расчёта типа диска
		switch (*(WORD *)&m_pCatBuffer[FMT_NORD_DISK_TYPE])
		{
			case 0:
				disk_size = 80 * 2 * 10; //0 - 80 дор. 2 стороны
				break;

			case 1:
				disk_size = 40 * 2 * 10; //1 - 40 дор. 2 стороны
				break;

			case 2:
				disk_size = 80 * 1 * 10; //2 - 80 дор. 1 сторона
				break;

			case 3:
				disk_size = 40 * 1 * 10; //3 - 40 дор. 1 сторона
				break;
		}
	}

	if (*(WORD *)&m_pCatBuffer[FMT_NORD_BEGIN_BLOCK])
	{
		//если у нас логический диск, то берём размер логического диска
		disk_size = (int)m_sParseImgResult.nImageSize / 512;
	}
	else
	{
		//если у нас образ диска. то вычисляем посложнее
		disk_size = max(disk_size, (int)m_sParseImgResult.nImageSize / 512); //норд 3.5 может быть больше 800кб, сильно больше.
	}

	int cat_status = *(WORD *)&m_pCatBuffer[FMT_NORD_CAT_NUMBER]; //читаем кол-во и состояние копий каталогов на диске

	switch (cat_status)
	{
		case 0:
		case 1:
			disk_size -= (m_nVersion == 30) ? 2 * 10 : 10; //0,1 - одна копия каталога, начальный сектор 0
			break;

		case 2: //2 - одна копия, начальный сектор 012

			//это означает что на диске есть только вторая копия каталога, все остальные числа
			//означают, что есть как минимум одна копия, и она в начале диска
			if (!SeektoBlkReadData(012, m_pCatBuffer, m_nCatSize)) //читаем каталог
			{
				return false;
			}

		case 3: //3 - две копии. 0 и 012 секторы
			disk_size -= 2 * 10; //т.е. по любому от свободного размера диска надо вычесть размер обеих копий каталога.
			break;
	}

	//каталог диска
	auto pDiskCat = reinterpret_cast<NordFileRecord *>(m_pCatBuffer + FMT_NORD_CAT_BEGIN);
	AbstractFileRecord AFR; //экземпляр абстрактной записи
	auto pNDRec = reinterpret_cast<NordFileRecord *>(AFR.pSpecificData); //а в ней копия оригинальной записи

	for (int i = 0; i < m_nMKCatSize; ++i) //цикл по всему каталогу
	{
		m_nMKLastCatRecord = i;

		if (m_nVersion == 30 && recs_count >= files_total) //файлы кончились, выходим
		{
			break;
		}

		if (IsEndOfCat(&pDiskCat[i]))
		{
			break;
		}

		//преобразуем запись и поместим в массив
		AFR.clear();
		AFR.nSpecificDataLength = sizeof(NordFileRecord);
		*pNDRec = pDiskCat[i]; //копируем текущую запись как есть
		ConvertRealToAbstractRecord(&AFR);

		if ((pDiskCat[i].status & 0200) || (pDiskCat[i].status & 2))
		{
			//в норд 3.5 удалённые не считаются
			//плохие наверное тоже не считаются, но проверить не на чём
			if (m_nVersion != 30) //а в старых - считаются
			{
				recs_count++;
			}
		}
		else
		{
			recs_count++;
		}

		if (!(AFR.nAttr & FR_ATTR_DELETED))
		{
			if (pDiskCat[i].name[0] == 0177)
			{
				//если директория
				if (pDiskCat[i].status > m_sDiskCat.nMaxDirNum)
				{
					m_sDiskCat.nMaxDirNum = pDiskCat[i].status; //максимальный номер директории определим заодно
				}
			}
			else
			{
				//если файл
				used_size += pDiskCat[i].len_blk;
			}
		}

		//выбираем только те записи, которые к нужной директории принадлежат.
		if (AFR.nDirBelong == m_sDiskCat.nCurrDirNum)
		{
			m_sDiskCat.vecFC.push_back(AFR);
		}
	}

#ifdef _DEBUG
	DebugOutCatalog(reinterpret_cast<NordFileRecord *>(m_pCatBuffer + FMT_NORD_CAT_BEGIN));
#endif
	m_sDiskCat.nFreeRecs = m_sDiskCat.nTotalRecs - recs_count;
	m_sDiskCat.nTotalBlocks = disk_size;
	m_sDiskCat.nFreeBlocks = disk_size - used_size;
	return true;
}

bool CBKFloppyImage_Nord::WriteCurrentDir()
{
	if (!CBKFloppyImage_Prototype::WriteCurrentDir())
	{
		return false;
	}

	//если памяти под массив не выделилось - то ошибка, дальше работать невозможно
	if (m_pCatBuffer == nullptr)
	{
		m_nLastErrorNumber = IMAGE_ERROR_NOT_ENOUGHT_MEMORY;
		return false;
	}

#ifdef _DEBUG
	DebugOutCatalog(reinterpret_cast<NordFileRecord *>(m_pCatBuffer + FMT_NORD_CAT_BEGIN));
#endif
	int cat_status = *(WORD *)&m_pCatBuffer[FMT_NORD_CAT_NUMBER];

	//если есть первая копия, сохраняем её
	if ((cat_status & 1) || (cat_status == 0))
	{
		if (!SeektoBlkWriteData(0, m_pCatBuffer, m_nCatSize)) //сохраняем каталог как есть
		{
			return false;
		}
	}

	//если есть вторая копия, сохраняем её
	if (cat_status & 2)
	{
		if (!SeektoBlkWriteData(012, m_pCatBuffer, m_nCatSize))
		{
			return false;
		}
	}

	return true;
}

bool CBKFloppyImage_Nord::ChangeDir(AbstractFileRecord *pFR)
{
	if (CBKFloppyImage_Prototype::ChangeDir(pFR))
	{
		return true;
	}
	else if ((pFR->nAttr & FR_ATTR_LOGDISK) && (pFR->nRecType == RECORD_TYPE_LOGDSK))
	{
		m_nLastErrorNumber = IMAGE_ERROR_OK;
		return true;
	}

//  m_nLastErrorNumber = IMAGE_ERROR_IS_NOT_DIR;
	return false;
}

bool CBKFloppyImage_Nord::VerifyDir(AbstractFileRecord *pFR)
{
	if (pFR->nAttr & FR_ATTR_DIR)
	{
		ConvertAbstractToRealRecord(pFR);
		auto pNDRec = reinterpret_cast<NordFileRecord *>(pFR->pSpecificData); //Вот эту запись надо добавить
		//проверим, вдруг такая директория уже есть
		int nIndex = FindRecord2(pNDRec, false); //мы тут не знаем номер директории. мы можем только по имени проверить.

		if (nIndex >= 0)
		{
			return true;
		}
	}

	return false;
}

bool CBKFloppyImage_Nord::CreateDir(AbstractFileRecord *pFR)
{
	m_nLastErrorNumber = IMAGE_ERROR_OK;
	bool bRet = false;

	if (m_bFileROMode)
	{
		//Если образ открылся только для чтения,
		m_nLastErrorNumber = IMAGE_ERROR_FILE_WRITE_PROTECRD;
		return bRet; //то записать в него мы ничего не сможем.
	}

	if (m_sDiskCat.nFreeRecs <= 0)
	{
		m_nLastErrorNumber = IMAGE_ERROR_FS_CAT_FULL;
		return false;
	}

	if (pFR->nAttr & FR_ATTR_DIR)
	{
		ConvertAbstractToRealRecord(pFR);
		auto pDiskCat = reinterpret_cast<NordFileRecord *>(m_pCatBuffer + FMT_NORD_CAT_BEGIN);//каталог диска
		auto pNDRec = reinterpret_cast<NordFileRecord *>(pFR->pSpecificData); //Вот эту запись надо добавить
		pFR->nDirBelong = pNDRec->dir_num = m_sDiskCat.nCurrDirNum; //номер текущего открытого подкаталога
		//проверим, вдруг такая директория уже есть
		int nIndex = FindRecord2(pNDRec, false); //мы тут не знаем номер директории. мы можем только по имени проверить.

		if (nIndex >= 0)
		{
			m_nLastErrorNumber = IMAGE_ERROR_FS_DIR_EXIST;
			pFR->nDirNum = pDiskCat[nIndex].status; //и заодно узнаем номер директории
		}
		else
		{
			//найдём свободное место в каталоге.
			bool bHole = false;
			bool bFound = false;

			for (int i = 0; i < m_nMKLastCatRecord; ++i)
			{
				if (IsEndOfCat(&pDiskCat[i]))
				{
					break;
				}

				if ((pDiskCat[i].status & 0200) && (pDiskCat[i].name[0] == 0177))
				{
					//если нашли дырку в которой было имя директории
					bHole = true;
					nIndex = i;
					bFound = true;
					break;
				}
			}

			if (!bFound)
			{
				nIndex = m_nMKLastCatRecord;

				if (nIndex < m_nMKCatSize) //если в конце каталога вообще есть место
				{
					bFound = true; //то нашли, иначе - нет
				}
			}

			if (bFound)
			{
				pFR->nDirNum = pNDRec->status = m_sDiskCat.nMaxDirNum + 1; //назначаем номер директории.

				//а теперь более продвинутый способ найти номер каталога
				for (int n = 1; n < 0177; ++n) //пройдёмся по всем номерам
				{
					bool bFound = false;

					for (int i = 0; i < m_nMKLastCatRecord; ++i) //во всём каталоге ищем
					{
						if (IsEndOfCat(&pDiskCat[i]))
						{
							break;
						}

						if (pDiskCat[i].status & 0200) //удалённые файлы игнорируем
						{
							continue;
						}

						if (pDiskCat[i].name[0] == 0177) //если директория
						{
							if (pDiskCat[i].status == n) //и её номер такой
							{
								bFound = true;
								break;
							}
						}
					}

					if (!bFound) //если такой номер в каталоге не найден
					{
						pFR->nDirNum = pNDRec->status = n; //то будем использовать его
						break;
					}
				}

				//если ошибок не произошло, сохраним результаты
				if (bHole)
				{
					//сохраняем нашу запись вместо удалённой директории
					pDiskCat[nIndex] = pNDRec;
				}
				else
				{
					//если нашли свободную область в конце каталога
					int nHoleSize = pDiskCat[nIndex].len_blk;
					int nStartBlock = pDiskCat[nIndex].start_block;
					//сохраняем нашу запись
					pDiskCat[nIndex] = pNDRec;
					//сохраняем признак конца каталога
					NordFileRecord hole;
					hole.start_block = nStartBlock + pNDRec->len_blk;
					hole.len_blk = nHoleSize - pNDRec->len_blk;
					//и запись с инфой о свободной области
					pDiskCat[nIndex + 1] = hole;
					m_nMKLastCatRecord++;
				}

				//сохраняем каталог
				*(uint16_t *)&m_pCatBuffer[FMT_NORD_CAT_RECORD_NUMBER] += 1; //поправим параметры - количество файлов
				bRet = WriteCurrentDir();
				m_sDiskCat.nFreeRecs--;
			}
			else
			{
				m_nLastErrorNumber = IMAGE_ERROR_FS_CAT_FULL;
			}
		}
	}
	else
	{
		m_nLastErrorNumber = IMAGE_ERROR_IS_NOT_DIR;
	}

	return bRet;
}

bool CBKFloppyImage_Nord::DeleteDir(AbstractFileRecord *pFR)
{
	m_nLastErrorNumber = IMAGE_ERROR_OK;
	bool bRet = false;

	if (m_bFileROMode)
	{
		//Если образ открылся только для чтения,
		m_nLastErrorNumber = IMAGE_ERROR_FILE_WRITE_PROTECRD;
		return bRet; //то записать в него мы ничего не сможем.
	}

	if (pFR->nAttr & FR_ATTR_DELETED)
	{
		return true; //уже удалённое не удаляем повторно
	}

	if (pFR->nAttr & FR_ATTR_DIR)
	{
		ConvertAbstractToRealRecord(pFR);
		//узнать есть ли файлы в директории.
		bool bExist = false;
		auto pDiskCat = reinterpret_cast<NordFileRecord *>(m_pCatBuffer + FMT_NORD_CAT_BEGIN);//каталог диска

		for (int i = 0; i < m_nMKLastCatRecord; ++i)
		{
			if (IsEndOfCat(&pDiskCat[i]))
			{
				break;
			}

			if (pDiskCat[i].status & 0200)
			{
				//если удалённый файл - дырка, то игнорируем
				continue;
			}
			else if (pDiskCat[i].dir_num == pFR->nDirNum)
			{
				bExist = true; //нашли файл, принадлежащий этой директории
				break;
			}
		}

		//если есть - не удалять.
		if (bExist)
		{
			m_nLastErrorNumber = IMAGE_ERROR_FS_DIR_NOT_EMPTY;
		}
		else
		{
			//удалить можно пустую директорию.
			auto pNDRec = reinterpret_cast<NordFileRecord *>(pFR->pSpecificData); //Вот эту запись надо удалить
			int nIndex = FindRecord(pNDRec);

			if (nIndex >= 0) //если нашли
			{
				pDiskCat[nIndex].status = 0375; //пометим как удалённую
				pDiskCat[nIndex].dir_num = 1;
				*(uint16_t *)&m_pCatBuffer[FMT_NORD_CAT_RECORD_NUMBER] -= 1; //поправим параметры - количество файлов
				bRet = WriteCurrentDir(); //сохраним директорию
				m_sDiskCat.nFreeRecs++;
			}
			else
			{
				m_nLastErrorNumber = IMAGE_ERROR_FILE_NOT_FOUND;
			}
		}
	}
	else
	{
		m_nLastErrorNumber = IMAGE_ERROR_IS_NOT_DIR;
	}

	return bRet;
}

bool CBKFloppyImage_Nord::GetNextFileName(AbstractFileRecord *pFR)
{
	pFR->clear();
	auto pNDRec = reinterpret_cast<NordFileRecord *>(pFR->pSpecificData); //оригинальная запись
	auto pDiskCat = reinterpret_cast<NordFileRecord *>(m_pCatBuffer + FMT_NORD_CAT_BEGIN);//каталог диска
#ifdef _DEBUG
	DebugOutCatalog(pDiskCat);
#endif

	//и выводим всё что найдём. но по одному.
	while (m_nCatPos < m_nMKLastCatRecord) //цикл по всему каталогу
	{
		if (IsEndOfCat(&pDiskCat[m_nCatPos]))
		{
			break;
		}

		if (pDiskCat[m_nCatPos].dir_num == m_sDiskCat.nCurrDirNum) //ищем только в текущей директории
		{
			*pNDRec = pDiskCat[m_nCatPos];
			pFR->nSpecificDataLength = sizeof(NordFileRecord);
			ConvertRealToAbstractRecord(pFR);
			return true;
		}

		m_nCatPos++;
	}

	if (!m_vecPC.empty())
	{
		m_nCatPos = m_vecPC.back();
		m_vecPC.pop_back();
	}

	return false;
}

bool CBKFloppyImage_Nord::RenameRecord(AbstractFileRecord *pFR)
{
	auto pNDRec = reinterpret_cast<NordFileRecord *>(pFR->pSpecificData); //оригинальная запись
	int nIndex = FindRecord(pNDRec); //сперва найдём её

	if (nIndex >= 0)
	{
		ConvertAbstractToRealRecord(pFR, true); //теперь скорректируем имя реальной записи
		auto pDiskCat = reinterpret_cast<NordFileRecord *>(m_pCatBuffer + FMT_NORD_CAT_BEGIN); //каталог диска
		pDiskCat[nIndex] = *pNDRec; //теперь скорректируем в каталоге
		return WriteCurrentDir(); //сохраним каталог
	}
	else
	{
		//что-то не так
	}

	return false;
}


bool CBKFloppyImage_Nord::ReadFile(AbstractFileRecord *pFR, uint8_t *pBuffer)
{
	m_nLastErrorNumber = IMAGE_ERROR_OK;
	bool bRet = true;

	if (pFR->nAttr & (FR_ATTR_DIR | FR_ATTR_LINK))
	{
		m_nLastErrorNumber = IMAGE_ERROR_IS_NOT_FILE;
		return false; //если это не файл - выход с ошибкой
	}

	ConvertAbstractToRealRecord(pFR);
	size_t len = pFR->nSize;
	size_t sector = pFR->nStartBlock;

	if (SeekToBlock(sector)) //перемещаемся к началу файла
	{
		//поскольку файл располагается единым куском, прочитаем его целиком
		if (int(len) > 0)
		{
			if (!ReadData(pBuffer, len))
			{
				m_nLastErrorNumber = IMAGE_ERROR_CANNOT_READ_FILE;
				bRet = false;
			}
		}
	}
	else
	{
		bRet = false;
	}

	return bRet;
}

bool CBKFloppyImage_Nord::WriteFile(AbstractFileRecord *pFR, uint8_t *pBuffer)
{
	m_nLastErrorNumber = IMAGE_ERROR_OK;
	bool bRet = false;

	if (m_bFileROMode)
	{
		//Если образ открылся только для чтения,
		m_nLastErrorNumber = IMAGE_ERROR_FILE_WRITE_PROTECRD;
		return false; //то записать в него мы ничего не сможем.
	}

	if (pFR->nAttr & (FR_ATTR_DIR | FR_ATTR_LINK))
	{
		m_nLastErrorNumber = IMAGE_ERROR_IS_NOT_FILE;
		return false; //если это не файл - выход с ошибкой
	}

	ConvertAbstractToRealRecord(pFR);
	auto pDiskCat = reinterpret_cast<NordFileRecord *>(m_pCatBuffer + FMT_NORD_CAT_BEGIN);//каталог диска
	auto pNDRec = reinterpret_cast<NordFileRecord *>(pFR->pSpecificData); //Вот эту запись надо добавить
	pFR->nDirBelong = pNDRec->dir_num = m_sDiskCat.nCurrDirNum; //номер текущего открытого подкаталога

	if (m_sDiskCat.nFreeBlocks < pNDRec->len_blk)
	{
		m_nLastErrorNumber = IMAGE_ERROR_FS_DISK_FULL;
		return false;
	}

	if (m_sDiskCat.nFreeRecs <= 0)
	{
		m_nLastErrorNumber = IMAGE_ERROR_FS_CAT_FULL;
		return false;
	}

	//поищем, вдруг такое имя файла уже существует.
	int nIndex = FindRecord2(pNDRec, true);

	if (nIndex >= 0)
	{
		m_nLastErrorNumber = IMAGE_ERROR_FS_FILE_EXIST;
		*pNDRec = pDiskCat[nIndex];
		ConvertRealToAbstractRecord(pFR); //и сделаем найденную запись актуальной.
		return false;
	}

	int nIter = 0;
l_wrCycle:
	//найдём свободное место в каталоге.
	bool bFound = false;
	bool bHole = false; //что нашли, дырку или место в конце, т.к. разные способы обработки каталога

	for (int i = 0; i < m_nMKLastCatRecord; ++i)
	{
		if (IsEndOfCat(&pDiskCat[i]))
		{
			break;
		}

		if ((pDiskCat[i].status & 0200) && (pDiskCat[i].len_blk >= pNDRec->len_blk))
		{
			//если нашли дырку подходящего размера
			bHole = true;
			nIndex = i;
			bFound = true;
			break;
		}
	}

	if (!bFound)
	{
		nIndex = m_nMKLastCatRecord;

		if (nIndex < m_nMKCatSize) //если в конце каталога вообще есть место
		{
			bFound = true; //то нашли, иначе - нет
		}
	}

	if (bFound)
	{
		//можно сохранять
		int nFirstFreeBlock = pNDRec->start_block = pDiskCat[nIndex].start_block;
		int nHoleSize = pDiskCat[nIndex].len_blk;

		if (bHole)
		{
			//если нашли дырку в каталоге, надо раздвинуть каталог, или не раздвигать, если размер дырки совпадает
			if (nHoleSize > pNDRec->len_blk)
			{
				//раздвинем каталог
				int i = m_nMKLastCatRecord + 1; //найдём конец каталога

				if (i >= m_nMKCatSize) //если каталог раздвинуть нельзя
				{
					m_nLastErrorNumber = IMAGE_ERROR_FS_CAT_FULL;
					goto l_squeeze;
				}
				else
				{
					//раздвигаем
					while (i > nIndex)
					{
						pDiskCat[i] = pDiskCat[i - 1];
						i--;
					}

					//сохраняем нашу запись
					pDiskCat[nIndex] = pNDRec;
					NordFileRecord hole;
					hole.status = 0375;
					hole.dir_num = 1;
					hole.start_block = pNDRec->start_block + pNDRec->len_blk;
					hole.len_blk = nHoleSize - pNDRec->len_blk;
					UNICODEtoBK(L"<HOLE>", hole.name, 14, true);
					hole.length = (hole.len_blk * m_nBlockSize) % 0200000;
					//и запись с инфой о дырке
					pDiskCat[nIndex + 1] = hole;
					m_nMKLastCatRecord++;
				}
			}
			else
			{
				//ничего раздвигать не надо, просто
				//сохраняем нашу запись
				pDiskCat[nIndex] = pNDRec;
			}
		}
		else
		{
			//если нашли свободную область в конце каталога
			//сохраняем нашу запись
			pDiskCat[nIndex] = pNDRec;
			//сохраняем признак конца каталога
			NordFileRecord hole;
			hole.start_block = pNDRec->start_block + pNDRec->len_blk;
			hole.len_blk = nHoleSize - pNDRec->len_blk;
			//и запись с инфой о свободной области
			pDiskCat[nIndex + 1] = hole;
			m_nMKLastCatRecord++;
		}

		if (m_nLastErrorNumber == IMAGE_ERROR_OK)
		{
			//перемещаемся к месту
			if (SeekToBlock(nFirstFreeBlock))
			{
				int nReaded;
				int nLen = pFR->nSize; //размер файла

				if (nLen == 0)
				{
					nLen++; //запись файла нулевой длины не допускается, поэтому скорректируем.
				}

				while (nLen > 0)
				{
					memset(m_mBlock, 0, COPY_BLOCK_SIZE);
					nReaded = (nLen >= COPY_BLOCK_SIZE) ? COPY_BLOCK_SIZE : nLen;
					memcpy(m_mBlock, pBuffer, nReaded);
					pBuffer += nReaded;

					//пишем с выравниванием по секторам.
					if (nReaded > 0)
					{
						if (!WriteData(m_mBlock, (((nReaded - 1) | (m_nBlockSize - 1)) + 1)))
						{
							return false;
						}
					}

					nLen -= nReaded;
				}

				if (m_nLastErrorNumber == IMAGE_ERROR_OK)
				{
					//если ошибок не произошло, сохраним результаты
					*(uint16_t *)&m_pCatBuffer[FMT_NORD_CAT_RECORD_NUMBER] += 1;
					//наконец сохраняем каталог
					bRet = WriteCurrentDir();
					m_sDiskCat.nFreeRecs--;
					m_sDiskCat.nFreeBlocks -= pNDRec->len_blk;
				}
			}
		}
	}
	else
	{
l_squeeze:
		// если нет дырки, но суммарное свободное место на диске есть - делаем сквизирование и пишем в конец.
		LRESULT definite = AfxGetMainWnd()->SendMessage(WM_SEND_MESSAGEBOX, WPARAM(MB_YESNO | MB_ICONINFORMATION), reinterpret_cast<LPARAM>(_T("Диск сильно фрагментирован. Выполнить сквизирование?")));

		switch (definite)
		{
			case IDYES:
				if (!Squeeze())
				{
					return false;
				}

				if (++nIter < 2)
				{
					goto l_wrCycle;
				}

				break;

			default:
			case IDNO:
				break;
		}

		m_nLastErrorNumber = IMAGE_ERROR_FS_DISK_FULL_SQEEZE;
	}

	return bRet;
}


bool CBKFloppyImage_Nord::DeleteFile(AbstractFileRecord *pFR, bool bForce)
{
	m_nLastErrorNumber = IMAGE_ERROR_OK;
	bool bRet = false;

	if (m_bFileROMode)
	{
		//Если образ открылся только для чтения,
		m_nLastErrorNumber = IMAGE_ERROR_FILE_WRITE_PROTECRD;
		return bRet; //то записать в него мы ничего не сможем.
	}

	if (pFR->nAttr & (FR_ATTR_DIR | FR_ATTR_LINK))
	{
		m_nLastErrorNumber = IMAGE_ERROR_IS_NOT_FILE;
		return false; //если это не файл - выход с ошибкой
	}

	if (pFR->nAttr & FR_ATTR_DELETED)
	{
		return true; //уже удалённое не удаляем повторно
	}

	ConvertAbstractToRealRecord(pFR);
	auto pNDRec = reinterpret_cast<NordFileRecord *>(pFR->pSpecificData); //Вот эту запись надо удалить
	//найдём её в каталоге
	auto pDiskCat = reinterpret_cast<NordFileRecord *>(m_pCatBuffer + FMT_NORD_CAT_BEGIN);//каталог диска
	int nIndex = FindRecord(pNDRec);

	if (nIndex >= 0) //если нашли
	{
		if ((pDiskCat[nIndex].status == 2) && !bForce)
		{
			m_nLastErrorNumber = IMAGE_ERROR_FS_FILE_PROTECTED;
		}
		else
		{
			pDiskCat[nIndex].status = 0375; //пометим как удалённую
			pDiskCat[nIndex].dir_num = 1;
			*(uint16_t *)&m_pCatBuffer[FMT_NORD_CAT_RECORD_NUMBER] -= 1; //поправим параметры - количество файлов
			bRet = WriteCurrentDir(); //сохраним директорию
			m_sDiskCat.nFreeRecs++;
			m_sDiskCat.nFreeBlocks += pDiskCat[nIndex].len_blk;
		}
	}
	else
	{
		m_nLastErrorNumber = IMAGE_ERROR_FILE_NOT_FOUND;
	}

	return bRet;
}

//сквизирование диска
bool CBKFloppyImage_Nord::Squeeze()
{
	m_nLastErrorNumber = IMAGE_ERROR_OK;
	bool bRet = true;
	auto pDiskCat = reinterpret_cast<NordFileRecord *>(m_pCatBuffer + FMT_NORD_CAT_BEGIN);//каталог диска
	int p = 0;

	while (p <= m_nMKLastCatRecord)
	{
		if (pDiskCat[p].status & 0200) //если нашли дырку
		{
			int n = p + 1; //индекс следующей записи

			if (n > m_nMKLastCatRecord) //если p - последняя запись
			{
				//Тут надо обработать выход
				pDiskCat[p].status = pDiskCat[p].dir_num = 0;
				memset(pDiskCat[p].name, 0, 14);
				pDiskCat[p].address = pDiskCat[p].length = 0;
				break;
			}

			if (pDiskCat[n].status & 0200) //и за дыркой снова дырка
			{
				pDiskCat[p].len_blk += pDiskCat[n].len_blk; //первую - укрупним

				//а вторую удалим.
				while (n < m_nMKLastCatRecord) //сдвигаем каталог
				{
					pDiskCat[n] = pDiskCat[n + 1];
					n++;
				}

				memset(&pDiskCat[m_nMKLastCatRecord--], 0, sizeof(NordFileRecord));
				continue; //и всё сначала
			}

			size_t nBufSize = pDiskCat[n].len_blk * m_nBlockSize;
			auto pBuf = new uint8_t[nBufSize];

			if (pBuf)
			{
				if (SeekToBlock(pDiskCat[n].start_block))
				{
					if (!ReadData(pBuf, nBufSize))
					{
						m_nLastErrorNumber = IMAGE_ERROR_CANNOT_READ_FILE;
						delete[] pBuf;
						bRet = false;
						break;
					}

					if (SeekToBlock(pDiskCat[p].start_block))
					{
						if (!WriteData(pBuf, nBufSize))
						{
							m_nLastErrorNumber = IMAGE_ERROR_CANNOT_READ_FILE;
							delete[] pBuf;
							bRet = false;
							break;
						}
						//теперь надо записи местами поменять.
						swap(pDiskCat[p], pDiskCat[n]); //обменяем записи целиком
						swap(pDiskCat[p].start_block, pDiskCat[n].start_block); //начальные блоки вернём как было.
						pDiskCat[n].start_block = pDiskCat[p].start_block + pDiskCat[p].len_blk;
					}
					else
					{
						bRet = false;
					}
				}
				else
				{
					bRet = false;
				}

				delete[] pBuf;
			}
			else
			{
				m_nLastErrorNumber = IMAGE_ERROR_NOT_ENOUGHT_MEMORY;
				bRet = false;
				break;
			}
		}

		p++;
	}

	return bRet;
}

#ifdef _DEBUG
//отладочный вывод каталога
#pragma warning(disable:4996)
void CBKFloppyImage_Nord::DebugOutCatalog(NordFileRecord *pMKRec)
{
	CString strModuleName;
	::GetModuleFileName(AfxGetInstanceHandle(), strModuleName.GetBufferSetLength(_MAX_PATH), _MAX_PATH);
	strModuleName.ReleaseBuffer();
	CString strDrive, strPath;
	_tsplitpath_s(strModuleName.GetString(), strDrive.GetBufferSetLength(_MAX_DRIVE), _MAX_DRIVE, strPath.GetBufferSetLength(_MAX_PATH), _MAX_PATH, nullptr, 0, nullptr, 0);
	strDrive.ReleaseBuffer();
	strPath.ReleaseBuffer();
	FILE *log = _wfopen(strDrive + strPath + "\\dirlog.txt", L"wt");
	fprintf(log, "N#  Stat.Dir.Name           start  lenblk  address length\n");
	uint8_t name[15];

	for (int i = 0; i < m_nMKLastCatRecord; ++i)
	{
		memcpy(name, pMKRec[i].name, 14);
		name[14] = 0;
		fprintf(log, "%03d %03d  %03d %-14s %06o %06o  %06o  %06o\n", i, pMKRec[i].status, pMKRec[i].dir_num, name, pMKRec[i].start_block, pMKRec[i].len_blk, pMKRec[i].address, pMKRec[i].length);
	}

	fclose(log);
}
#endif
