/*
 *  работа с образами винчестера БК в форматах "Самара" и "Альтпро"
 *
 Прога по мотивам исходника bkhdd.cpp (c) 2010 Terra software
*/

#include "stdafx.h"
#include <direct.h>
#include <io.h>
#include <fcntl.h>
#include "getopt.h"
#include <vector>
#include <algorithm>

#include "BKHDDTool.h"
#include "HDIStuff.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// Единственный объект приложения

//CWinApp theApp;

using namespace std;

//mbr для Альтпро
WORD g_MBRAltPro[256] = { 0xA0, 0x15DF, 0xFF87, 0xFFEA, 0x15DF, 0x1C, 0xFFE0, 0x87, 0x0 };

USector g_sector;
ProrgamOptions g_options;

int _tmain(int argc, TCHAR *argv[], TCHAR *envp[])
{
	srand((unsigned)time(NULL));
	_tsetlocale(LC_ALL, _T("Russian"));
	int nRetCode = 0;
	HMODULE hModule = ::GetModuleHandle(NULL);

	if (hModule != NULL)
	{
		// инициализировать MFC, а также печать и сообщения об ошибках про сбое
		if (!AfxWinInit(hModule, NULL, ::GetCommandLine(), 0))
		{
			// TODO: измените код ошибки соответственно своим потребностям
			_tprintf(_T("Критическая ошибка: сбой при инициализации MFC\n"));
			nRetCode = 1;
		}
		else
		{
			FILE *pHDDfile = NULL;
			TCHAR curDir[_MAX_PATH];
			GetCurrentDirectory(_MAX_PATH, curDir);
			g_options.strAppPath = CString(curDir);
			static struct option long_options[] =
			{
				{_T("help"),        ARG_NONE, 0, _T('?')},
				{_T("altpro"),      ARG_NONE, 0, _T('a')},
				{_T("samara"),      ARG_NONE, 0, _T('m')},
				{_T("hdi"),         ARG_NONE, 0, _T('i')},
				{_T("path"),        ARG_REQ,  0, _T('p')},
				{_T("cylinder"),    ARG_REQ,  0, _T('c')},
				{_T("head"),        ARG_REQ,  0, _T('h')},
				{_T("sector"),      ARG_REQ,  0, _T('s')},
				{ ARG_NULL , ARG_NULL , ARG_NULL , ARG_NULL }
			};
			static TCHAR optstring[] = _T("?amip:c:h:s:");
			int option_index = 0;
			int c;
			BOOL bShowHelp = FALSE;

			while ((c = getopt_long(argc, argv, optstring, long_options, &option_index)) != -1)
			{
				// Handle options
				switch (c)
				{
					case _T('?'):
						bShowHelp = TRUE;
						break;

					case _T('a'):
						g_options.bAltPro = TRUE;
						g_options.bSamara = FALSE;
						break;

					case _T('m'):
						g_options.bAltPro = FALSE;
						g_options.bSamara = TRUE;
						break;

					case _T('i'):
						g_options.bHDI = TRUE;
						break;

					case _T('p'):
						g_options.strPath = CString(optarg);
						break;

					case _T('c'):
						g_options.C = LOWORD(_tcstol(optarg, 0, 10));
						g_options.bCreate = TRUE;
						break;

					case _T('h'):
						g_options.H = LOWORD(_tcstol(optarg, 0, 10));
						g_options.bCreate = TRUE;
						break;

					case _T('s'):
						g_options.S = LOWORD(_tcstol(optarg, 0, 10));
						g_options.bCreate = TRUE;
						break;
				}

				if (bShowHelp)
				{
					Usage();
					return 0;
				}
			}

			argc -= optind;
			argv += optind;

			if (argc < 1)
			{
				Usage();
			}
			else
			{
				g_options.strImgName = CString(argv[0]);

				if (g_options.bCreate)
				{
					if (g_options.bHDI)
					{
						//если создаём hdi файл, то подменим расширение.
						TCHAR pDrive[_MAX_DRIVE];
						TCHAR pPath[_MAX_PATH];
						TCHAR pTitle[_MAX_PATH];
						TCHAR pExt[_MAX_EXT];
						_tsplitpath_s(g_options.strImgName, pDrive, _MAX_DRIVE, pPath, _MAX_PATH, pTitle, _MAX_PATH, pExt, _MAX_EXT);
						g_options.strImgName = CString(pDrive) + CString(pPath) + CString(pTitle) + _T(".hdi");
					}

					//создаём образ
					pHDDfile = _tfopen(g_options.strImgName, _T("wb"));

					if (pHDDfile)
					{
						IMGFormat format;
						format.bHDI = g_options.bHDI;
						format.bSamara = g_options.bSamara;
						format.C = g_options.C;
						format.H = g_options.H;
						format.S = g_options.S;
						format.nLogDiskNum = 0;

						if (g_options.bHDI)
						{
							//создаём заголовочный сектор HDI.
							CreateHDISector(&format, g_sector.b);

							if (SECTOR_SIZEB != fwrite(g_sector.b, 1, SECTOR_SIZEB, pHDDfile))
							{
								_tprintf(_T("Не удалось записать HDI сектор в файл %s\n"), g_options.strImgName.GetBuffer());
							}
						}

						AssemblyImage(pHDDfile, &format);
						fclose(pHDDfile);
					}
					else
					{
						_tprintf(_T("Не удалось создать файл %s\n"), g_options.strImgName.GetBuffer());
						nRetCode = 1;
					}
				}
				else
				{
					//разбираем образ
					pHDDfile = _tfopen(g_options.strImgName, _T("rb"));

					if (pHDDfile)
					{
						AnalyseImage(pHDDfile);
						fclose(pHDDfile);
					}
					else
					{
						_tprintf(_T("Не удалось открыть файл %s\n"), g_options.strImgName.GetBuffer());
						nRetCode = 1;
					}
				}
			}
		}
	}
	else
	{
		// TODO: Измените код ошибки соответственно своим потребностям
		_tprintf(_T("Критическая ошибка: неудачное завершение GetModuleHandle\n"));
		nRetCode = 1;
	}

	return nRetCode;
}

void Usage()
{
	_tprintf(_T("Программа для работы с образами винчестера БК0010-11\n"));
	_tprintf(_T("(c) 2010 Terra software, 2014 gid\n\n"));
	_tprintf(_T("Использование:\n"));
	_tprintf(_T("bkhdd -? (--help)\n"));
	_tprintf(_T("  Вывод этой справки.\n"));
	_tprintf(_T("bkhdd -a | -m [-i][-p<Путь>][-c<N> -h<N> -s<N>] <Имя образа HDD>\n"));
	_tprintf(_T("  -a (--altpro) - использовать формат образа диска 'АльтПро'\n"));
	_tprintf(_T("  -m (--samara) - использовать формат образа диска 'Самара'\n"));
	_tprintf(_T("    Если не задана ни одна опция, по умолчанию используется формат 'АльтПро'.\n"));
	_tprintf(_T("  -i (--hdi) - создавать образ в формате hdi. При разборке флаг игнорируется.\n"));
	_tprintf(_T("  -p<Путь> (--path <Путь>) - директория, где искать образы дискет для\n"));
	_tprintf(_T("    сборки образа винчестера, или куда сохранять образы дискет при разборке\n"));
	_tprintf(_T("    образа винчестера.\n"));
	_tprintf(_T("  -c<N> (--cylinder <N>) - количество цилиндров (дорожек)\n"));
	_tprintf(_T("  -h<N> (--head <N>) - количество головок\n"));
	_tprintf(_T("  -s<N> (--sector <N>) - количество секторов на дорожке\n\n"));
	_tprintf(_T("  <Имя образа HDD> - возможно задать с полным путём к файлу.\n\n"));
	_tprintf(_T("Если ключ -p не задан, то используется та же директория, где находится образ.\n"));
	_tprintf(_T("Если задан хоть один ключ, определяющий геометрию, то это означает создание\n"));
	_tprintf(_T("образа винчестера из образов дискет, иначе - разборка образа винчестера на\n"));
	_tprintf(_T("образы дискет.\n\n"));
	_tprintf(_T("При разборке создаются файлы part_nnn.img, где nnn - номера рзделов в том порядке,\n"));
	_tprintf(_T("в котором они находились в образе винчестера.\n"));
	_tprintf(_T("При сборке образы разделов сортируются в алфавитном порядке, и в таком же порядке\n"));
	_tprintf(_T("размещаются в образе винчестера.\n"));
	_tprintf(_T("После создания образ винчестера можно записать на винчестер или карту памяти\n"));
	_tprintf(_T("с помощью бесплатной утилиты dd -http://www.chrysocome.net/dd\n"));
}

BOOL AssemblyImage(FILE *f, IMGFormat *imgf)
{
	BOOL bRet = FALSE;
	_tprintf(_T("Тип образа: %s\n"), (imgf->bHDI) ? _T("HDI") : _T("IMG"));
	_tprintf(_T("Формат образа: %s\n"), (imgf->bSamara) ? _T("Самара") : _T("АльтПро"));
	_tprintf(_T("Геометрия\nКоличество цилиндров: %d\n"), imgf->C);
	_tprintf(_T("Количество головок:   %d\n"), imgf->H);
	_tprintf(_T("Количество секторов:  %d\n"), imgf->S);
	MakeOutDir(TRUE);

	if (imgf->bSamara)
	{
		bRet = AssemblySamara(f, imgf);
	}
	else
	{
		bRet = AssemblyAltPro(f, imgf);
	}

	return bRet;
}

BOOL AnalyseImage(FILE *f)
{
	BOOL bRet = FALSE;
	BOOL bHDI = FALSE;

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

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

		cs = -cs;

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

		IMGFormat format;
		format.bHDI = bHDI;
		_tprintf(_T("Тип образа: %s\n"), (format.bHDI) ? _T("HDI") : _T("IMG"));

		if (CheckFormat(f, &format))
		{
			_tprintf(_T("Формат образа: %s\n"), (format.bSamara) ? _T("Самара") : _T("АльтПро"));
			_tprintf(_T("Геометрия\nКоличество цилиндров: %d\n"), format.C);
			_tprintf(_T("Количество головок:   %d\n"), format.H);
			_tprintf(_T("Количество секторов:  %d\n"), format.S);
			_tprintf(_T("\nКоличество разделов: %d\n"), format.nLogDiskNum);
			MakeOutDir();

			if (format.bSamara)
			{
				bRet = SplitSamara(f, &format);
			}
			else
			{
				bRet = SplitAltPro(f, &format);
			}
		}
		else
		{
			_tprintf(_T("Ошибка чтения или формат нераспознан!\n"));
		}
	}

	return bRet;
}

void InverseSector(USector &sector)
{
	for (int i = 0; i < SECTOR_SIZEW; ++i)
	{
		sector.w[i] = ~sector.w[i];
	}
}

//проверка формата и заодно определение геометрии
//выход: TRUE - параметры структуры IMGFormat достоверны
//       FALSE - нет. формат не распознан
BOOL CheckFormat(FILE *f, IMGFormat *imgf)
{
	BOOL bRet = FALSE;
	IMGFormat aimgf(imgf);
	IMGFormat simgf(imgf);
	//проверка на альтпро.
	BOOL bAltPro = CheckAltPro(f, &aimgf);
	//проверка на формат Самара
	BOOL bSamara = CheckSamara(f, &simgf);

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

	bRet = bAltPro | bSamara;

	if (bSamara && g_options.bAltPro)
	{
		_tprintf(_T("Формат образа опознан как 'Самара', но в опциях задано обрабатывать как 'АльтПро'\n"));
		_tprintf(_T("Образ будет обработан как 'Самара'\n"));
	}
	else if (bAltPro && g_options.bSamara)
	{
		_tprintf(_T("Формат образа опознан как 'АльтПро', но в опциях задано обрабатывать как 'Самара'\n"));
		_tprintf(_T("Образ будет обработан как 'АльтПро'\n"));
	}

	if (!bRet) //образ никак неопознан.
	{
		if (g_options.bAltPro)
		{
			_tprintf(_T("Формат образа не опознан, но в опциях задано обрабатывать как 'АльтПро'\n"));
			_tprintf(_T("Образ будет обработан как 'АльтПро'\n"));
			g_options.bSamara = FALSE;
			*imgf = aimgf;
			bRet = TRUE;
		}
		else if (g_options.bSamara)
		{
			_tprintf(_T("Формат образа не опознан, но в опциях задано обрабатывать как 'Самара'\n"));
			_tprintf(_T("Образ будет обработан как 'Самара'\n"));
			g_options.bSamara = TRUE;
			*imgf = simgf;
			bRet = TRUE;
		}
	}

	return bRet;
}

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

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

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

		//если контрольная сумма совпадает.
		if (g_sector.w[AHDD_PART_W - imgf->nLogDiskNum * 2] == crc)
		{
			bRet = TRUE; //параметры структуры IMGFormat достоверны
		}
	}
	else
	{
		_tprintf(_T("Ошибка чтения таблицы разделов формата 'АльтПро'\n"));
	}

	return bRet;
}

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

	if ((fseek(f, SHDD_PT_SEC * SECTOR_SIZEB + ((imgf->bHDI) ? SECTOR_SIZEB : 0), SEEK_SET) == 0)
	        && (fread(g_sector.b, 1, SECTOR_SIZEB, f) == SECTOR_SIZEB))
	{
		//проверяем блок 1 (сектор 2)
		InverseSector(g_sector);
		//прочитаем заранее геометрию
		imgf->C = g_sector.w[SHDD_CYL_W];
		imgf->H = g_sector.b[SHDD_HEAD_B];
		imgf->S = g_sector.b[SHDD_SEC_B];
		int nPartitions = 0;
		DWORD lba = 0;

		while (nPartitions < 64)
		{
			// подсчитаем диски
			lba = ((g_sector.w[SHDD_PART_W + nPartitions * 2] << 16) | g_sector.w[SHDD_PART_W + 1 + nPartitions * 2]);

			if (lba == 0)
			{
				break;
			}

			nPartitions++;
		}

		if ((nPartitions != 0) && ((imgf->C | imgf->H | imgf->S) != 0))
		{
			imgf->nLogDiskNum = nPartitions;
			imgf->bSamara = TRUE;
			WORD nld = 2;

			for (int i = 0; i < nPartitions; ++i, nld++)
			{
				DWORD lba = ((g_sector.w[SHDD_PART_W + i * 2] << 16) | g_sector.w[SHDD_PART_W + 1 + i * 2]) * SECTOR_SIZEB;

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

					if (buf.w[SHDD_NLD_W] != nld)
					{
						imgf->bSamara = FALSE;
						break;
					}
				}
				else
				{
					imgf->bSamara = FALSE;
					break;
				}
			}

			bRet = imgf->bSamara;
		}
	}
	else
	{
		_tprintf(_T("Ошибка чтения таблицы разделов формата 'Самара'\n"));
	}

	return bRet;
}

//разбираем образ винта альтпро на лог.диски
BOOL SplitAltPro(FILE *f, IMGFormat *imgf)
{
	BOOL bRet = FALSE;
	int nErrors = 0;

	//прочитаем сектор 8, нам нужна таблица разделов.
	if ((fseek(f, AHDD_PT_SEC * SECTOR_SIZEB + ((imgf->bHDI) ? SECTOR_SIZEB : 0), SEEK_SET) == 0)
	        && (fread(g_sector.b, 1, SECTOR_SIZEB, f) == SECTOR_SIZEB))
	{
		InverseSector(g_sector);

		for (int nld = 0; nld < imgf->nLogDiskNum; ++nld)
		{
			int nCyl = g_sector.w[AHDD_PART_W - nld * 2]; //первое слово из таблицы разделов
			int nHead = nCyl & 0xF; //номер головки из таблицы разделов
			nCyl >>= 4; //номер дорожки из таблицы разделов
			int lba = (nCyl * imgf->H + nHead) * imgf->S * SECTOR_SIZEB; //рассчитываем начало раздела в блоках
			int nLen = g_sector.w[AHDD_PART_W - 1 - nld * 2]; //размер логического диска в блоках
			_tprintf(_T("part #%d:: cyl = %d head = %d lba = %ld\nlen = %d\r") , nld, nCyl, nHead, lba, nLen);

			if (fseek(f, lba + ((imgf->bHDI) ? SECTOR_SIZEB : 0), SEEK_SET) == 0)
			{
				//переместимся к началу раздела
				if (!(bRet = WriteLogDiskImage(f, nld, nLen)))
				{
					_tprintf(_T("\nОшибка извлечения раздела %d\n"), nld);
					nErrors++;
				}
			}
			else
			{
				_tprintf(_T("\nОшибка перемещения к разделу %d\n"), nld);
				nErrors++;
			}
		}

		_tprintf(_T("\nУспешно извлечено %d разделов\n"), imgf->nLogDiskNum - nErrors);

		if (nErrors)
		{
			_tprintf(_T("С ошибками - %d разделов\n"), nErrors);
		}
	}
	else
	{
		_tprintf(_T("Ошибка чтения таблицы разделов формата 'АльтПро'\n"));
	}

	return bRet;
}

//разбираем образ винта самара на лог.диски
BOOL SplitSamara(FILE *f, IMGFormat *imgf)
{
	BOOL bRet = FALSE;
	int nErrors = 0;
	USector buf;

	//прочитаем сектор 2, нам нужна таблица разделов.
	if ((fseek(f, SHDD_PT_SEC * SECTOR_SIZEB + ((imgf->bHDI) ? SECTOR_SIZEB : 0), SEEK_SET) == 0)
	        && (fread(g_sector.b, 1, SECTOR_SIZEB, f) == SECTOR_SIZEB))
	{
		InverseSector(g_sector);

		for (int nld = 0; nld < imgf->nLogDiskNum; ++nld)
		{
			int lba = ((g_sector.w[SHDD_PART_W + nld * 2] << 16) | g_sector.w[SHDD_PART_W + 1 + nld * 2]) * SECTOR_SIZEB;

			//перемещаемся к началу лог диска
			if (fseek(f, lba + ((imgf->bHDI) ? SECTOR_SIZEB : 0), SEEK_SET) == 0)
			{
				//читаем начальный блок
				if ((fread(buf.b, 1, SECTOR_SIZEB, f)) == SECTOR_SIZEB)
				{
					InverseSector(buf);
					int nLen = buf.w[1];
					_tprintf(_T("part #%d:: lba = %ld len = %d\r"), nld, lba, nLen);

					//переместимся к началу раздела
					if (!(bRet = WriteLogDiskImage(f, nld, nLen)))
					{
						_tprintf(_T("\nОшибка извлечения раздела %d\n"), nld);
						nErrors++;
					}
				}
				else
				{
					_tprintf(_T("\nОшибка чтения начального блока раздела %d\n"), nld);
					nErrors++;
				}
			}
			else
			{
				_tprintf(_T("\nОшибка перемещения к начальному блоку раздела %d\n"), nld);
				nErrors++;
			}
		}

		_tprintf(_T("\nУспешно извлечено %d разделов\n"), imgf->nLogDiskNum - nErrors);

		if (nErrors)
		{
			_tprintf(_T("С ошибками - %d разделов\n"), nErrors);
		}
	}
	else
	{
		_tprintf(_T("Ошибка чтения таблицы разделов формата 'Самара'\n"));
	}

	return bRet;
}

//запись раздела из образа винчестера в отдельный образ
BOOL WriteLogDiskImage(FILE *f, int nld, int nLen)
{
	BOOL bRet = TRUE;
	USector buf;
	TCHAR pOutName[64] = {0};
	//сформируем очередное имя
	_stprintf(pOutName, _T("part_%03d.img"), nld);
	FILE *outf = _tfopen(pOutName, _T("wb"));

	if (outf)
	{
		for (int i = 0; i < nLen; ++i)
		{
			if (SECTOR_SIZEB == fread(buf.b, 1, SECTOR_SIZEB, f))
			{
				InverseSector(buf);

				if (SECTOR_SIZEB != fwrite(buf.b, 1, SECTOR_SIZEB, outf))
				{
					_tprintf(_T("\nОшибка записи в образ FDD %s\n"), pOutName);
					bRet = FALSE;
					break;
				}
			}
			else
			{
				_tprintf(_T("\nОшибка чтения из образа HDD %s\n"), g_options.strImgName.GetBuffer());
				bRet = FALSE;
				break;
			}
		}

		fclose(outf);
	}
	else
	{
		bRet = FALSE;
	}

	return bRet;
}

//запись раздела из отдельного образа в образ винчестера
BOOL ReadLogDiskImage(FILE *f, IMGFormat *imgf, CString &strName)
{
	BOOL bRet = TRUE;
	USector buf;
	FILE *inf = _tfopen(strName, _T("rb"));

	if (inf)
	{
		fseek(inf, 0, SEEK_END);
		long len = (ftell(inf) + 511) / 512; //размер файла в блоках
		fseek(inf, 0, SEEK_SET);
		//размер раздела не может быть меньше 1600 блоков. это просто нерационально.
		//!!!TODO: в Ксидос - может. Минимальный размер раздела надо как-то к геометрии привязывать.
		long partLen = (len < 1600) ? 1600 : len;
		_tprintf(_T("Размер файла в блоках: %d, размер раздела - %d\n"), len, partLen);

		for (int i = 0; i < partLen; ++i)
		{
			memset(buf.b, 0, SECTOR_SIZEB); //для начала очистим буфер. если размер файла будет не кратен блоку,
			//остаток сектора должен быть заполнен нулями.

			if (!feof(inf))
			{
				fread(buf.b, 1, SECTOR_SIZEB, inf); //читаем сколько сможем
			}

			if (ferror(inf))
			{
				_tprintf(_T("\nОшибка чтения из образа FDD %s\n"), strName.GetBuffer());
				bRet = FALSE;
				break;
			}
			else
			{
				InverseSector(buf);

				if (SECTOR_SIZEB != fwrite(buf.b, 1, SECTOR_SIZEB, f))
				{
					_tprintf(_T("\nОшибка записи в образ HDD %s\n"), g_options.strImgName.GetBuffer());
					bRet = FALSE;
					break;
				}
			}

			if (++imgf->nSector == imgf->S)
			{
				imgf->nSector = 0;

				if (++imgf->nHead == imgf->H)
				{
					imgf->nHead = 0;

					if (++imgf->nCyl > imgf->C)
					{
						//_tprintf(_T("Переполнение образа диска\n"));
						imgf->C = imgf->nCyl; //корректируем размер в сторону увеличения.
						bRet = FALSE;
					}
				}
			}
		}

		fclose(inf);
	}
	else
	{
		bRet = FALSE;
	}

	return bRet;
}

void MakeOutDir(BOOL bChangeOnly)
{
	TCHAR pDrive[_MAX_DRIVE];
	TCHAR pPath[_MAX_PATH];
	TCHAR pTitle[_MAX_PATH];
	TCHAR pExt[_MAX_EXT];

	if (!g_options.strPath.IsEmpty()) //если путь задали
	{
		//путь должен гарантированно кончаться слешем.
		g_options.strPath.TrimRight(_T('\\'));
		g_options.strPath += _T('\\');
		//разобьём путь на составляющие.
		_tsplitpath_s(g_options.strPath, pDrive, _MAX_DRIVE, pPath, _MAX_PATH, pTitle, _MAX_PATH, pExt, _MAX_EXT);
		CString strDrive = CString(pDrive);
		CString strPath = CString(pPath);

		if (!strDrive.IsEmpty()) //если задан полный путь
		{
			g_options.strPath = strDrive + strPath;
		}
		else //если задан относительный путь
		{
			//относительный путь должен гарантированно начинаться со слеша.
			if (strPath.GetAt(0) != _T('\\'))
			{
				strPath = _T('\\') + strPath;
			}

			g_options.strPath = g_options.strAppPath + strPath;
		}

		if (bChangeOnly)
		{
			if (_tchdir(g_options.strPath))
			{
				_tprintf(_T("Не удалось найти директорию %s\n"), g_options.strPath.GetBuffer());
			}
		}
		else
		{
			if (_tmkdir(g_options.strPath)) //создадим нужную директорию
			{
				_tprintf(_T("Не удалось создать директорию %s\n"), g_options.strPath.GetBuffer());
			}
			else
			{
				_tchdir(g_options.strPath);
			}
		}
	}
}

//запись пустых секторов от текущего места до конца дорожки.
//либо, если указано количество nWriteNum - заданное кол-во от текущего места
BOOL WriteEmptySectors(FILE *f, IMGFormat *imgf, int nWriteNum)
{
	BOOL bRet = TRUE;
	USector buf;
	memset(buf.b, 0377, SECTOR_SIZEB);
	int nStart, nStop;

	if (nWriteNum == 0)
	{
		nStart = imgf->nSector;
		nStop = imgf->S;
	}
	else
	{
		nStart = 0;
		nStop = nWriteNum;
	}

	for (int i = nStart; i < nStop; ++i)
	{
		if ((fwrite(buf.b, 1, SECTOR_SIZEB, f)) != SECTOR_SIZEB)
		{
			_tprintf(_T("Ошибка записи в файл %s\n"), g_options.strImgName.GetBuffer());
			return FALSE;
		}

		if (++imgf->nSector == imgf->S)
		{
			imgf->nSector = 0;

			if (++imgf->nHead == imgf->H)
			{
				imgf->nHead = 0;

				if (++imgf->nCyl > imgf->C)
				{
					//_tprintf(_T("Переполнение образа диска\n"));
					imgf->C = imgf->nCyl;
					bRet = FALSE;
				}
			}
		}
	}

	return bRet;
}

//если задано достаточно большое кол-во дорожек, и за концом разделов есть место
//то надо забить его нулями
void WriteEmptyTail(FILE *f, IMGFormat *imgf)
{
	USector buf;
	memset(buf.b, 0377, SECTOR_SIZEB);

	while (1)
	{
		if ((fwrite(buf.b, 1, SECTOR_SIZEB, f)) != SECTOR_SIZEB)
		{
			_tprintf(_T("Ошибка записи в файл %s\n"), g_options.strImgName.GetBuffer());
			break;
		}

		if (++imgf->nSector == imgf->S)
		{
			imgf->nSector = 0;

			if (++imgf->nHead == imgf->H)
			{
				imgf->nHead = 0;

				if (++imgf->nCyl > imgf->C)
				{
					break;
				}
			}
		}
	}
}


BOOL AssemblyAltPro(FILE *f, IMGFormat *imgf)
{
	BOOL bRet = FALSE;
	static const CString pstrExts[3] =
	{
		_T(".img"),
		_T(".bkd"),
		_T(".dsk")
	};
	struct _tfinddata_t c_file;
	intptr_t hFile;
	TCHAR pExt[_MAX_EXT];
	// в g_sector будем формировать таблицу разделов.
	memset(g_sector.b, 0, SECTOR_SIZEB);
	imgf->nCyl = 0;
	imgf->nHead = 0;
	imgf->nSector = 0;
	imgf->nLogDiskNum = 0;
	WORD boot = 2;
	WriteEmptySectors(f, imgf); //запишем пустую нулевую дорожку
	vector <CString> aFileList;

	if ((hFile = _tfindfirst(_T("*.*"), &c_file)) == -1L)
	{
		_tprintf(_T("В директории %s ничего не найдено!\n"), g_options.strPath.GetBuffer());
	}
	else
	{
		do
		{
			if (c_file.attrib & _A_SUBDIR)
			{
				continue; //директории игнорируем
			}
			else
			{
				_tsplitpath_s(c_file.name, NULL, 0, NULL, 0, NULL, 0, pExt, _MAX_EXT);

				for (int i = 0; i < 3; ++i)
				{
					if (pstrExts[i].CompareNoCase(pExt) == 0)
					{
						aFileList.push_back(CString(c_file.name));
						break;
					}
				}
			}
		}
		while (_tfindnext(hFile, &c_file) == 0);

		_findclose(hFile);
	}

	if (!aFileList.empty())
	{
		//отсортируем найденное в порядке возрастания
		std::sort(aFileList.begin(), aFileList.end());

		//затем по порядку всё это и обработаем
		for (size_t i = 0; i < aFileList.size(); ++i)
		{
			int beginLba = (imgf->nCyl * imgf->H + imgf->nHead) * imgf->S;
			g_sector.w[AHDD_PART_W - imgf->nLogDiskNum * 2] = (imgf->nHead & 0xf) | (imgf->nCyl << 4);
			_tprintf(_T("Раздел: %03d, файл: %s\n"), imgf->nLogDiskNum, aFileList[i].GetBuffer());
			bRet = ReadLogDiskImage(f, imgf, aFileList[i]);
			bRet = WriteEmptySectors(f, imgf);
			int endLba = (imgf->nCyl * imgf->H + imgf->nHead) * imgf->S;
			int len = endLba - beginLba;
			//размер высчитывается вместе с пустым пространством, выравнивающим разделы по началу дорожки
			//хотя никто не мешает задавать реальный размер в блоках, но в оригинале почему-то не так
			g_sector.w[AHDD_PART_W - 1 - imgf->nLogDiskNum * 2] = len;
			imgf->nLogDiskNum++;
		}

		aFileList.clear();
	}

	if (bRet)
	{
		WriteEmptyTail(f, imgf);
	}

	g_sector.w[AHDD_CYL_W] = imgf->C;
	g_sector.w[AHDD_HEAD_W] = imgf->H & 0xff; //нужен только младший байт
	g_sector.w[AHDD_SEC_W] = imgf->S;
	g_sector.w[AHDD_LOGD_W] = LOWORD(imgf->nLogDiskNum) | (boot << 8);
	WORD crc = 012701;

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

	g_sector.w[AHDD_PART_W - imgf->nLogDiskNum * 2] = crc;
	fseek(f, AHDD_PT_SEC * SECTOR_SIZEB + ((imgf->bHDI) ? SECTOR_SIZEB : 0), SEEK_SET);
	InverseSector(g_sector);

	if ((fwrite(g_sector.b, 1, SECTOR_SIZEB, f)) != SECTOR_SIZEB)
	{
		_tprintf(_T("Ошибка записи в файл %s \n"), g_options.strImgName.GetBuffer());
		bRet = FALSE;
	}

	fseek(f, ((imgf->bHDI) ? SECTOR_SIZEB : 0), SEEK_SET);
	memcpy(g_sector.b, g_MBRAltPro, SECTOR_SIZEB);
	InverseSector(g_sector);

	if ((fwrite(g_sector.b, 1, SECTOR_SIZEB, f)) != SECTOR_SIZEB)
	{
		_tprintf(_T("Ошибка записи в файл %s \n"), g_options.strImgName.GetBuffer());
		bRet = FALSE;
	}

	return bRet;
}

BOOL AssemblySamara(FILE *f, IMGFormat *imgf)
{
	BOOL bRet = FALSE;
	static const CString pstrExts[3] =
	{
		_T(".img"),
		_T(".bkd"),
		_T(".dsk")
	};
	USector buf;
	struct _tfinddata_t c_file;
	intptr_t hFile;
	TCHAR pExt[_MAX_EXT];
	// в g_sector будем формировать таблицу разделов.
	memset(g_sector.b, 0, SECTOR_SIZEB);
	imgf->nCyl = 0;
	imgf->nHead = 0;
	imgf->nSector = 0;
	imgf->nLogDiskNum = 0;
	WORD boot = 2;
	DWORD lba = 3;
	WriteEmptySectors(f, imgf, lba); //сперва запишем 3 пустых сектора.
	vector <CString> aFileList;

	if ((hFile = _tfindfirst(_T("*.*"), &c_file)) == -1L)
	{
		_tprintf(_T("В директории %s ничего не найдено!\n"), g_options.strPath.GetBuffer());
	}
	else
	{
		do
		{
			if (c_file.attrib & _A_SUBDIR)
			{
				continue; //директории игнорируем
			}
			else
			{
				_tsplitpath_s(c_file.name, NULL, 0, NULL, 0, NULL, 0, pExt, _MAX_EXT);

				for (int i = 0; i < 3; ++i)
				{
					if (pstrExts[i].CompareNoCase(pExt) == 0)
					{
						aFileList.push_back(CString(c_file.name));
						break;
					}
				}
			}
		}
		while (_tfindnext(hFile, &c_file) == 0);

		_findclose(hFile);
	}

	if (!aFileList.empty())
	{
		//отсортируем найденное в порядке возрастания
		std::sort(aFileList.begin(), aFileList.end());

		//затем по порядку всё это и обработаем
		for (size_t i = 0; i < aFileList.size(); ++i)
		{
			//сперва приходится высчитать будущий размер тут
			//а потом то же самое, в  функции ReadLogDiskImage - нерационально
			long nBlkLen = (c_file.size + 511) / 512; //размер файла в блоках
			//размер раздела не может быть меньше 1600 блоков. это просто нерационально.
			WORD len = (nBlkLen < 1600) ? 1600 : LOWORD(nBlkLen);
			//сперва надо создать начальный сектор
			memset(buf.b, 0, SECTOR_SIZEB);
			buf.w[SHDD_NLD_W] = imgf->nLogDiskNum + 2;      // номер ld
			buf.w[SHDD_LD_LEN_W] = len;   // длина ld
			buf.w[SHDD_ADR_BOOT_W] = 02000;   // адрес загрузки загрузчика :)
			buf.w[SHHD_ADR_PAR_W] = 01000;    // адрес блока параметров для загрузчика
			buf.w[SHDD_PAGE_W] = 014400;  // состояние регистра страниц
			g_sector.w[SHDD_PART_W + imgf->nLogDiskNum * 2] = HIWORD(lba);
			g_sector.w[SHDD_PART_W + 1 + imgf->nLogDiskNum * 2] = LOWORD(lba);
			InverseSector(buf);

			if ((fwrite(buf.b, 1, SECTOR_SIZEB, f)) != SECTOR_SIZEB)
			{
				_tprintf(_T("Ошибка записи в файл %s \n"), g_options.strImgName.GetBuffer());
				bRet = FALSE;
			}

			_tprintf(_T("Раздел: %03d, файл: %s\n"), imgf->nLogDiskNum, aFileList[i].GetBuffer());
			bRet = ReadLogDiskImage(f, imgf, aFileList[i]);
			lba += len;
			imgf->nLogDiskNum++;
		}

		aFileList.clear();
	}

	if (bRet)
	{
		WriteEmptyTail(f, imgf);
	}

	g_sector.w[SHDD_BOOT_W] = boot; // boot
	g_sector.w[SHDD_CYL_W] = imgf->C;
	g_sector.b[SHDD_HEAD_B] = LOBYTE(imgf->H);
	g_sector.b[SHDD_SEC_B] = LOBYTE(imgf->S);
	fseek(f, SHDD_PT_SEC * SECTOR_SIZEB + ((imgf->bHDI) ? SECTOR_SIZEB : 0), SEEK_SET);
	InverseSector(g_sector);

	if ((fwrite(g_sector.b, 1, SECTOR_SIZEB, f)) != SECTOR_SIZEB)
	{
		_tprintf(_T("Ошибка записи в файл %s \n"), g_options.strImgName.GetBuffer());
		bRet = FALSE;
	}

	return bRet;
}


/*
Структрура диска альтпро.
Блоки 0..6 (секторы 1..7) - резидентные модули и т.п.
Блок 7 (Сектор 8) - таблица разделов и разные полезные данные.
формат сектора 8:
имя      | смещение | размер | назначение
------------------------------------------------
HD$SPD   | 0        | 2      | признак быстрого чтения (125252)
         | 2        | 766    | область таблицы разделов (помещается 125. лог дисков и
         |          |        | 1 слово - контрольная сумма, вместе с геометрией)
HD$LOG   | 770      | 1      | всего лог.дисков
HD$UNI   | 771      | 1      | # устр. для загрузки по умолч. (0 - А, 2 - С ...)
HD$SEC   | 772      | 2      | секторов на дорожке
HD$HEA   | 774      | 1      | всего головок
HD$DRV   | 775      | 1      | код # привода,уст.внешним драйвером
HD4TRK   | 776      | 2      | всего дорожек

таблица разделов как стек растёт вверх.
Записи о логических дисках идут по порядку, начиная с логического диска с номером 0.
начинается с 766, заканчивается 2
Формат записи следующий (отсчёт естественно в направлении от HD$LOG-2 к HD$SPD):
1 слово - номер головки (младшие 4 бита) и номер дорожки (остальные 12 бит)
          ограничение: макс. 4096 из 65536 возможных.
  если это слово < 0 (т.е. на дорожки отводится уже 11 бит.)
          ограничение стало: макс. 2048 из 65536 возможных.
  то раздел считается защищённым от записи.
  разделы выравниваются по началу цилиндра, т.е. начало всегда сектор 1.

2 слово - объём логического диска в блоках, т.е. 65536 = макс. 32Мб.
(предполагается, что каждый лог. диск начинается с первого сектора дорожки, определённой первым словом).

--------------------------------------------------------------------------
Структура диска самара.
первые три блока - МБР
Блок 0 (сектор 1) - ?
Блок 1 (сектор 2)
смещение | размер | назначение
------------------------------------------------
0        | 2      | # устр. для загрузки по умолч. (0 - А, 2 - С ...)
2        | 2      | количество дорожек
4        | 1      | количество головок
5        | 1      | количество секторов
6        | x      | таблица разделов, максимум 64 раздела

формат элемента таблицы разделов:
смещение | размер | назначение
------------------------------------------------
0        | 2      | номер логического блока начала раздела, ст. слово
2        | 2      | номер логического блока начала раздела, мл. слово

Блок 2 (сектор 3) - ?

каждый логический диск начинается с начального блока
формат:
смещение | размер | назначение
------------------------------------------------
0        | 2      | номер лог. диска (0 - А, 2 - С ...)
2        | 2      | размер лог. диска в блоках
4        | 2      | адрес загрузки загрузчика лог. диска
6        | 2      | адрес блока параметров для загрузчика
10       | 2      | состояние регистра страниц

*/

