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

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

using namespace std;
#include "BKHDDTool.h"
#include "HDIStuff.h"

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

USector g_sector;
ProrgamOptions g_options;


int wmain(int argc, wchar_t *argv[], wchar_t *envp[])
{
	srand((unsigned)time(nullptr));
	_wsetlocale(LC_ALL, L"Russian");
	int nRetCode = 0;
	FILE *pHDDfile = nullptr;
	wchar_t curDir[_MAX_PATH];
	_wgetcwd(curDir, _MAX_PATH);// GetCurrentDirectory(_MAX_PATH, curDir);
	g_options.strAppPath = wstring(curDir);
	static struct option long_options[] =
	{
		{ L"help",        ARG_NONE, 0, L'?' },
		{ L"altpro",      ARG_NONE, 0, L'a' },
		{ L"samara",      ARG_NONE, 0, L'm' },
		{ L"hdi",         ARG_NONE, 0, L'i' },
		{ L"path",        ARG_REQ,  0, L'p' },
		{ L"cylinder",    ARG_REQ,  0, L'c' },
		{ L"head",        ARG_REQ,  0, L'h' },
		{ L"sector",      ARG_REQ,  0, L's' },
		{ ARG_NULL , ARG_NULL , ARG_NULL , ARG_NULL }
	};
	static wchar_t optstring[] = L"?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 L'?':
				bShowHelp = true;
				break;

			case L'a':
				g_options.bAltPro = true;
				g_options.bSamara = false;
				break;

			case L'm':
				g_options.bAltPro = false;
				g_options.bSamara = true;
				break;

			case L'i':
				g_options.bHDI = true;
				break;

			case L'p':
				g_options.strPath = wstring(optarg);
				break;

			case L'c':
				g_options.C = _tcstol(optarg, 0, 10) & 0xffff;
				g_options.bCreate = true;
				break;

			case L'h':
				g_options.H = _tcstol(optarg, 0, 10) & 0xffff;
				g_options.bCreate = true;
				break;

			case L's':
				g_options.S = _tcstol(optarg, 0, 10) & 0xffff;
				g_options.bCreate = true;
				break;
		}

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

	argc -= optind;
	argv += optind;

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

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

			//создаём образ
			pHDDfile = _wfopen(g_options.strImgName.c_str(), L"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))
					{
						wprintf(L"Не удалось записать HDI сектор в файл %s\n", g_options.strImgName.c_str());
					}
				}

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

			if (pHDDfile)
			{
				AnalyseImage(pHDDfile);
				fclose(pHDDfile);
			}
			else
			{
				wprintf(L"Не удалось открыть файл %s\n", g_options.strImgName.c_str());
				nRetCode = 1;
			}
		}
	}

	return nRetCode;
}


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

bool AssemblyImage(FILE *f, IMGFormat *imgf)
{
	bool bRet = false;
	wprintf(L"Тип образа: %s\n", (imgf->bHDI) ? L"HDI" : L"IMG");
	wprintf(L"Формат образа: %s\n", (imgf->bSamara) ? L"Самара" : L"АльтПро");
	wprintf(L"Геометрия\nКоличество цилиндров: %d\n", imgf->C);
	wprintf(L"Количество головок:   %d\n", imgf->H);
	wprintf(L"Количество секторов:  %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)  //прочтём первый сектор.
	{
		//подсчитаем контрольную сумму
		uint8_t 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;
		wprintf(L"Тип образа: %s\n", (format.bHDI) ? L"HDI" : L"IMG");

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

			if (format.bSamara)
			{
				bRet = SplitSamara(f, &format);
			}
			else
			{
				bRet = SplitAltPro(f, &format);
			}
		}
		else
		{
			wprintf(L"Ошибка чтения или формат нераспознан!\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)
	{
		wprintf(L"Формат образа опознан как 'Самара', но в опциях задано обрабатывать как 'АльтПро'\n");
		wprintf(L"Образ будет обработан как 'Самара'\n");
	}
	else if (bAltPro && g_options.bSamara)
	{
		wprintf(L"Формат образа опознан как 'АльтПро', но в опциях задано обрабатывать как 'Самара'\n");
		wprintf(L"Образ будет обработан как 'АльтПро'\n");
	}

	if (!bRet) //образ никак неопознан.
	{
		if (g_options.bAltPro)
		{
			wprintf(L"Формат образа не опознан, но в опциях задано обрабатывать как 'АльтПро'\n");
			wprintf(L"Образ будет обработан как 'АльтПро'\n");
			g_options.bSamara = false;
			*imgf = aimgf;
			bRet = true;
		}
		else if (g_options.bSamara)
		{
			wprintf(L"Формат образа не опознан, но в опциях задано обрабатывать как 'Самара'\n");
			wprintf(L"Образ будет обработан как 'Самара'\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;
		uint16_t 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
	{
		wprintf(L"Ошибка чтения таблицы разделов формата 'АльтПро'\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;
		uint32_t 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;
			uint16_t nld = 2;

			for (int i = 0; i < nPartitions; ++i, nld++)
			{
				uint32_t 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
	{
		wprintf(L"Ошибка чтения таблицы разделов формата 'Самара'\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]; //размер логического диска в блоках
			wprintf(L"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)))
				{
					wprintf(L"\nОшибка извлечения раздела %d\n", nld);
					nErrors++;
				}
			}
			else
			{
				wprintf(L"\nОшибка перемещения к разделу %d\n", nld);
				nErrors++;
			}
		}

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

		if (nErrors)
		{
			wprintf(L"С ошибками - %d разделов\n", nErrors);
		}
	}
	else
	{
		wprintf(L"Ошибка чтения таблицы разделов формата 'АльтПро'\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];
					wprintf(L"part #%d:: lba = %ld len = %d\r", nld, lba, nLen);

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

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

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

	return bRet;
}

//запись раздела из образа винчестера в отдельный образ
bool WriteLogDiskImage(FILE *f, int nld, int nLen)
{
	bool bRet = true;
	USector buf;
	wchar_t pOutName[64] = {0};
	//сформируем очередное имя
	_swprintf(pOutName, L"part_%03d.img", nld);
	FILE *outf = _wfopen(pOutName, L"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))
				{
					wprintf(L"\nОшибка записи в образ FDD %s\n", pOutName);
					bRet = false;
					break;
				}
			}
			else
			{
				wprintf(L"\nОшибка чтения из образа HDD %s\n", g_options.strImgName.c_str());
				bRet = false;
				break;
			}
		}

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

	return bRet;
}

//запись раздела из отдельного образа в образ винчестера
bool ReadLogDiskImage(FILE *f, IMGFormat *imgf, wstring &strName)
{
	bool bRet = true;
	USector buf;
	FILE *inf = _wfopen(strName.c_str(), L"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;
		wprintf(L"Размер файла в блоках: %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))
			{
				wprintf(L"\nОшибка чтения из образа FDD %s\n", strName.c_str());
				bRet = false;
				break;
			}
			else
			{
				InverseSector(buf);

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

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

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

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

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

	return bRet;
}

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

	if (!g_options.strPath.empty()) //если путь задали
	{
		//путь должен гарантированно кончаться слешем.
		wchar_t wc;

		//сперва удалим все возможные слеши
		while ((wc = g_options.strPath.back()) == L'\\')
		{
			g_options.strPath.pop_back();
		}

		//затем добавим один необходимый
		g_options.strPath += L'\\';
		//разобьём путь на составляющие.
		_wsplitpath_s(g_options.strPath.c_str(), pDrive, _MAX_DRIVE, pPath, _MAX_PATH, pTitle, _MAX_PATH, pExt, _MAX_EXT);
		wstring strDrive = wstring(pDrive);
		wstring strPath = wstring(pPath);

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

			g_options.strPath = g_options.strAppPath + strPath;
		}

		if (bChangeOnly)
		{
			if (_wchdir(g_options.strPath.c_str()))
			{
				wprintf(L"Не удалось найти директорию %s\n", g_options.strPath.c_str());
			}
		}
		else
		{
			if (_wmkdir(g_options.strPath.c_str())) //создадим нужную директорию
			{
				wprintf(L"Не удалось создать директорию %s\n", g_options.strPath.c_str());
			}
			else
			{
				_wchdir(g_options.strPath.c_str());
			}
		}
	}
}

//запись пустых секторов от текущего места до конца дорожки.
//либо, если указано количество 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)
		{
			wprintf(L"Ошибка записи в файл %s\n", g_options.strImgName.c_str());
			return false;
		}

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

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

				if (++imgf->nCyl > imgf->C)
				{
					//wprintf(L"Переполнение образа диска\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)
		{
			wprintf(L"Ошибка записи в файл %s\n", g_options.strImgName.c_str());
			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 wstring pstrExts[3] =
	{
		L".img",
		L".bkd",
		L".dsk"
	};
	struct _wfinddata_t c_file;
	intptr_t hFile;
	wchar_t pExt[_MAX_EXT];
	// в g_sector будем формировать таблицу разделов.
	memset(g_sector.b, 0, SECTOR_SIZEB);
	imgf->nCyl = 0;
	imgf->nHead = 0;
	imgf->nSector = 0;
	imgf->nLogDiskNum = 0;
	uint16_t boot = 2;
	WriteEmptySectors(f, imgf); //запишем пустую нулевую дорожку
	vector <wstring> aFileList;

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

				for (int i = 0; i < 3; ++i)
				{
					if (wcscoll(pstrExts[i].c_str(), pExt) == 0)
					{
						aFileList.push_back(wstring(c_file.name));
						break;
					}
				}
			}
		}
		while (_wfindnext(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);
			wprintf(L"Раздел: %03d, файл: %s\n", imgf->nLogDiskNum, aFileList[i].c_str());
			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] = (imgf->nLogDiskNum & 0xffff) | (boot << 8);
	uint16_t 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)
	{
		wprintf(L"Ошибка записи в файл %s \n", g_options.strImgName.c_str());
		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)
	{
		wprintf(L"Ошибка записи в файл %s \n", g_options.strImgName.c_str());
		bRet = false;
	}

	return bRet;
}

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

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

				for (int i = 0; i < 3; ++i)
				{
					if (wcscoll(pstrExts[i].c_str(), pExt) == 0)
					{
						aFileList.push_back(wstring(c_file.name));
						break;
					}
				}
			}
		}
		while (_wfindnext(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 блоков. это просто нерационально.
			uint16_t len = (nBlkLen < 1600) ? 1600 : nBlkLen & 0xffff;
			//сперва надо создать начальный сектор
			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] = (lba >> 16) & 0xffff;
			g_sector.w[SHDD_PART_W + 1 + imgf->nLogDiskNum * 2] = lba & 0xffff;
			InverseSector(buf);

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

			wprintf(L"Раздел: %03d, файл: %s\n", imgf->nLogDiskNum, aFileList[i].c_str());
			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] = (imgf->H & 0xff);
	g_sector.b[SHDD_SEC_B] = (imgf->S & 0xff);
	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)
	{
		wprintf(L"Ошибка записи в файл %s \n", g_options.strImgName.c_str());
		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      | состояние регистра страниц

*/

