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

#include "stdafx.h"
#include "HDDImgMaker.h"
#include "HDDImgMakerDlg.h"
#include "afxdialogex.h"


#ifdef _DEBUG
#define new DEBUG_NEW
#endif

static SYS_SECTOR g_sys_sector =
{
	0x045a, //0
	0,      //1
	0xc837, //2
	0,      //3
	0,      //4
	0,      //5
	0,      //6
	{0, 0, 0}, //7,8,9
	{0},    //10
	1,      //20
	1,      //21
	4,      //22
	{0},    //23
	{0},    //27
	0x8001, //47
	0,      //48
	0x200,  //49
	0x4000, //50
	{0},    //51
	0,      //57,58
	0,      //59
	0,      //60,61
	{0}     //62-255
};

static TCHAR pDrive[_MAX_DRIVE] = { 0 };
static TCHAR pPath[_MAX_PATH] = { 0 };
static TCHAR pTitle[_MAX_PATH] = { 0 };
static TCHAR pExt[_MAX_EXT] = { 0 };


// Диалоговое окно CAboutDlg используется для описания сведений о приложении

class CAboutDlg : public CDialogEx
{
	public:
		CAboutDlg();

// Данные диалогового окна
		enum { IDD = IDD_ABOUTBOX };

	protected:
		virtual void DoDataExchange(CDataExchange *pDX) override;    // поддержка DDX/DDV

// Реализация
	protected:
		DECLARE_MESSAGE_MAP()
	public:
		virtual BOOL OnInitDialog() override;
};

CAboutDlg::CAboutDlg() : CDialogEx(CAboutDlg::IDD)
{
}

void CAboutDlg::DoDataExchange(CDataExchange *pDX)
{
	CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()


BOOL CAboutDlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();
	DWORD dwHandle;
	CString name = CString(theApp.m_pszAppName) + _T(".exe");
	DWORD dwSize = GetFileVersionInfoSize(name.GetBuffer(), &dwHandle);

	if (dwSize)
	{
		auto *lpData = new TCHAR[ dwSize ];

		if (lpData)
		{
			GetFileVersionInfo(name.GetBuffer(), dwHandle, dwSize, lpData);
			UINT uBufLen;
			VS_FIXEDFILEINFO *lpfi;
			VerQueryValue(lpData, _T("\\"), (void **)&lpfi, &uBufLen);
			CString s;
			s.Format(_T("BK Virtual HDD Maker v%d.%d.%d.%d "),
			         (lpfi->dwFileVersionMS >> 16), lpfi->dwFileVersionMS & 0xFFFF,
			         (lpfi->dwFileVersionLS >> 16), lpfi->dwFileVersionLS & 0xFFFF);
#ifdef _WIN64
			s += _T("x64");
#else
			s += _T("x86");
#endif
			GetDlgItem(IDC_STATIC2)->SetWindowText(s);
			delete[] lpData;
		}
		else
		{
			GetDlgItem(IDC_STATIC2)->SetWindowText(_T("BK Virtual HDD Maker v???"));
		}
	}

	return TRUE;  // return TRUE unless you set the focus to a control
	// Исключение: страница свойств OCX должна возвращать значение FALSE
}

// диалоговое окно CHDDImgMakerDlg



CHDDImgMakerDlg::CHDDImgMakerDlg(CWnd *pParent /*=nullptr*/)
	: CDialogEx(CHDDImgMakerDlg::IDD, pParent)
	, m_strImgName(_T(""))
	, m_strExistingImgName(_T(""))
	, m_strModelName(_T("BKEMU HARD DRIVE IMAGE"))
	, m_strSerialNumber(_T(""))
	, m_nCylinders(1)
	, m_nHeads(1)
	, m_nSectors(1)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CHDDImgMakerDlg::DoDataExchange(CDataExchange *pDX)
{
	CDialogEx::DoDataExchange(pDX);
	DDX_Text(pDX, IDC_EDIT_IMGNAME, m_strImgName);
	DDX_Text(pDX, IDC_EDIT_EXISTING_IMGNAME, m_strExistingImgName);
	DDX_Text(pDX, IDC_EDIT_MODELNAME, m_strModelName);
	DDV_MaxChars(pDX, m_strModelName, 40);
	DDX_Text(pDX, IDC_EDIT_SERNUMBER, m_strSerialNumber);
	DDV_MaxChars(pDX, m_strSerialNumber, 20);
	DDX_Text(pDX, IDC_EDIT_CYLINDERS, m_nCylinders);
	DDV_MinMaxInt(pDX, m_nCylinders, 1, 65536);
	DDX_Text(pDX, IDC_EDIT_HEADS, m_nHeads);
	DDV_MinMaxInt(pDX, m_nHeads, 1, 16);
	DDX_Text(pDX, IDC_EDIT_SECTORS, m_nSectors);
	DDV_MinMaxInt(pDX, m_nSectors, 1, 256);
	DDX_Control(pDX, IDC_SPIN_CYLINDERS, m_spinCylinders);
	DDX_Control(pDX, IDC_SPIN_HEADS, m_spinHeads);
	DDX_Control(pDX, IDC_SPIN_SECTORS, m_spinSectors);
	DDX_Control(pDX, IDC_PROGRESS_CREATE, m_progress);
	DDX_Control(pDX, IDC_COMBO_HDITYPE, m_TypeCombo);
}

BEGIN_MESSAGE_MAP(CHDDImgMakerDlg, CDialogEx)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_EN_CHANGE(IDC_EDIT_CYLINDERS, &CHDDImgMakerDlg::OnEnChangeEditCylinders)
	ON_EN_CHANGE(IDC_EDIT_HEADS, &CHDDImgMakerDlg::OnEnChangeEditHeads)
	ON_EN_CHANGE(IDC_EDIT_SECTORS, &CHDDImgMakerDlg::OnEnChangeEditSectors)
	ON_BN_CLICKED(IDC_BUTTON_SERNUMBER, &CHDDImgMakerDlg::OnBnClickedButtonSernumber)
	ON_BN_CLICKED(IDC_BUTTON_BROWSE, &CHDDImgMakerDlg::OnBnClickedButtonBrowse)
	ON_BN_CLICKED(IDC_BUTTON_BROWSE_EXISTING, &CHDDImgMakerDlg::OnBnClickedButtonBrowseExisting)
	ON_BN_CLICKED(IDC_BUTTON_MAKE, &CHDDImgMakerDlg::OnBnClickedButtonMake)
	ON_BN_CLICKED(IDC_BUTTON_HDI2IMG, &CHDDImgMakerDlg::OnBnClickedButtonHdi2img)
	ON_EN_KILLFOCUS(IDC_EDIT_IMGNAME, &CHDDImgMakerDlg::OnEnKillfocusEditImgname)
	ON_EN_KILLFOCUS(IDC_EDIT_EXISTING_IMGNAME, &CHDDImgMakerDlg::OnEnKillfocusEditExistingImgname)
	ON_EN_KILLFOCUS(IDC_EDIT_SECTORS, &CHDDImgMakerDlg::OnEnKillfocusEditSectors)
	ON_EN_SETFOCUS(IDC_EDIT_SECTORS, &CHDDImgMakerDlg::OnEnSetfocusEditSectors)
	ON_EN_KILLFOCUS(IDC_EDIT_HEADS, &CHDDImgMakerDlg::OnEnKillfocusEditHeads)
	ON_EN_SETFOCUS(IDC_EDIT_HEADS, &CHDDImgMakerDlg::OnEnSetfocusEditHeads)
	ON_EN_KILLFOCUS(IDC_EDIT_CYLINDERS, &CHDDImgMakerDlg::OnEnKillfocusEditCylinders)
	ON_EN_SETFOCUS(IDC_EDIT_CYLINDERS, &CHDDImgMakerDlg::OnEnSetfocusEditCylinders)
END_MESSAGE_MAP()




BOOL CHDDImgMakerDlg::PreTranslateMessage(MSG *pMsg)
{
	if (pMsg->message >= WM_MOUSEFIRST && pMsg->message <= WM_MOUSELAST)
	{
		m_tt.RelayEvent(pMsg);
	}

	return CDialogEx::PreTranslateMessage(pMsg);
}

static const TOOLTIP_STRUCT tt_strc[] =
{
	{ IDC_EDIT_IMGNAME,             _T("Здесь задаётся имя нового, создаваемого образа. Задать хоть что-нибудь обязательно.") },
	{ IDC_BUTTON_BROWSE,            _T("Кнопка вызова файлового диалога, чтобы можно было не вручную задать местоположение нового образа.") },
	{ IDC_EDIT_EXISTING_IMGNAME,    _T("Здесь можно задать образ, который будет сконвертирован в БКшный образ, имя которого задаётся выше.") },
	{ IDC_BUTTON_BROWSE_EXISTING,   _T("Кнопка вызова файлового диалога, для выбора существующего образа для конвертации.") },
	{ IDC_EDIT_CYLINDERS,           _T("При создании нового образа тут задаётся геометрия - количество цилиндров. При конвертации поле игнорируется.") },
	{ IDC_SPIN_CYLINDERS,           _T("Тут можно задавать не вручную.") },
	{ IDC_EDIT_HEADS,               _T("При создании нового образа тут задаётся геометрия - количество головок. При конвертации поле игнорируется.") },
	{ IDC_SPIN_HEADS,               _T("Тут можно задавать не вручную.") },
	{ IDC_EDIT_SECTORS,             _T("При создании нового образа тут задаётся геометрия - количество секторов на дорожке. При конвертации поле игнорируется.") },
	{ IDC_SPIN_SECTORS,             _T("Тут можно задавать не вручную.") },
	{ IDC_EDIT_MODELNAME,           _T("Здесь можно задать модель геренируемого виртуального HDD.") },
	{ IDC_EDIT_SERNUMBER,           _T("Здесь можно задать серийный номер геренируемого виртуального HDD.") },
	{ IDC_BUTTON_SERNUMBER,         _T("Сгенерировать случайный серийный номер геренируемого виртуального HDD.") },
	{ IDC_BUTTON_HDI2IMG,           _T("Обратное преобразование БКшного образа HDD в PCшный посекторный образ.") },
	{ IDC_COMBO_HDITYPE,            _T("Выбрать тип генерируемого образа HDD. (Функционал пока нереализован в эмуляторе).") },
	{ IDC_BUTTON_MAKE,              _T("Создать новый образ или сконвертировать из существующего.") },
	{ IDOK,                         _T("Выйти из программы.") }
};

// обработчики сообщений CHDDImgMakerDlg


BOOL CHDDImgMakerDlg::OnInitDialog()
{
	srand((unsigned)time(nullptr));
	CDialogEx::OnInitDialog();
	// Добавление пункта "О программе..." в системное меню.
	// IDM_ABOUTBOX должен быть в пределах системной команды.
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);
	CMenu *pSysMenu = GetSystemMenu(FALSE);

	if (pSysMenu != nullptr)
	{
		CString strAboutMenu;
		BOOL bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
		ASSERT(bNameValid);

		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	// Задаёт значок для этого диалогового окна. Среда делает это автоматически,
	//  если главное окно приложения не является диалоговым
	SetIcon(m_hIcon, TRUE);         // Крупный значок
	SetIcon(m_hIcon, FALSE);        // Мелкий значок
	TCHAR pModuleName[_MAX_PATH] = { 0 };

	if (::GetModuleFileName(AfxGetInstanceHandle(), pModuleName, _MAX_PATH))
	{
		_tsplitpath_s(pModuleName, pDrive, _MAX_DRIVE, pPath, _MAX_PATH, nullptr, 0, nullptr, 0);
		m_strAppPath = CString(pDrive) + CString(pPath);
	}
	else
	{
		m_strAppPath = _T("");
	}

	//создаём объект тултипов
	m_tt.Create(this);
	m_tt.SetMaxTipWidth(256);

	for (auto i : tt_strc)
	{
		m_tt.AddTool(GetDlgItem(i.nID), i.pszStr);
	}

	m_TypeCombo.InsertString(0, _T("hdix"));
	m_TypeCombo.InsertString(1, _T("hdi"));
	m_TypeCombo.SetCurSel(1);
	m_strExistingImgPath = m_strAppPath;
	m_bCancel = false;
	m_bConvert = false;
	m_progress.SetPos(0);
	m_progress.SetStep(1);
	m_progress.SetState(PBST_NORMAL);
	m_spinCylinders.SetRange32(1, 65536);
	m_spinCylinders.SetPos32(m_nCylinders);
	m_spinCylinders.SetBuddy(GetDlgItem(IDC_EDIT_CYLINDERS));
	m_spinHeads.SetRange32(1, 16);
	m_spinHeads.SetPos32(m_nHeads);
	m_spinHeads.SetBuddy(GetDlgItem(IDC_EDIT_HEADS));
	m_spinSectors.SetRange32(1, 256);
	m_spinSectors.SetPos32(m_nSectors);
	m_spinSectors.SetBuddy(GetDlgItem(IDC_EDIT_SECTORS));
	OutByteSize();
	OnBnClickedButtonSernumber();
	ChangeMakeButtonText();
	return TRUE;  // возврат значения TRUE, если фокус не передан элементу управления
}

void CHDDImgMakerDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		CAboutDlg dlgAbout;
		dlgAbout.DoModal();
	}
	else
	{
		CDialogEx::OnSysCommand(nID, lParam);
	}
}

// При добавлении кнопки свёртывания в диалоговое окно нужно воспользоваться приведённым ниже кодом,
//  чтобы нарисовать значок.  Для приложений MFC, использующих модель документов или представлений,
//  это автоматически выполняется рабочей областью.

void CHDDImgMakerDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); // контекст устройства для рисования
		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
		// Выравнивание значка по центру клиентского прямоугольника
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;
		// Нарисуйте значок
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialogEx::OnPaint();
	}
}

// Система вызывает эту функцию для получения отображения курсора при перемещении
//  свёрнутого окна.
HCURSOR CHDDImgMakerDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}

void CHDDImgMakerDlg::OutByteSize()
{
	UpdateData(TRUE);
	__int64 nsize = __int64(m_nCylinders) * __int64(m_nHeads) * __int64(m_nSectors) * 512;
	CString t;
	t.Format(_T("%I64d байт"), nsize);
	SetDlgItemText(IDC_STATIC_RESULT_SIZE, t);
}


void CHDDImgMakerDlg::OnEnChangeEditCylinders()
{
	OutByteSize();
}

void CHDDImgMakerDlg::OnEnChangeEditHeads()
{
	OutByteSize();
}

void CHDDImgMakerDlg::OnEnChangeEditSectors()
{
	OutByteSize();
}

void CHDDImgMakerDlg::OnBnClickedButtonSernumber()
{
	// тут будет генерация серийного номера Серийный номер такого типа
	// BK-AAAADDDDDDDD, где A - буква, D - цифра.
	m_strSerialNumber = _T("BK-AAAADDDDDDDD");

	for (int i = 3; i < 7; ++i)
	{
		TCHAR ch = _T('A') + TCHAR((double)rand() / (RAND_MAX + 1) * (_T('Z') - _T('A')));
		m_strSerialNumber.SetAt(i, ch);
	}

	for (int i = 7; i < 15; ++i)
	{
		TCHAR ch = _T('0') + TCHAR((double)rand() / (RAND_MAX + 1) * (_T('9') - _T('0')));
		m_strSerialNumber.SetAt(i, ch);
	}

	SetDlgItemText(IDC_EDIT_SERNUMBER, m_strSerialNumber);
}

void CHDDImgMakerDlg::OnBnClickedButtonBrowse()
{
	CString strFilterImg = _T("Виртуальный жёсткий диск БК (*.hdi)|*.hdi|Все файлы (*.*)|*.*||");
	CFileDialog dlg(TRUE, _T("hdi"), nullptr,
	                OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_NOCHANGEDIR | OFN_EXPLORER,
	                strFilterImg, this);

	if (dlg.DoModal() == IDOK)
	{
		m_strImgName = dlg.GetPathName();
		SetDlgItemText(IDC_EDIT_IMGNAME, m_strImgName);
		m_bCancel = false;
	}
	else
	{
		m_bCancel = true;
	}
}

void CHDDImgMakerDlg::OnBnClickedButtonBrowseExisting()
{
	CString strFilterImg = _T("Образ жёсткого диска БК (*.img)|*.img|Все файлы (*.*)|*.*||");
	CFileDialog dlg(TRUE, _T("img"), nullptr,
	                OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_NOCHANGEDIR | OFN_EXPLORER,
	                strFilterImg, this);

	if (dlg.DoModal() == IDOK)
	{
		m_strExistingImgName = dlg.GetPathName();
		m_strExistingImgName.Trim();
		SetDlgItemText(IDC_EDIT_EXISTING_IMGNAME, m_strExistingImgName);
		OnEnKillfocusEditExistingImgname();
	}
}

/*
Обратное преобразование hdi в img
*/
void CHDDImgMakerDlg::OnBnClickedButtonHdi2img()
{
	BYTE pSector[SECTOR_SIZE] = { 0 };
	SYS_SECTOR sector0;
	CString strFilterImg = _T("Виртуальный жёсткий диск БК (*.hdi)|*.hdi|Все файлы (*.*)|*.*||");
	CFileDialog dlg(TRUE, _T("hdi"), nullptr,
	                OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_NOCHANGEDIR | OFN_EXPLORER | OFN_FILEMUSTEXIST,
	                strFilterImg, this);

	if (dlg.DoModal() == IDOK)
	{
		bool bStatus = false;
		CString strHDIName = dlg.GetPathName();
		strHDIName.Trim();
		_tsplitpath_s(strHDIName, pDrive, _MAX_DRIVE, pPath, _MAX_PATH, pTitle, _MAX_PATH, pExt, _MAX_EXT);
		CString strImgName = CString(pDrive) + CString(pPath) + CString(pTitle) + _T(".img");

		if (m_file.Open(strImgName, CFile::modeCreate | CFile::modeWrite))
		{
			if (m_fileExisting.Open(strHDIName, CFile::modeRead | CFile::shareDenyWrite))
			{
				m_fileExisting.Read(&sector0, sizeof(SYS_SECTOR)); //сперва прочитаем заголовок.
				//проверим контрольную сумму, вдруг нам фуфло какое-то подсовывают
				BYTE cs = 0;
				auto *pSector0 = reinterpret_cast<BYTE *>(&sector0);

				for (int i = 0; i < 511; ++i)
				{
					cs += pSector0[i];
				}

				cs = -cs;

				if (pSector0[511] == cs && pSector0[510] == 0xA5)
				{
					bool bContinue = false; //флаг продолжения. если образ короче, чем задано в геометрии
					int nTotalSectors = sector0.cylinders * sector0.heads * sector0.sectors; //произведение максимальных значений влазит в 32 бита
					m_progress.SetPos(0);
					m_progress.SetRange32(0, nTotalSectors);

					for (int i = 0; i < nTotalSectors; ++i)
					{
						if (!bContinue)
						{
							int r = m_fileExisting.Read(pSector, SECTOR_SIZE);

							if (r != SECTOR_SIZE)
							{
								int answer = AfxMessageBox(_T("Неожиданный конец файла hdi.\r\nОтмена - остановить преобразование и оставить образ как есть.\r\nОстальные кнопки - продолжить и дополнить образ нулями до полного размера."), MB_CANCELTRYCONTINUE | MB_ICONSTOP);

								if (answer == IDCANCEL)
								{
									break;
								}

								bContinue = true; //выбираем дополнение образа нулями
								ZeroMemory(pSector, SECTOR_SIZE);
							}
						}

						//для упрощения анализа нужно инвертировать БКшный образ.
						//для реального образа - не нужно.
						//InverseBuf(*(reinterpret_cast<USector*>(pSector)));
						m_file.Write(pSector, SECTOR_SIZE);
						m_progress.SetPos(i);
					}

					m_progress.SetPos(nTotalSectors);
					bStatus = true;
				}
				else
				{
					AfxMessageBox(_T("Виртуальный жёсткий диск БК вероятно повреждён или имеет неизвестный формат."), MB_OK | MB_ICONEXCLAMATION);
				}

				m_fileExisting.Close();
			}

			m_file.Close();

			if (bStatus)
			{
				AfxMessageBox(_T("Образ успешно преобразован."), MB_OK | MB_ICONINFORMATION);
			}
		}
		else
		{
			AfxMessageBox(_T("Не удалось создать образ ") + strImgName, MB_OK | MB_ICONEXCLAMATION);
		}
	}

	m_progress.SetPos(0);
}

void CHDDImgMakerDlg::OnBnClickedButtonMake()
{
	//расширения разных типов виртуальных HDD
	static const CString strVHDDext[2] =
	{
		_T(".hdix"),
		_T(".hdi")
	};
	int nTypeHDI = m_TypeCombo.GetCurSel(); //и далее в зависимости от выбранного формата формировать образ.
	m_strImgName.Trim();

	// если мы конвертируем, и имя не задано, то его можно создать из существующего
	if (m_bConvert && m_strImgName.IsEmpty())
	{
		_tsplitpath_s(m_strExistingImgName, pDrive, _MAX_DRIVE, pPath, _MAX_PATH, pTitle, _MAX_PATH, pExt, _MAX_EXT);
		m_strImgName = CString(pDrive) + CString(pPath) + CString(pTitle);
	}

	//если имя файла не задано, принудительно заставим выбрать.
	while (m_strImgName.IsEmpty())
	{
wrongName:
		OnBnClickedButtonBrowse();

		//если передумали, то просто выход.
		if (m_bCancel)
		{
			m_bCancel = false;
			return;
		}
	}

	//нужна проверка на валидность имени. т.к. там может быть и путь, а может и не быть.
	//если пути нет, надо добавить путь по умолчанию.
	_tsplitpath_s(m_strImgName, pDrive, _MAX_DRIVE, pPath, _MAX_PATH, pTitle, _MAX_PATH, pExt, _MAX_EXT);
	CString strDrive = CString(pDrive);
	CString strPath = CString(pPath);
	CString strTitle = CString(pTitle);
	CString strExt = CString(pExt);

	if (strExt.IsEmpty())
	{
		strExt = strVHDDext[nTypeHDI];
	}
	else if (strExt != strVHDDext[nTypeHDI])
	{
		strExt += strVHDDext[nTypeHDI];
	}

	CString strImgPath = strDrive + strPath;
	CString strImgName = (strTitle.IsEmpty()) ? _T("") : (strTitle + strExt);

	//имя файла пусто, даже расширения нет
	if (strImgName.IsEmpty())
	{
		goto wrongName; //заставим выбрать или вписать хоть что-то
	}

	if (strImgPath.IsEmpty()) //если пути вообще нет
	{
		m_strImgName = m_strExistingImgPath + strImgName; //значит создадим файл рядом с существующим образом, а если его нету - то рядом с прогой
	}
	else if (strDrive.IsEmpty() && !strPath.IsEmpty())  //если буквы диска нету, но есть путь
	{
		m_strImgName = m_strExistingImgPath + strPath + strImgName; //значит человек хочет, чтобы файл создавался по пути, относительно пути существующего образа, а если его нету - то проги
	}
	else
	{
		m_strImgName = strImgPath + strImgName;
	}

	//иначе - есть всё, значит всё нормально
	SetDlgItemText(IDC_EDIT_IMGNAME, m_strImgName);
	UpdateWindow(); //заставим показать новый m_strImgName
	int nTotalSectors = m_nCylinders * m_nHeads * m_nSectors; //произведение максимальных значений влазит в 32 бита
	m_progress.SetPos(0);
	m_progress.SetRange32(0, nTotalSectors);

	switch (nTypeHDI)
	{
		case 0:
		default:
			WorkHDIX(nTotalSectors);
			break;

		case 1:
			WorkHDI(nTotalSectors);
			break;
	}

	m_progress.SetPos(0);
}

void CHDDImgMakerDlg::WorkHDI(int nTotalSectors)
{
	//теперь создадим образ
	BYTE pSector[SECTOR_SIZE];
	ZeroMemory(pSector, SECTOR_SIZE);
	SYS_SECTOR sector0;
	memcpy(&sector0, &g_sys_sector, sizeof(SYS_SECTOR));
	sector0.cylinders = m_nCylinders;
	sector0.heads = m_nHeads;
	sector0.sectors = m_nSectors;
	sector0.raw_bytes_per_sector = SECTOR_SIZE;
	sector0.raw_bytes_per_track = SECTOR_SIZE * m_nSectors;
	sector0.total_used_sectors = nTotalSectors;
	sector0.capacity_in_sectors = nTotalSectors + 1;
	CopyString2Sys(sector0.serial_number, m_strSerialNumber, 20);
	CopyString2Sys(sector0.model_name, m_strModelName, 40);
	CopyString2Sys(sector0.fw_version, _T("v1.0.0.0"), 8);
	//сформируем контрольную сумму
	BYTE cs = 0;
	auto *pSector0 = (BYTE *)&sector0;
	pSector0[510] = 0xA5;

	for (int i = 0; i < 511; ++i)
	{
		cs += pSector0[i];
	}

	cs = -cs;
	pSector0[511] = cs;

	/*
	если мы конвертируем из существующего образа, то
	здесь надо обработчик вставить
	*/
	if (m_bConvert)
	{
		if (m_file.Open(m_strImgName, CFile::modeCreate | CFile::modeWrite))
		{
			m_file.Write(&sector0, sizeof(SYS_SECTOR));

			if (m_fileExisting.Open(m_strExistingImgName, CFile::modeRead | CFile::shareDenyWrite))
			{
				bool bContinue = false;

				for (int i = 0; i < nTotalSectors; ++i)
				{
					if (!bContinue)
					{
						UINT r = m_fileExisting.Read(pSector, SECTOR_SIZE);

						if (r == 0)
						{
							ZeroMemory(pSector, SECTOR_SIZE);
							bContinue = true;
						}
					}

					m_file.Write(pSector, SECTOR_SIZE);
					m_progress.SetPos(i);
				}

				m_progress.SetPos(nTotalSectors);
				m_fileExisting.Close();
			}

			m_file.Close();
			AfxMessageBox(_T("Образ успешно сконвертирован."), MB_OK | MB_ICONINFORMATION);
		}
		else
		{
			AfxMessageBox(_T("Не удалось создать образ ") + m_strImgName, MB_OK | MB_ICONEXCLAMATION);
		}
	}
	else
	{
		if (m_file.Open(m_strImgName, CFile::modeCreate | CFile::modeWrite))
		{
			m_file.Write(&sector0, sizeof(SYS_SECTOR));

			for (int i = 0; i < nTotalSectors; ++i)
			{
				m_file.Write(pSector, SECTOR_SIZE);
				m_progress.SetPos(i);
			}

			m_progress.SetPos(nTotalSectors);
			m_file.Close();
			AfxMessageBox(_T("Образ успешно создан."), MB_OK | MB_ICONINFORMATION);
		}
		else
		{
			AfxMessageBox(_T("Не удалось создать образ ") + m_strImgName, MB_OK | MB_ICONEXCLAMATION);
		}
	}
}


/*
нужен ини файл в котором будет храниться такая инфа
[SYS_SECTOR]
Cylinders = (DWORD)
Heads = (DWORD)
Sectors = (DWORD)
Sector size = (DWORD)
Total sectors = (DWORD)
Serial number = (String)
Model name = (String)
FW version = (String)
Image File Name = (String)
*/
void CHDDImgMakerDlg::WorkHDIX(int nTotalSectors)
{
	BYTE pSector[SECTOR_SIZE];
	ZeroMemory(pSector, SECTOR_SIZE);
	m_Ini.Clear();
	//надо добавить строковые ресурсы. секцию и ключи
	m_Ini.SetValueInt(IDS_HDIX_SECTION,    IDS_HDIX_CYLINDERS, m_nCylinders);
	m_Ini.SetValueInt(IDS_HDIX_SECTION,    IDS_HDIX_HEADS, m_nHeads);
	m_Ini.SetValueInt(IDS_HDIX_SECTION,    IDS_HDIX_SECTORS, m_nSectors);
	m_Ini.SetValueInt(IDS_HDIX_SECTION,    IDS_HDIX_SECTOR_SIZE, SECTOR_SIZE);
	m_Ini.SetValueInt(IDS_HDIX_SECTION,    IDS_HDIX_TOTAL_SECTORS, nTotalSectors);
	m_Ini.SetValueString(IDS_HDIX_SECTION, IDS_HDIX_SERIAL_NUMBER, m_strSerialNumber);
	m_Ini.SetValueString(IDS_HDIX_SECTION, IDS_HDIX_MODELNAME, m_strModelName);
	m_Ini.SetValueString(IDS_HDIX_SECTION, IDS_HDIX_FWVERSION, _T("v1.0.0.1"));

	if (m_bConvert)
	{
		//тут надо только создать hdix файл.
		_tsplitpath_s(m_strExistingImgName, pDrive, _MAX_DRIVE, pPath, _MAX_PATH, pTitle, _MAX_PATH, pExt, _MAX_EXT);
		CString strName = CString(pTitle) + CString(pExt);
		m_Ini.SetValueString(IDS_HDIX_SECTION, IDS_HDIX_IMG_FILENAME, strName);
		m_progress.SetPos(nTotalSectors);
		m_Ini.SetIniFileName(m_strImgName);

		if (m_Ini.FlushIni())
		{
			AfxMessageBox(_T("Образ успешно сконвертирован."), MB_OK | MB_ICONINFORMATION);
		}
		else
		{
			AfxMessageBox(_T("Не удалось сконвертировать образ."), MB_OK | MB_ICONEXCLAMATION);
		}
	}
	else
	{
		_tsplitpath_s(m_strImgName, pDrive, _MAX_DRIVE, pPath, _MAX_PATH, pTitle, _MAX_PATH, pExt, _MAX_EXT);
		CString strName = CString(pTitle) + _T(".img");
		m_Ini.SetValueString(IDS_HDIX_SECTION, IDS_HDIX_IMG_FILENAME, strName);
		strName = CString(pDrive) + CString(pPath) + strName;

		if (m_file.Open(strName, CFile::modeCreate | CFile::modeWrite))
		{
			for (int i = 0; i < nTotalSectors; ++i)
			{
				m_file.Write(pSector, SECTOR_SIZE);
				m_progress.SetPos(i);
			}

			m_progress.SetPos(nTotalSectors);
			m_file.Close();
			AfxMessageBox(_T("Образ успешно создан."), MB_OK | MB_ICONINFORMATION);
			m_Ini.SetIniFileName(m_strImgName);
			m_Ini.FlushIni();
		}
		else
		{
			AfxMessageBox(_T("Не удалось создать образ ") + strName, MB_OK | MB_ICONEXCLAMATION);
		}
	}
}

void CHDDImgMakerDlg::CopyString2Sys(BYTE *dst, const CString &str, int max_len)
{
	int sizeOfString = str.GetLength();
	auto lpszAscii = new CHAR[sizeOfString]; //а потом, необходимо самим делать delete [] использованных строк

	if (lpszAscii)
	{
		int t_len = WideCharToMultiByte(CP_ACP, 0, str, -1, lpszAscii, sizeOfString, nullptr, nullptr);

		for (int i = 0; i < max_len; ++i)
		{
			dst[i] = (i < sizeOfString) ? lpszAscii[i] : ' ';
		}

		delete [] lpszAscii;

		//а теперь по традиции переставим байты в словах
		for (int i = 0; i < max_len; i += 2)
		{
			BYTE t = dst[i];
			dst[i] = dst[i + 1];
			dst[i + 1] = t;
		}
	}
	else
	{
		for (int i = 0; i < max_len; ++i)
		{
			dst[i] = ' ';
		}
	}
}



void CHDDImgMakerDlg::OnEnKillfocusEditImgname()
{
	_UpdateData();
	ChangeFormatExistingImage();
}

void CHDDImgMakerDlg::_UpdateData()
{
	UpdateData(TRUE);
	m_strImgName.Trim();
	m_strExistingImgName.Trim();
	UpdateData(FALSE);
}

void CHDDImgMakerDlg::ChangeMakeButtonText()
{
	if (m_bConvert)
	{
		SetDlgItemText(IDC_BUTTON_MAKE, _T("Конвертировать"));
	}
	else
	{
		SetDlgItemText(IDC_BUTTON_MAKE, _T("Создать"));
	}
}

void CHDDImgMakerDlg::ChangeFormatExistingImage()
{
	if (m_strExistingImgName.IsEmpty())
	{
		SetDlgItemText(IDC_STATIC_FORMAT, _T(""));
	}
}
//имя существующего образа изменилось.
void CHDDImgMakerDlg::OnEnKillfocusEditExistingImgname()
{
	_UpdateData();
	m_bConvert = false;
	//проверка на валидность имени. т.к. там может быть и путь, а может и не быть.
	//если пути нет, надо добавить путь по умолчанию.
	_tsplitpath_s(m_strExistingImgName, pDrive, _MAX_DRIVE, pPath, _MAX_PATH, pTitle, _MAX_PATH, pExt, _MAX_EXT);
	CString strDrive = CString(pDrive);
	CString strPath = CString(pPath);
	CString strTitle = CString(pTitle);
	CString strExt = CString(pExt);

	//имя файла пусто
	if (m_strExistingImgName.IsEmpty() || strTitle.IsEmpty())
	{
		//разрешим менять геометрию
		EnableGeometryChange(true);
		ChangeMakeButtonText();
		return;
	}

	if (strExt.IsEmpty())
	{
		strExt = _T(".img");
	}

	CString strImgPath = strDrive + strPath;
	CString strImgName = strTitle + strExt;

	if (strImgPath.IsEmpty()) //если пути вообще нет
	{
		m_strExistingImgName = m_strAppPath + strImgName; //значит предполагаем, что файл находится рядом с прогой
	}
	else if (strDrive.IsEmpty() && !strPath.IsEmpty())  //если буквы диска нету, но есть путь
	{
		m_strExistingImgName = m_strAppPath + strPath + strImgName; //значит человек хочет, чтобы файл искался по пути, относительно пути проги
	}
	else
	{
		m_strExistingImgName = strImgPath + strImgName;
	}

	SetDlgItemText(IDC_EDIT_EXISTING_IMGNAME, m_strExistingImgName);
	UpdateWindow();
	//теперь, после окончательного формирования выделим путь, который получился в результате
	_tsplitpath_s(m_strExistingImgName, pDrive, _MAX_DRIVE, pPath, _MAX_PATH, pTitle, _MAX_PATH, pExt, _MAX_EXT);
	m_strExistingImgPath = CString(pDrive) + CString(pPath);
	//иначе - есть всё, значит всё нормально
	SetDlgItemText(IDC_STATIC_FORMAT, _T(""));

	//теперь открываем файл и анализируем его.
	if (m_fileExisting.Open(m_strExistingImgName, CFile::modeRead | CFile::shareDenyWrite))
	{
		//определяем тип образа: AltPro/Samara, читаем геометрию, обновляем данные
		//и запрещаем менять размеры, если всё нормально.
		AnalyseExistingImage(&m_fileExisting);
		m_fileExisting.Close();
	}
	else
	{
		AfxMessageBox(_T("Не удалось открыть образ ") + m_strExistingImgName, MB_OK | MB_ICONEXCLAMATION);
	}

	ChangeMakeButtonText();
}

void CHDDImgMakerDlg::AnalyseExistingImage(CFile *pFile)
{
	USector buf;
	USector tmpbuf;
	WORD C, H, S;
	BYTE nPartitions;
	bool bSamara = false;

	//проверка типа образа.
	if ((pFile->Seek(7 * 512, CFile::begin) == 7 * 512) && (pFile->Read(buf.b, 512) == 512))
	{
		InverseBuf(buf);
		//прочитаем заранее геометрию
		C = buf.w[255 - 0];
		H = buf.w[255 - 1] & 0xff;
		S = buf.w[255 - 2];
		//и количество разделов
		nPartitions = buf.b[511 - 7];
		WORD crc = 012701;

		for (int i = 0; i < (nPartitions * 2 + 4); ++i)
		{
			crc += buf.w[255 - i];
		}

		if (buf.w[251 - (nPartitions * 2)] != crc)
		{
			//образ не в формате альтпро, но тогда может в формате Самара?
			if ((pFile->Seek(512, CFile::begin) == 512) && (pFile->Read(buf.b, 512) == 512))
			{
				InverseBuf(buf);
				//Определяем геометрию
				int nCylVol = buf.w[1]; //объём цилиндра
				S = buf.b[4]; //число секторов на дорожке
				H = buf.b[5] + 1; //число головок

				//число цилиндров нигде не хранится, и его надо будет вычислить примерное количество
				if (nCylVol && (nCylVol == H * S)) //если совпало. значит пока всё правильно
				{
					ULONGLONG len = pFile->GetLength() / SECTOR_SIZE;
					C = WORD(len / nCylVol); //вычислим количество дорожек

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

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

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

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

						if ((pFile->Seek(lba, CFile::begin) == lba) && (pFile->Read(tmpbuf.b, 512) == 512))
						{
							InverseBuf(tmpbuf);

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

						nPartitions++; nld++;
					}
				}

				if (bSamara)
				{
					goto l_OK_Convert;
				}
			}
			else
			{
				goto l_Error_Read;
			}
		}
		else
		{
l_OK_Convert:
			//всё в порядке, можно конвертировать
			m_bConvert = true;
			//обновляем данные и запрещаем менять размеры
			EnableGeometryChange(false);
			m_nCylinders = C;
			m_nHeads = H;
			m_nSectors = S;
			UpdateData(FALSE);
		}

		if (!m_bConvert)
		{
			AfxMessageBox(_T("Формат образа неопознан. Игнорируем."), MB_OK | MB_ICONINFORMATION);
		}
		else
		{
			SetDlgItemText(IDC_STATIC_FORMAT, (bSamara) ? _T("Самара") : _T("АльтПро"));
		}
	}
	else
	{
l_Error_Read:
		AfxMessageBox(_T("Ошибка чтения образа ") + m_strExistingImgName, MB_OK | MB_ICONEXCLAMATION);
	}
}

void CHDDImgMakerDlg::InverseBuf(USector &buf)
{
	for (WORD &i : buf.w)
	{
		i = ~i;
	}
}

void CHDDImgMakerDlg::EnableGeometryChange(bool bEnable)
{
	GetDlgItem(IDC_EDIT_CYLINDERS)->EnableWindow(bEnable);
	GetDlgItem(IDC_EDIT_HEADS)->EnableWindow(bEnable);
	GetDlgItem(IDC_EDIT_SECTORS)->EnableWindow(bEnable);
	GetDlgItem(IDC_SPIN_CYLINDERS)->EnableWindow(bEnable);
	GetDlgItem(IDC_SPIN_HEADS)->EnableWindow(bEnable);
	GetDlgItem(IDC_SPIN_SECTORS)->EnableWindow(bEnable);
}


void CHDDImgMakerDlg::OnEnKillfocusEditSectors()
{
	UpdateData(TRUE);
	ChangeFormatExistingImage();
}

void CHDDImgMakerDlg::OnEnSetfocusEditSectors()
{
	UpdateData(TRUE);
	ChangeFormatExistingImage();
}

void CHDDImgMakerDlg::OnEnKillfocusEditHeads()
{
	UpdateData(TRUE);
	ChangeFormatExistingImage();
}

void CHDDImgMakerDlg::OnEnSetfocusEditHeads()
{
	UpdateData(TRUE);
	ChangeFormatExistingImage();
}

void CHDDImgMakerDlg::OnEnKillfocusEditCylinders()
{
	UpdateData(TRUE);
	ChangeFormatExistingImage();
}

void CHDDImgMakerDlg::OnEnSetfocusEditCylinders()
{
	UpdateData(TRUE);
	ChangeFormatExistingImage();
}

