// LoadImgDlg.cpp: файл реализации
//

#include "stdafx.h"
#include "resource.h"
#include "LoadImgDlg.h"
#include "BKErrMsgSys.h"

// CLoadImgDlg
#pragma warning(disable:4996)

IMPLEMENT_DYNAMIC(CLoadImgDlg, CFileDialog)

CLoadImgDlg::CLoadImgDlg(BOOL bOpenFileDialog, LPCTSTR lpszDefExt, LPCTSTR lpszFileName,
                         DWORD dwFlags, LPCTSTR lpszFilter, CWnd *pParentWnd, DWORD dwSize, BOOL bVistaStyle) :
	CFileDialog(bOpenFileDialog, lpszDefExt, lpszFileName, dwFlags, lpszFilter, pParentWnd, dwSize, bVistaStyle)
{
	m_ofn.hwndOwner = pParentWnd->GetSafeHwnd();
	m_ofn.Flags |= OFN_EXPLORER | OFN_ENABLEHOOK | OFN_ENABLETEMPLATE;
	m_ofn.lpTemplateName = MAKEINTRESOURCE(IDD_LOAD_IMAGE);
	m_ofn.lpfnHook = OFNHookProc;
	m_ofn.lCustData = reinterpret_cast<LPARAM>(this);
}

CLoadImgDlg::~CLoadImgDlg()
{
}


BEGIN_MESSAGE_MAP(CLoadImgDlg, CFileDialog)
END_MESSAGE_MAP()



// обработчики сообщений CLoadImgDlg
UINT_PTR CALLBACK CLoadImgDlg::OFNHookProc(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
	static CRect rectHD;
	auto obj = reinterpret_cast<CLoadImgDlg *>(lParam);
	//!!! под Win8 хук работает, но параметр ему не передаётся, поэтому обойдёмся статическими объектами

	switch (uiMsg)
	{
		case WM_INITDIALOG:
		{
			::GetWindowRect(hdlg, &rectHD);
			break;
		}

		case WM_CHILDACTIVATE:
		{
			HWND hParent = ::GetParent(hdlg);
			CRect rectFD;
			BOOL bres = ::GetWindowRect(hParent, &rectFD);
			CRect rectHD1;
			bres = ::GetWindowRect(hdlg, &rectHD1);
			//увеличим окно в ширину
			bres = ::MoveWindow(hParent, rectFD.left, rectFD.top, rectFD.Width() + rectHD.Width(), rectFD.Height() - rectHD.Height(), FALSE);
			//переместим превьюшку на своё новое место
			bres = ::MoveWindow(hdlg, rectFD.Width() - 20, -355, rectHD.Width(), rectHD1.Height(), FALSE);
			break;
		}

		case WM_NOTIFY:
		{
			auto pNm = reinterpret_cast<LPNMHDR >(lParam);

			//auto pOFN = reinterpret_cast<LPOFNOTIFY >(wParam);

			if (pNm->code == CDN_SELCHANGE)
			{
				CString strPath;
				HWND hParent = ::GetParent(hdlg);
				::SendMessage(hParent, CDM_GETFILEPATH, 1024, reinterpret_cast<LPARAM>(strPath.GetBufferSetLength(1024)));
				strPath.ReleaseBuffer();
				CLoadImgDlg::DrawPreview(hdlg, strPath);
				return 0;
			}

			break;
		}
	}

	return 0;
}

void CLoadImgDlg::DrawPreview(HWND hdlg, const CString &strPath)
{
	CWnd *pDlg = CWnd::FromHandle(hdlg);
	CString strFormatName, strSystemFlag;
	GetImgFormat(strPath, strFormatName, strSystemFlag);
	pDlg->GetDlgItem(IDC_LI_SYSTEM)->SetWindowText(strFormatName);
	pDlg->GetDlgItem(IDC_LI_BOOTABLE)->SetWindowText(strSystemFlag);
}


void CLoadImgDlg::GetImgFormat(const CString &strImageName, CString &strFormatName, CString &strSystemFlag)
{
	PARSE_RESULT res = ParseImage(strImageName, 0);
	strSystemFlag = _T("");

	if (res.bImageBootable)
	{
		strSystemFlag.LoadString(IDS_DISK_SYSTEM);
	}

	switch (res.imageOSType)
	{
		case IMAGE_TYPE_ERROR:
			strFormatName.LoadString(IDS_DISK_FORMAT_ERROR);
			break;

		case IMAGE_TYPE_ANDOS:
			strFormatName.LoadString(IDS_DISK_FORMAT_ANDOS);
			break;

		case IMAGE_TYPE_MKDOS:
			strFormatName.LoadString(IDS_DISK_FORMAT_MKDOS);
			break;

		case IMAGE_TYPE_AODOS:
			strFormatName.LoadString(IDS_DISK_FORMAT_AODOS);
			break;

		case IMAGE_TYPE_NORD:
			strFormatName.LoadString(IDS_DISK_FORMAT_NORD);
			break;

		case IMAGE_TYPE_MIKRODOS:
			strFormatName.LoadString(IDS_DISK_FORMAT_MICRODOS);
			break;

		case IMAGE_TYPE_CSIDOS3:
			strFormatName.LoadString(IDS_DISK_FORMAT_CSIDOS);
			break;

		case IMAGE_TYPE_RT11:
			strFormatName.LoadString(IDS_DISK_FORMAT_RT11);
			break;

		case IMAGE_TYPE_NCDOS:
			strFormatName.LoadString(IDS_DISK_FORMAT_NCDOS);
			break;

		case IMAGE_TYPE_DXDOS:
			strFormatName.LoadString(IDS_DISK_FORMAT_DXDOS);
			break;

		case IMAGE_TYPE_MSDOS:
			strFormatName.LoadString(IDS_DISK_FORMAT_MSDOS);
			break;

		default:
		{
			strFormatName.LoadString(IDS_DISK_FORMAT_UNKNOWN);
			//теперь проверим на образ винчестера
			IMGFormat hddFormat;

			if (AnalyseImage(strImageName, &hddFormat))
			{
				if (hddFormat.bSamara)
				{
					strFormatName = _T("HDD Samara");
				}
				else
				{
					strFormatName = _T("HDD AltPro");
				}
			}
		}
	}
}

const char *CLoadImgDlg::strID_Andos = "ANDOS ";
const char *CLoadImgDlg::strID_DXDOS = "DX-DOS";
const char *CLoadImgDlg::strID_FAT12 = "FAT12   ";
const char *CLoadImgDlg::strID_Aodos = " AO-DOS ";
const char *CLoadImgDlg::strID_Nord = "NORD ";
const char *CLoadImgDlg::strID_Nord_Voland = "BY VOLAND"; //мультизагрузочные диски - тоже будем опознавать как норд
const char *CLoadImgDlg::strID_RT11 = "?BOOT-";
const char *CLoadImgDlg::strID_RT11Q = "?QUBOOT-";

USector CLoadImgDlg::m_sector;

#define BLOCK_SIZE SECTOR_SIZE

bool CLoadImgDlg::ReadLBA(FILE *f, void *buffer, UINT lba, int numSectors)
{
	if (f)
	{
		if (fseek(f, lba * BLOCK_SIZE, SEEK_SET) == 0)
		{
			lba = numSectors * BLOCK_SIZE;
			return (lba == fread(buffer, 1, lba, f));
		}
	}

	return false;
}

/*определение типа образа, на выходе номер, соответствующий определённой ОС
0 - образ не опознан, -1 - ошибка
*/

PARSE_RESULT CLoadImgDlg::ParseImage(const CString &fname, UINT nBaseOffset)
{
	FILE *BKFloppyImgFile = nullptr;
	PARSE_RESULT ret;
	bool bAC1 = false; //условие андос 1 (метка диска)
	bool bAC2 = false; //условие андос 2 (параметры диска)

	if ((BKFloppyImgFile = _tfopen(fname, _T("rb"))) == nullptr)
	{
		ret.imageOSType = IMAGE_TYPE_ERROR;
		return ret;
	}

	ret.strName = fname;
	ret.nBaseOffset = nBaseOffset;
	fseek(BKFloppyImgFile, 0, SEEK_END);
	ret.nImageSize = ftell(BKFloppyImgFile);//получим размер файла
	fseek(BKFloppyImgFile, 0, SEEK_SET);
	nBaseOffset /= BLOCK_SIZE; //смещение в LBA

	if (ReadLBA(BKFloppyImgFile, m_sector.b, nBaseOffset + 0, 1))
	{
		// Получим признак системного диска
		if (m_sector.w[0] == 0240 /*NOP*/)
			if (m_sector.w[1] != 5 /*RESET*/)
			{
				ret.bImageBootable = true;
			}

		//проверим тут, а то потом, прямо в условии присваивания не работают из-за оптимизации проверки условий
		bAC1 = (memcmp(strID_Andos, (char *)(m_sector.b + 04), strlen(strID_Andos)) == 0);
		//      секторов в кластере                   число файлов в корне       медиадескриптор                   число секторов в одной фат
		bAC2 = ((m_sector.b[015] == 4) && (*(uint16_t *)(m_sector.b + 021) == 112) && (m_sector.b[025] == 0371) && (*(uint16_t *)(m_sector.b + 026) == 2));

		// Узнаем формат образа
		if (m_sector.w[0400 / 2] == 0123456) //если микродос
		{
			//определяем с определённой долей вероятности систему аодос
			if (m_sector.b[4] == 032 && (m_sector.w[6 / 2] == 256 || m_sector.w[6 / 2] == 512))
			{
				//либо расширенный формат
				ret.imageOSType = IMAGE_TYPE_AODOS;
			}
			else if (memcmp(strID_Aodos, (char *)(m_sector.b + 4), strlen(strID_Aodos)) == 0)
			{
				//либо по метке диска
				//аодос 1.77 так не распознаётся. его будем считать просто микродосом. т.к. там нету директорий
				ret.imageOSType = IMAGE_TYPE_AODOS;
			}
			else if (memcmp(strID_Nord, (char *)(m_sector.b + 4), strlen(strID_Nord)) == 0)
			{
				//предполагаем, что это метка для системы
				ret.imageOSType = IMAGE_TYPE_NORD;
			}
			else if (memcmp(strID_Nord_Voland, (char *)(m_sector.b + 0xa0), strlen(strID_Nord_Voland)) == 0)
			{
				//мультизагрузочные диски - тоже будем опознавать как норд
				ret.imageOSType = IMAGE_TYPE_NORD;
			}
			/*обнаружилась нехорошая привычка AODOS и возможно NORD мимикрировать под MKDOS,
			так что проверка на него - последней, при этом, т.к. MKDOSу похрен на ячейки 04..012
			то иногда, если там были признаки аодоса, мкдосный диск принимается за аодосный.
			но это лучше, чем наоборот, ибо в первом случае просто в имени директории будет глючный символ,
			а во втором - вообще все директории пропадают.
			*/
			else if (m_sector.w[0402 / 2] == 051414)
			{
				ret.imageOSType = IMAGE_TYPE_MKDOS;
			}
			//тут надо найти способ как определить прочую экзотику
			else
			{
				int nRet = AnalyseMicrodos(BKFloppyImgFile, nBaseOffset, m_sector.b);

				switch (nRet)
				{
					case 0:
						ret.imageOSType = IMAGE_TYPE_MIKRODOS;
						break;

					case 1:
						ret.imageOSType = IMAGE_TYPE_AODOS;
						break;

					case 2:
						ret.imageOSType = IMAGE_TYPE_NORD;
						break;

					default:
						ret.imageOSType = IMAGE_TYPE_ERROR;
						break;
				}
			}
		}
		//проверка на формат FAT12, если надпись "FAT12"
		//однако, в некоторых андосах, нету идентификатора фс, зато есть метка андос,
		//но бывает, что и метки нету, зато параметры BPB специфические
		else if ( //если хоть что-то выполняется, то скорее всего это FAT12, но возможны ложные срабатывания из-за мусора в этой области
		    bAC1 || bAC2 ||
		    (memcmp(strID_FAT12, (char *)(m_sector.b + 066), strlen(strID_FAT12)) == 0)
		)
		{
			uint8_t nMediaDescriptor = m_sector.b[025];
			int nBootSize = *(uint16_t *)(m_sector.b + 016) * (*(uint16_t *)(m_sector.b + 013));
			uint8_t b[BLOCK_SIZE];
			ReadLBA(BKFloppyImgFile, b, nBaseOffset + nBootSize / BLOCK_SIZE, 1);

			if (b[1] == 0xff && b[0] == nMediaDescriptor) //если медиадескриптор в BPB и фат совпадают, то это точно FAT12
			{
				if (bAC1 || bAC2) //если одно из этих условий - то это скорее всего ANDOS
				{
					ret.imageOSType = IMAGE_TYPE_ANDOS;
				}
				else if (memcmp(strID_DXDOS, (char *)(m_sector.b + 04), strlen(strID_DXDOS)) == 0) //если метка диска DX-DOS, то это скорее всего DX-DOS, других вариантов пока нет.
				{
					ret.imageOSType = IMAGE_TYPE_DXDOS;
				}
				else
				{
					ret.imageOSType = IMAGE_TYPE_MSDOS; //тут только MS DOS остался
				}
			}
			else
			{
				ret.imageOSType = IMAGE_TYPE_UNKNOWN;
			}
		}
		//проверка на предположительно возможно НС-ДОС
		else if (m_sector.w[2 / 2] == 012700 && m_sector.w[4 / 2] == 0404 && m_sector.w[6 / 2] == 0104012)
		{
			//нс-дос никак себя не выделяет среди других ос, поэтому будем её определять по коду.
			ret.imageOSType = IMAGE_TYPE_NCDOS;
		}
		else
		{
			//дальше сложные проверки
			//проверим, а не рт11 ли у нас
			int t = IsRT11_old(BKFloppyImgFile, nBaseOffset, m_sector.b);

			switch (t)
			{
				case 0:
				{
					t = IsRT11(BKFloppyImgFile, nBaseOffset, m_sector.b, ret.nImageSize);

					switch (t)
					{
						case 0:
						{
							//нет, не рт11
							//проверим, а не ксидос ли у нас
							t = IsCSIDOS3(BKFloppyImgFile, nBaseOffset, m_sector.b);

							switch (t)
							{
								case 0:
									ret.imageOSType = IMAGE_TYPE_UNKNOWN;
									break;

								case 1:
									ret.imageOSType = IMAGE_TYPE_CSIDOS3;
									break;

								default:
									ret.imageOSType = IMAGE_TYPE_ERROR;
							}

							break;
						}

						case 1:
						{
							ret.imageOSType = IMAGE_TYPE_RT11;
							break;
						}

						default:
							ret.imageOSType = IMAGE_TYPE_ERROR;
					}

					break;
				}

				case 1:
				{
					ret.imageOSType = IMAGE_TYPE_RT11;
					break;
				}

				default:
					ret.imageOSType = IMAGE_TYPE_ERROR;
			}
		}
	}
	else
	{
		ret.imageOSType = IMAGE_TYPE_ERROR;
	}

	fclose(BKFloppyImgFile);
	return ret;
}

/*
анализатор каталога микродос, для уточнения формата.
2 - Nord
1 - Aodos
0 - Microdos
-1 - ошибка чтения файла
*/
int CLoadImgDlg::AnalyseMicrodos(FILE *f, UINT nBaseOffsetLBA, uint8_t *buf)
{
#pragma pack(push)
#pragma pack(1)
	struct MikrodosFileRecord
	{
		uint8_t dir_dir;   /*статус записи: бит 7 - бэд блок, остальные значения - неизвестно */
		uint8_t dir_num;   /*статус записи: значения неизвестны */
		uint8_t name[14];  //имя файла, если начинается с 0177 - каталог. и значит это не микродос
		uint16_t start_block;//начальный блок
		uint16_t len_blk;   //длина в блоках, если len_blk == 0 то это тоже с большой долей вероятности каталог
		uint16_t address;   //стартовый адрес
		uint16_t length;    //длина, или остаток длины от длины в блоках, если размер файла > 64кб
		MikrodosFileRecord()
		{
			memset(this, 0, sizeof(MikrodosFileRecord));
		}
		MikrodosFileRecord &operator = (const MikrodosFileRecord &src)
		{
			memcpy(this, &src, sizeof(MikrodosFileRecord));
			return *this;
		}
		MikrodosFileRecord &operator = (const MikrodosFileRecord *src)
		{
			memcpy(this, src, sizeof(MikrodosFileRecord));
			return *this;
		}
	};
#pragma pack(pop)
	int nRet = -1;
	int nCatSize = 10; //размер каталога - одна сторона нулевой дорожки.
	auto pCatBuffer = new uint8_t[nCatSize * BLOCK_SIZE];

	if (pCatBuffer)
	{
		//перемещаемся к нулевому сектору, + 0 - для наглядности
		if (ReadLBA(f, pCatBuffer, nBaseOffsetLBA + 0, nCatSize))
		{
			int nRecNum = *(uint16_t *)&pCatBuffer[030]; //читаем общее кол-во файлов.
			auto pDiskCat = reinterpret_cast<MikrodosFileRecord *>(pCatBuffer + 0500); //каталог диска
			int nMKDirFlag = 0;
			int nAODirFlag = 0;

			//сканируем каталог и ищем там директории
			for (int i = 0; i < nRecNum; ++i)
			{
				if (pDiskCat[i].name[0] == 0177)
				{
					nMKDirFlag++;
				}
				else if (pDiskCat[i].name[0] != 0 && pDiskCat[i].len_blk == 0)
				{
					nAODirFlag++;
				}
			}

			if (nMKDirFlag && nAODirFlag)
			{
				nRet = 0;
			}
			else if (nMKDirFlag)
			{
				nRet = 2;
			}
			else if (nAODirFlag)
			{
				nRet = 1;
			}
			else
			{
				nRet = 0;
			}
		}

		delete[]pCatBuffer;
	}
	else
	{
		CBKMessageBox(g_pstrErrors[BK_ERROR_NOTENMEMR], MB_OK);
	}

	return nRet;
}



//сравнение двух символов, без учёта регистра, русских букв не сравниваем, т.к. пока не нужно
//выход: true - равны
//      false - не равны
bool CLoadImgDlg::charcompare(uint8_t a, uint8_t b)
{
	if ('a' <= a && a <= 'z')
	{
		a -= ' '; //делаем из маленькой буквы большую
	}

	if ('a' <= b && b <= 'z')
	{
		b -= ' '; //делаем из маленькой буквы большую
	}

	if (a == b)
	{
		return true;
	}

	return false;
}

bool CLoadImgDlg::substrfind(uint8_t *bufs, size_t len_bufs, uint8_t *buff, size_t len_buff)
{
	size_t b_pos = 0; //позиция в буфере.
	size_t s_pos = 0; //позиция в строке, которую ищем
	size_t t_pos;
	bool bOk = false;

	while (b_pos < len_bufs)
	{
		//пока есть где искать
		//ищем совпадение с первым символом
		if (charcompare(bufs[b_pos++], buff[s_pos]))
		{
			//нашли. теперь проверим на совпадения с остальными символами
			t_pos = b_pos;
			s_pos++; //первый символ совпал, начинаем проверять со второго
			bOk = true; //флаг выхода из цикла поиска на совпадение строки

			while (s_pos < len_buff)
			{
				//пока строка не кончится
				if (!charcompare(buff[s_pos++], bufs[t_pos++]))
				{
					//если символ не совпал
					bOk = false; //то не ОК
					break;
				}
			}

			b_pos = t_pos;
			s_pos = 0; //вернёмся на начало строки

			if (bOk)
			{
				//если таки вся строка совпала
				break; //незачем дальше искать
			}
		}
	}

	//на выходе отсюда bOk == true если строка нашлась, иначе - false
	return bOk;
}

/*
Проверим, а не rt11 ли это старым методом. Поскольку непонятно как узнать, что это у нас УКНЦшный АДОС.
выход:
1 - rt11
0 - не rt11
-1 - ошибка чтения файла
*/
int CLoadImgDlg::IsRT11_old(FILE *f, UINT nBaseOffsetLBA, uint8_t *buf)
{
	int nRet = 0;

	//перемещаемся к нулевому сектору, + 0 - для наглядности

	if (!ReadLBA(f, buf, nBaseOffsetLBA + 0, 1))
	{
		return -1;
	}

	//надо найти строку strID_RT11 где-то в нулевом секторе.
	size_t s_len = strlen(strID_RT11);
	size_t b_len = BLOCK_SIZE - s_len;
	bool bOk = substrfind(buf, b_len, (uint8_t *)strID_RT11, s_len);

	if (bOk)
	{
		nRet = 1;
	}
	else
	{
		//если не нашли строку первого вида, поищем второго
		s_len = strlen(strID_RT11Q);
		b_len = BLOCK_SIZE - s_len;
		bOk = substrfind(buf, b_len, (uint8_t *)strID_RT11Q, s_len);

		if (bOk)
		{
			nRet = 1;
		}
	}

	return nRet;
}

/*
Проверим, а не rt11 ли это. выход:
1 - rt11
0 - не rt11
-1 - ошибка чтения файла
*/
int CLoadImgDlg::IsRT11(FILE *f, UINT nBaseOffsetLBA, uint8_t *buf, UINT nFileSize)
{
	int nRet = 0;
	auto pwBuf = reinterpret_cast<uint16_t *>(buf);
	uint8_t bufsh[BLOCK_SIZE];
	//проверку будем осуществлять методом эмуляции чтения каталога.
	unsigned int nImgSize = nFileSize / BLOCK_SIZE; //размер образа в блоках.

	//перемещаемся к первому сектору
	if (!ReadLBA(f, buf, nBaseOffsetLBA + 1, 1))
	{
		return -1;
	}

	unsigned int nBeginBlock = pwBuf[0724 / 2]; //получим блок, с которого начинается сектор.
	unsigned int nCurrentSegment = 1; //номер текущего сегмента
	unsigned int nTotalSegments = 0;    //всего возможных сегментов в каталоге
	unsigned int nUsedSegments = -1;    //число занятых сегментов, не должно совпадать с nCount
	unsigned int nAddBytes = 0;
	unsigned int nCount = 0; //счётчик пройденных сегментов

	//!!! фикс для АДОС
	if ((nBeginBlock == 0) || (nBeginBlock > 255))
	{
		nBeginBlock = 6;
	}

	if (nBeginBlock < nImgSize)
	{
		while (nCurrentSegment > 0)
		{
			int offs = ((nCurrentSegment - 1) * 2 + nBeginBlock);

			if (!ReadLBA(f, bufsh, nBaseOffsetLBA + offs, 1)) //перемещаемся к началу текущего сегмента каталога и читаем
			{
				return -1;
			}

			auto CurrentSegmentHeader = reinterpret_cast<RT11SegmentHdr *>(bufsh);

			if (nTotalSegments == 0)
			{
				nTotalSegments = CurrentSegmentHeader->segments_num; //если в первый раз, запомним число сегментов.

				if (nTotalSegments > 31) //если число сегментов больше 31, то не RT-11
				{
					break;
				}

				nUsedSegments = CurrentSegmentHeader->used_segments;

				//если в первом сегменте счётчик использованных сегментов больше общего числа сегментов
				if (nUsedSegments > nTotalSegments)
				{
					break;
				}

				nAddBytes = CurrentSegmentHeader->filerecord_add_bytes;

				if (nAddBytes > 252) //если число доп слов больше размера сегмента - то это не RT-11
				{
					//т.к. в сегменте обязательно должен быть маркер конца сегмента, то в нем должны умещаться как минимум 2 записи.
					//поэтому 72*7 делим пополам
					break;
				}
			}
			else
			{
				//если не в первый - сравним старое кол-во с новым, если не равно - значит не RT-11
				if (nTotalSegments != CurrentSegmentHeader->segments_num)
				{
					break;
				}

				if (nAddBytes != CurrentSegmentHeader->filerecord_add_bytes)
				{
					break;
				}
			}

			//если начальный блок в сегменте выходит за пределы образа - то это не RT-11
			if (CurrentSegmentHeader->file_block > nImgSize)
			{
				break;
			}

			nCurrentSegment = CurrentSegmentHeader->next_segment;

			if (nCurrentSegment > 31) //если номер следующего сегмента больше 31, то не RT-11
			{
				break;
			}

			if (nCurrentSegment > nTotalSegments) //если номер следующего сегмента больше общего кол-ва сегментов, то не RT-11
			{
				break;
			}

			nCount++; //чтобы не зациклиться, считаем пройденные сегменты

			if (nCount > nTotalSegments) //если их было больше, чем общее число сегментов - то это не RT-11
			{
				break;
			}
		}

		//когда выходим из цикла
		//если соблюдаются следующие условия, то очень вероятно, что это всё-таки RT-11
		// третье условие - избыточно
		if ((nCurrentSegment == 0) && (nCount == nUsedSegments) && (nCount <= nTotalSegments))
		{
			nRet = 1;
		}
	}

	return nRet;
}

/*
Проверим, а не ксидос ли это. выход:
1 - ксидос
0 - не ксидос
-1 - ошибка чтения
*/
int CLoadImgDlg::IsCSIDOS3(FILE *f, UINT nBaseOffsetLBA, uint8_t *buf)
{
	auto pwBuf = reinterpret_cast<uint16_t *>(buf);
	int nRet = -1;

	//проверим, а не ксидос ли у нас
	if (ReadLBA(f, buf, nBaseOffsetLBA + 2, 1))
	{
		if ((pwBuf[8 / 2] == 0123123) || (pwBuf[8 / 2] == 0123124))
		{
			//если ксидос или особый ксидос
			if ((pwBuf[4 / 2] == 0123123) && (pwBuf[6 / 2] == 0123123)) //если ксидос3
			{
				nRet = 1;
			}
			else
			{
				nRet = 0; //про ксидос других версий ничего неизвестно
			}
		}
		else
		{
			nRet = 0; //вообще не ксидос
		}
	}

	return nRet;
}


//почти копипаста из BKHDDTool, функции анализа образа HDD

//константы для доступа к данным таблицы разделов АльтПро

//номер блока, где находится таблица разделов АльтПро
#define AHDD_PT_SEC 7
#define AHDD_PART_W 251
#define AHDD_LOGD_W 252
#define AHDD_SEC_W  253
#define AHDD_HEAD_W 254
#define AHDD_CYL_W  255

#define AHDD_PART_B 0766
#define AHDD_LOGD_B 0770
#define AHDD_UNI_B  0771
#define AHDD_SEC_B  0772
#define AHDD_HEAD_B 0774
#define AHDD_DRV_B  0775
#define AHDD_CYL_B  0776

//константы для доступа к данным таблицы разделов Самара

//номер блока, где находится таблица разделов Самара
#define SHDD_PT_SEC     1

#define SHDD_BOOT_W     0   //# устр. для загрузки по умолч. (0 - А, 2 - С ...)
#define SHDD_CYLVOL_W   1   //объём цилиндра (общее количество секторов на дорожке) == H * S
#define SHDD_HEAD_SEC_W 2   //количество секторов на дорожке & номер последней головки (H - 1)
#define SHDD_PART_W     3   //таблица разделов

#define SHDD_BOOT_B     0   //# устр. для загрузки по умолч. (0 - А, 2 - С ...)
#define SHDD_CYLVOL_B   2   //объём цилиндра (общее количество секторов на дорожке) == H * S
#define SHDD_SEC_B      4   //количество секторов на дорожке
#define SHDD_HEAD_B     5   //номер последней головки (H - 1)
#define SHDD_PART_B     6   //таблица разделов

//константы для доступа к данным начального блока раздела Самара
#define SHDD_NLD_W      0   //номер лог. диска 
#define SHDD_LD_LEN_W   1   //размер лог. диска в блоках
#define SHDD_LD_FLAGS_W 2   //флаги - признаки
#define SHDD_ADR_BOOT_W 3   //адрес загрузки загрузчика лог. диска
#define SHHD_ADR_PAR_W  4   //адрес блока параметров для загрузчика
#define SHDD_PAGE_W     5   //состояние регистра страниц



bool CLoadImgDlg::AnalyseImage(const CString &fname, IMGFormat *pFormat)
{
	bool bRet = false;
	FILE *f = nullptr;

	if ((f = _tfopen(fname, _T("rb"))) == nullptr)
	{
		return false;
	}

	bRet = CheckFormat(f, pFormat);
	fclose(f);
	return bRet;
}

void CLoadImgDlg::InverseSector(USector &sector)
{
	for (uint16_t &i : sector.w)
	{
		i = ~i;
	}
}


//проверка формата и заодно определение геометрии
//выход: true - параметры структуры IMGFormat достоверны
//       false - нет. формат не распознан
bool CLoadImgDlg::CheckFormat(FILE *f, IMGFormat *pFormat)
{
	bool bRet = false;
	bool bHDI = false;

	if (fread(m_sector.b, 1, SECTOR_SIZE, f) == SECTOR_SIZE)  //прочтём первый сектор.
	{
		//подсчитаем контрольную сумму
		uint8_t cs = 0;

		for (int i = 0; i < SECTOR_SIZE - 1; ++i)
		{
			cs += m_sector.b[i];
		}

		cs = -cs;

		if (m_sector.b[SECTOR_SIZE - 2] == 0xA5 && m_sector.b[SECTOR_SIZE - 1] == cs)
		{
			bHDI = true; //если первый сектор правильный - то это вероятно образ HDI
		}
		else
		{
			bHDI = false; //если нет - то это просто IMG
			fseek(f, 0, SEEK_SET); //вернёмся к началу образа.
		}

		pFormat->bHDI = bHDI;
		IMGFormat aimgf(pFormat);
		IMGFormat simgf(pFormat);
		//проверка на альтпро.
		bool bAltPro = CheckAltPro(f, &aimgf);
		//проверка на формат Самара
		bool bSamara = CheckSamara(f, &simgf);

		if (bAltPro)
		{
			*pFormat = aimgf;
		}
		else if (bSamara)
		{
			*pFormat = simgf;
		}

		bRet = bAltPro || bSamara;
	}

	return bRet;
}

//проверка на альтпро.
bool CLoadImgDlg::CheckAltPro(FILE *f, IMGFormat *imgf)
{
	bool bRet = false;

	if ((fseek(f, AHDD_PT_SEC * SECTOR_SIZE + ((imgf->bHDI) ? SECTOR_SIZE : 0), SEEK_SET) == 0)
	        && (fread(m_sector.b, 1, SECTOR_SIZE, f) == SECTOR_SIZE))
	{
		//проверяем блок 7 (сектор 8)
		InverseSector(m_sector);
		//прочитаем геометрию
		imgf->C = m_sector.w[AHDD_CYL_W];
		imgf->H = m_sector.w[AHDD_HEAD_W] & 0xff; //нужен только младший байт
		imgf->S = m_sector.w[AHDD_SEC_W];
		//и количество разделов
		imgf->nLogDiskNum = m_sector.w[AHDD_LOGD_W] & 0xff;
		uint16_t crc = 012701;

		//посчитаем контрольную сумму
		for (int i = 0; i < (imgf->nLogDiskNum * 2 + 4); ++i)
		{
			crc += m_sector.w[AHDD_CYL_W - i];
		}

		//если контрольная сумма совпадает.
		if (m_sector.w[AHDD_PART_W - imgf->nLogDiskNum * 2] == crc)
		{
			bRet = true; //параметры структуры IMGFormat достоверны
		}
	}

	return bRet;
}

//проверка на формат Самара
bool CLoadImgDlg::CheckSamara(FILE *f, IMGFormat *imgf)
{
	bool bRet = false;
	USector buf;

	if ((fseek(f, SHDD_PT_SEC * SECTOR_SIZE + ((imgf->bHDI) ? SECTOR_SIZE : 0), SEEK_SET) == 0)
	        && (fread(m_sector.b, 1, SECTOR_SIZE, f) == SECTOR_SIZE))
	{
		//проверяем блок 1 (сектор 2)
		InverseSector(m_sector);
		//Определяем геометрию
		int nCylVol = m_sector.w[SHDD_CYLVOL_W]; //объём цилиндра
		imgf->S = m_sector.b[SHDD_SEC_B]; //число секторов на дорожке
		imgf->H = m_sector.b[SHDD_HEAD_B] + 1; //число головок

		//число цилиндров нигде не хранится, и его надо будет вычислить примерное количество
		if (nCylVol && (nCylVol == imgf->S * imgf->H)) //если совпало. значит пока всё правильно
		{
			fseek(f, 0, SEEK_END);
			long len = ftell(f);
			fseek(f, 0, SEEK_SET);
			len = (len / SECTOR_SIZE) - ((imgf->bHDI) ? 1 : 0);
			imgf->C = uint16_t(len / nCylVol); //вычислим количество дорожек

			if (len % nCylVol) //если ещё есть неполный цилиндр
			{
				imgf->C++; //его тоже засчитаем
			}

			int nPartitions = 0;
			uint16_t nld = 2;
			imgf->bSamara = true; //предполагем что формат самарский

			while (nPartitions < 64)
			{
				// получим номер начального блока
				UINT lba = UINT(m_sector.w[SHDD_PART_W + nPartitions * 2]) | (UINT(m_sector.w[SHDD_PART_W + 1 + nPartitions * 2]) << 16);

				if (lba == 0) //если 0 - разделы кончились
				{
					break;
				}
				//теперь перейдём к разделу и посмотрим его параметры
				lba *= SECTOR_SIZE; //смещение

				if ((fseek(f, lba + ((imgf->bHDI) ? SECTOR_SIZE : 0), SEEK_SET) == 0)
				        && (fread(buf.b, 1, SECTOR_SIZE, f) == SECTOR_SIZE))
				{
					InverseSector(buf);

					if (buf.w[SHDD_NLD_W] != nld) //если номер логического диска не совпал с ожидаемым
					{
						imgf->bSamara = false;  //то либо фромат не самарский, либо образ повреждён
						break;
					}
				}
				else
				{
					imgf->bSamara = false;
					break;
				}

				nPartitions++; nld++;
			}

			imgf->nLogDiskNum = nPartitions;
		}

		bRet = imgf->bSamara;
	}

	return bRet;
}

