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

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

#pragma warning(disable:4996)

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

constexpr auto HDIX_EXT = 0;
constexpr auto HDI_EXT = 1;
constexpr auto IMG_EXT = 2;

//расширения разных типов виртуальных HDD
const CString CHDDImgMakerDlg::strVHDDext[3] =
{
	_T(".hdix"),
	_T(".hdi"),
	_T(".img")
};

constexpr auto HDIX_ID_ROW_LEN = 16; //количество байтов в строке


CString CHDDImgMakerDlg::GetPath(CString &filename)
{
	CString strDrive, strPath;
	_tsplitpath_s(filename.GetString(), strDrive.GetBufferSetLength(_MAX_DRIVE), _MAX_DRIVE, strPath.GetBufferSetLength(_MAX_PATH), _MAX_PATH, nullptr, 0, nullptr, 0);
	strDrive.ReleaseBuffer();
	strPath.ReleaseBuffer();
	return strDrive + strPath;
}

CString CHDDImgMakerDlg::GetTitle(CString &filename)
{
	CString strTitle;
	_tsplitpath_s(filename.GetString(), nullptr, 0, nullptr, 0, strTitle.GetBufferSetLength(_MAX_PATH), _MAX_PATH, nullptr, 0);
	strTitle.ReleaseBuffer();
	return strTitle;
}

CString CHDDImgMakerDlg::GetExt(CString &filename)
{
	CString strExt;
	_tsplitpath_s(filename.GetString(), nullptr, 0, nullptr, 0, nullptr, 0, strExt.GetBufferSetLength(_MAX_EXT), _MAX_EXT);
	strExt.ReleaseBuffer();
	return strExt;
}

CString CHDDImgMakerDlg::GetName(CString &filename)
{
	CString strTitle, strExt;
	_tsplitpath_s(filename.GetString(), nullptr, 0, nullptr, 0, strTitle.GetBufferSetLength(_MAX_PATH), _MAX_PATH, strExt.GetBufferSetLength(_MAX_EXT), _MAX_EXT);
	strTitle.ReleaseBuffer();
	strExt.ReleaseBuffer();
	return strTitle + strExt;
}

int CHDDImgMakerDlg::ShowMessageBox(UINT msgID, UINT nType /*= 0*/, UINT nIDhelp /*= 0*/)
{
	CString strMsg;
	strMsg.LoadString(msgID);
	return AfxMessageBox(strMsg.GetString(), nType, nIDhelp);
}

int CHDDImgMakerDlg::ShowMessageBox(UINT msgID, CString str, UINT nType, UINT nIDhelp)
{
	CString strMsg;
	strMsg.LoadString(msgID);
	return AfxMessageBox((strMsg + str).GetString(), nType, nIDhelp);
}

// Диалоговое окно 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.GetString(), &dwHandle);

	if (dwSize)
	{
		auto lpData = new TCHAR[ dwSize ];
		CString str = _T("BK Virtual HDD Maker ");
		CString s;

		if (lpData)
		{
			GetFileVersionInfo(name.GetString(), dwHandle, dwSize, lpData);
			UINT uBufLen;
			VS_FIXEDFILEINFO *lpfi;
			VerQueryValue(lpData, _T("\\"), (void **)&lpfi, &uBufLen);
			s.Format(_T("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
			delete[] lpData;
		}
		else
		{
			s = _T("v???");
		}

		GetDlgItem(IDC_STATIC2)->SetWindowText(str + s);
	}

	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_ButtonMenu(nullptr)
	, 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_bCancel(false)
	, m_bConvert(false)
	, m_file(nullptr)
	, m_fileExisting(nullptr)
{
	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_WM_DESTROY()
	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_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)
	ON_CBN_SELCHANGE(IDC_COMBO_HDITYPE, &CHDDImgMakerDlg::OnCbnSelchangeComboHditype)
	ON_BN_CLICKED(IDC_MFCMENUBUTTON1, &CHDDImgMakerDlg::OnBnClickedMfcmenubutton1)
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,           IDS_TTSTR_EDIT_IMGNAME           },
	{ IDC_BUTTON_BROWSE,          IDS_TTSTR_BUTTON_BROWSE          },
	{ IDC_EDIT_EXISTING_IMGNAME,  IDS_TTSTR_EDIT_EXISTING_IMGNAME  },
	{ IDC_BUTTON_BROWSE_EXISTING, IDS_TTSTR_BUTTON_BROWSE_EXISTING },
	{ IDC_EDIT_CYLINDERS,         IDS_TTSTR_EDIT_CYLINDERS         },
	{ IDC_SPIN_CYLINDERS,         IDS_TTSTR_SPIN_CYLINDERS         },
	{ IDC_EDIT_HEADS,             IDS_TTSTR_EDIT_HEADS             },
	{ IDC_SPIN_HEADS,             IDS_TTSTR_SPIN_HEADS             },
	{ IDC_EDIT_SECTORS,           IDS_TTSTR_EDIT_SECTORS           },
	{ IDC_SPIN_SECTORS,           IDS_TTSTR_SPIN_SECTORS           },
	{ IDC_EDIT_MODELNAME,         IDS_TTSTR_EDIT_MODELNAME         },
	{ IDC_EDIT_SERNUMBER,         IDS_TTSTR_EDIT_SERNUMBER         },
	{ IDC_BUTTON_SERNUMBER,       IDS_TTSTR_BUTTON_SERNUMBER       },
	{ IDC_MFCMENUBUTTON1,         IDS_TTSTR_MFCMENUBUTTON1         },
	{ IDC_COMBO_HDITYPE,          IDS_TTSTR_COMBO_HDITYPE          },
	{ IDC_BUTTON_MAKE,            IDS_TTSTR_BUTTON_MAKE            },
	{ IDOK,                       IDS_TTSTR_IDOK                   }
};

// обработчики сообщений 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);        // Мелкий значок
	CString strModuleName;

	if (::GetModuleFileName(AfxGetInstanceHandle(), strModuleName.GetBufferSetLength(_MAX_PATH), _MAX_PATH))
	{
		strModuleName.ReleaseBuffer();
		m_strAppPath = GetPath(strModuleName);
	}
	else
	{
		strModuleName.ReleaseBuffer();
		m_strAppPath = _T("");
	}

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

	for (auto i : tt_strc)
	{
		CString str;
		str.LoadString(i.nStrResID);
		m_tt.AddTool(GetDlgItem(i.nID), str.GetString());
	}

	m_TypeCombo.InsertString(HDIX_EXT, strVHDDext[HDIX_EXT]);
	m_TypeCombo.InsertString(HDI_EXT, strVHDDext[HDI_EXT]);
	m_TypeCombo.SetCurSel(HDI_EXT);
	// Load application list into menu button
	m_ButtonMenu = CreatePopupMenu();
	static TCHAR *pMenu[2] =
	{
		_T("hdix -> hdi\0"),
		_T("hdi -> hdix\0")
	};
	MENUITEMINFO MenuInfo;
	memset(&MenuInfo, 0, sizeof(MENUITEMINFO));
	MenuInfo.cbSize = sizeof(MENUITEMINFO);
	MenuInfo.fMask = MIIM_ID | MIIM_STRING | MIIM_DATA;
	MenuInfo.wID = 1;

	for (auto &str : pMenu)
	{
		MenuInfo.dwTypeData = str;
		MenuInfo.cch = wcslen(MenuInfo.dwTypeData);
		InsertMenuItem(m_ButtonMenu, MenuInfo.wID, TRUE, &MenuInfo);
		MenuInfo.wID++;
	}

	// Attach menu to CMFCMenuButton
	auto pMenuButton = reinterpret_cast<CMFCMenuButton *>(GetDlgItem(IDC_MFCMENUBUTTON1));
	pMenuButton->m_bOSMenu = FALSE;
	pMenuButton->m_bRightArrow = FALSE;
	pMenuButton->m_bStayPressed = TRUE;
	pMenuButton->m_bDefaultClick = TRUE;
	pMenuButton->m_hMenu = m_ButtonMenu;
	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::OnDestroy()
{
	if (m_ButtonMenu)
	{
		DestroyMenu(m_ButtonMenu);
		m_ButtonMenu = nullptr;
	}

	CDialogEx::OnDestroy();
}

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) * SECTOR_SIZEB;
	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()
{
	m_strSerialNumber = CString(::GenerateSerialNumber().c_str());
	SetDlgItemText(IDC_EDIT_SERNUMBER, m_strSerialNumber);
}


void CHDDImgMakerDlg::OnBnClickedButtonBrowse()
{
	CString strFilterImg;
	strFilterImg.LoadString(IDS_STR_FILTERVHDD);
	CFileDialog dlg(TRUE, strVHDDext[HDI_EXT], 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;
	strFilterImg.LoadString(IDS_STR_FILTERALLIMG);
	CFileDialog dlg(TRUE, strVHDDext[IMG_EXT], 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();
	}
}



void CHDDImgMakerDlg::OnBnClickedMfcmenubutton1()
{
	auto pMenuButton = reinterpret_cast<CMFCMenuButton *>(GetDlgItem(IDC_MFCMENUBUTTON1));
	int nSel = pMenuButton->m_nMenuResult; //выделенный пункт меню

	switch (nSel)
	{
		default:
		case 0:
			ConvertHdi2Img();
			break;

		case 1:
			ConvertHdix2Hdi();
			break;

		case 2:
			ConvertHdi2Hdix();
			break;
	}
}

/*
Обратное преобразование hdi в img
*/
void CHDDImgMakerDlg::ConvertHdi2Img()
{
	uint8_t pSector[SECTOR_SIZEB] = { 0 };
	CString strFilterImg;
	strFilterImg.LoadString(IDS_STR_FILTERHDI);
	CFileDialog dlg(TRUE, strVHDDext[HDI_EXT], 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();
		CString strImgName = GetPath(strHDIName) + GetTitle(strHDIName) + strVHDDext[IMG_EXT];

		if (m_file = _tfopen(strImgName.GetString(), _T("wb")))
		{
			if (m_fileExisting = _tfopen(strHDIName.GetString(), _T("rb")))
			{
				fread(&m_sector0, 1, sizeof(SYS_SECTOR), m_fileExisting); //сперва прочитаем заголовок.

				//проверим контрольную сумму, вдруг нам фуфло какое-то подсовывают
				if (::CheckCS(reinterpret_cast<USector *>(&m_sector0)))
				{
					bool bContinue = false; //флаг продолжения. если образ короче, чем задано в геометрии
					int nTotalSectors = m_sector0.cylinders * m_sector0.heads * m_sector0.sectors; //произведение максимальных значений влазит в 32 бита
					m_progress.SetPos(0);
					m_progress.SetRange32(0, nTotalSectors);

					for (int i = 0; i < nTotalSectors; ++i)
					{
						if (!bContinue)
						{
							size_t r = fread(pSector, SECTOR_SIZEB, 1, m_fileExisting);

							if (r != SECTOR_SIZEB)
							{
								int answer = ShowMessageBox(IDS_STR_MSG_BROKENHDI, MB_CANCELTRYCONTINUE | MB_ICONSTOP);

								if (answer == IDCANCEL)
								{
									break;
								}

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

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

					m_progress.SetPos(nTotalSectors);
					bStatus = true;
				}
				else
				{
					ShowMessageBox(IDS_STR_MSG_CORRUPTHDI, MB_OK | MB_ICONEXCLAMATION);
				}

				fclose(m_fileExisting);
			}

			fclose(m_file);

			if (bStatus)
			{
				ShowMessageBox(IDS_STR_MSG_CONVERTSUCCESS, MB_OK | MB_ICONINFORMATION);
			}
		}
		else
		{
			ShowMessageBox(IDS_STR_MSG_CREATEERROR, strImgName, MB_OK | MB_ICONEXCLAMATION);
		}
	}

	m_progress.SetPos(0);
}

void CHDDImgMakerDlg::ConvertHdi2Hdix()
{
	uint8_t pSector[SECTOR_SIZEB] = { 0 };
	CString strFilterImg;
	strFilterImg.LoadString(IDS_STR_FILTERHDI);
	CFileDialog dlg(TRUE, strVHDDext[HDI_EXT], 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();
		CString str = GetPath(strHDIName) + GetTitle(strHDIName);
		CString strHdixName = str + strVHDDext[HDIX_EXT];
		CString strImgName = str + strVHDDext[IMG_EXT];

		if (m_fileExisting = _tfopen(strHDIName.GetString(), _T("rb")))
		{
			fread(&m_sector0, sizeof(SYS_SECTOR), 1, m_fileExisting); //сперва прочитаем заголовок.
			//проверим контрольную сумму, вдруг нам фуфло какое-то подсовывают
			auto pSector0 = reinterpret_cast<USector *>(&m_sector0);

			if (::CheckCS(pSector0))
			{
				int nTotalSectors = m_sector0.cylinders * m_sector0.heads * m_sector0.sectors; //произведение максимальных значений влазит в 32 бита
				m_progress.SetPos(0);
				m_progress.SetRange32(0, nTotalSectors);
				m_Ini.Clear();
				m_Ini.SetIniFileName(strHdixName);
				//надо добавить строковые ресурсы. секцию и ключи
				m_Ini.SetValueString(IDS_HDIX_SECTION, IDS_HDIX_IMG_TYPE, _T("ATA"));
				//генерация дампа паспорта.
				CString strKey, strVal, str;
				int nAddr = 0;

				while (nAddr < SECTOR_SIZEB)
				{
					strKey.Format(_T("0x%04X"), nAddr); //формируем имя параметра
					strVal.Empty();

					for (int i = 0; i < HDIX_ID_ROW_LEN; ++i)
					{
						str.Format(_T("0x%02x"), pSector0->b[nAddr++]);

						if (i)
						{
							strVal += _T(" ");
						}

						strVal += str;
					}

					m_Ini.SetValueString(IDS_HDIX_IDSECTION, strKey, strVal);
				}

				m_Ini.SetValueString(IDS_HDIX_SECTION, IDS_HDIX_IMG_FILENAME, GetName(strImgName));
				m_Ini.FlushIni(); //создаём hdix сигнатуру

				//создаём img файл
				if (m_file = _tfopen(strImgName.GetString(), _T("wb")))
				{
					for (int i = 0; i < nTotalSectors; ++i)
					{
						fread(pSector, SECTOR_SIZEB, 1, m_fileExisting);
						fwrite(pSector, SECTOR_SIZEB, 1, m_file);
						m_progress.SetPos(i);
					}

					m_progress.SetPos(nTotalSectors);
					bStatus = true;
					fclose(m_file);
				}
			}
			else
			{
				ShowMessageBox(IDS_STR_MSG_CORRUPTHDI, MB_OK | MB_ICONEXCLAMATION);
			}

			fclose(m_fileExisting);
		}
		else
		{
			ShowMessageBox(IDS_STR_MSG_READERROR, strHDIName, MB_OK | MB_ICONEXCLAMATION);
		}

		if (bStatus)
		{
			ShowMessageBox(IDS_STR_MSG_CONVERTSUCCESS, MB_OK | MB_ICONINFORMATION);
		}
	}

	m_progress.SetPos(0);
}

void CHDDImgMakerDlg::ConvertHdix2Hdi()
{
	uint8_t pSector[SECTOR_SIZEB] = { 0 };
	CString strFilterImg;
	strFilterImg.LoadString(IDS_STR_FILTERHDIX);
	CFileDialog dlg(TRUE, strVHDDext[HDIX_EXT], nullptr,
	                OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_NOCHANGEDIR | OFN_EXPLORER | OFN_FILEMUSTEXIST,
	                strFilterImg, this);

	if (dlg.DoModal() == IDOK)
	{
		bool bStatus = false;
		CString strHdixName = dlg.GetPathName();
		strHdixName.Trim();
		CString strHdiName = GetPath(strHdixName) + GetTitle(strHdixName) + strVHDDext[HDI_EXT];
		CString strImgName = GetPath(strHdixName) + m_Ini.GetValueString(IDS_HDIX_SECTION, IDS_HDIX_IMG_FILENAME, _T(""));
		m_Ini.Clear();
		m_Ini.SetIniFileName(strHdixName);

		if (m_Ini.GetValueString(IDS_HDIX_SECTION, IDS_HDIX_IMG_TYPE, _T("")) == _T("ATA"))
		{
			//нужно прочитать и сформировать паспорт
			auto pSector0 = reinterpret_cast<USector *>(&m_sector0);
			int nAddr = 0;
			//сейчас надо получить все ключи из заданной секции.
			CIni::INI_SECTION_STRUCT *pSS = m_Ini.GetSectionPtr(IDS_HDIX_IDSECTION);

			if (pSS)
			{
				CIni::INI_KEY_STRUCT *pKS = pSS->Keys;

				while (pKS)
				{
					nAddr = _tcstol(pKS->Key.GetString(), nullptr, 0);

					if (0 <= nAddr && nAddr < SECTOR_SIZEB)
					{
						auto pVal = (TCHAR *)pKS->Value.GetString();

						do
						{
							int v = _tcstol(pVal, &pVal, 0);
							pSector0->b[nAddr++] = v;
						}
						while (_tcslen(pVal));
					}

					pKS = pKS->next;
				}

				//теперь посчитаем КС
				if (::CheckCS(pSector0))
				{
					int nTotalSectors = m_sector0.cylinders * m_sector0.heads * m_sector0.sectors; //произведение максимальных значений влазит в 32 бита
					m_progress.SetPos(0);
					m_progress.SetRange32(0, nTotalSectors);

					if (m_file = _tfopen(strHdiName.GetString(), _T("wb")))
					{
						fwrite(&m_sector0, sizeof(SYS_SECTOR), 1, m_file);

						if (m_fileExisting = _tfopen(strImgName.GetString(), _T("rb")))
						{
							bool bContinue = false;

							for (int i = 0; i < nTotalSectors; ++i)
							{
								if (!bContinue)
								{
									size_t r = fread(pSector, SECTOR_SIZEB, 1, m_fileExisting);

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

								fwrite(pSector, SECTOR_SIZEB, 1, m_file);
								m_progress.SetPos(i);
							}

							m_progress.SetPos(nTotalSectors);
							fclose(m_fileExisting);
							bStatus = true;
						}
						else
						{
							ShowMessageBox(IDS_STR_MSG_READERROR, strImgName, MB_OK | MB_ICONEXCLAMATION);
						}

						fclose(m_file);
					}
					else
					{
						ShowMessageBox(IDS_STR_MSG_CREATEERROR, strHdiName, MB_OK | MB_ICONEXCLAMATION);
					}
				}

				if (bStatus)
				{
					ShowMessageBox(IDS_STR_MSG_CONVERTSUCCESS, MB_OK | MB_ICONINFORMATION);
				}
			}
		}
		else
		{
			ShowMessageBox(IDS_STR_MSG_UNABLECONVERT, strHdiName, MB_OK | MB_ICONEXCLAMATION);
		}
	}

	m_progress.SetPos(0);
}

void CHDDImgMakerDlg::OnCbnSelchangeComboHditype()
{
	int nTypeHDI = m_TypeCombo.GetCurSel();

	if (!m_strImgName.IsEmpty() && (GetExt(m_strImgName) != strVHDDext[nTypeHDI]))
	{
		m_strImgName = GetPath(m_strImgName) + GetTitle(m_strImgName) + strVHDDext[nTypeHDI];
		UpdateData(FALSE);
	}
}


void CHDDImgMakerDlg::OnBnClickedButtonMake()
{
	int nTypeHDI = m_TypeCombo.GetCurSel(); //и далее в зависимости от выбранного формата формировать образ.
	m_strImgName.Trim();

	// если мы конвертируем, и имя не задано, то его можно создать из существующего
	if (m_bConvert && m_strImgName.IsEmpty())
	{
		m_strImgName = GetPath(m_strExistingImgName) + GetTitle(m_strExistingImgName);
	}

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

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

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

	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);
	UpdateData(FALSE); //заставим показать новый 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::MakeSysSector()
{
	IMGFormat imgf;
	imgf.C = m_nCylinders;
	imgf.H = m_nHeads;
	imgf.S = m_nSectors;
	::CreateHDISector(&imgf, &m_sector0, std::wstring(m_strSerialNumber), std::wstring(m_strModelName));
}

void CHDDImgMakerDlg::WorkHDI(int nTotalSectors)
{
	//теперь создадим образ
	uint8_t pSector[SECTOR_SIZEB];
	ZeroMemory(pSector, SECTOR_SIZEB);
	MakeSysSector();

	/*
	если мы конвертируем из существующего образа, то
	здесь надо обработчик вставить
	*/
	if (m_bConvert)
	{
		if (m_file = _tfopen(m_strImgName.GetString(), _T("wb")))
		{
			fwrite(&m_sector0, sizeof(SYS_SECTOR), 1, m_file);

			if (m_fileExisting = _tfopen(m_strExistingImgName.GetString(), _T("rb")))
			{
				bool bContinue = false;

				for (int i = 0; i < nTotalSectors; ++i)
				{
					if (!bContinue)
					{
						size_t r = fread(pSector, SECTOR_SIZEB, 1, m_fileExisting);

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

					fwrite(pSector, SECTOR_SIZEB, 1, m_file);
					m_progress.SetPos(i);
				}

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

			fclose(m_file);
			ShowMessageBox(IDS_STR_MSG_CONVERTSUCCESS, MB_OK | MB_ICONINFORMATION);
		}
		else
		{
			ShowMessageBox(IDS_STR_MSG_CREATEERROR, m_strImgName, MB_OK | MB_ICONEXCLAMATION);
		}
	}
	else
	{
		if (m_file = _tfopen(m_strImgName.GetString(), _T("wb")))
		{
			fwrite(&m_sector0, sizeof(SYS_SECTOR), 1, m_file);

			for (int i = 0; i < nTotalSectors; ++i)
			{
				fwrite(pSector, SECTOR_SIZEB, 1, m_file);
				m_progress.SetPos(i);
			}

			m_progress.SetPos(nTotalSectors);
			fclose(m_file);
			ShowMessageBox(IDS_STR_MSG_CREATESUCCESS, MB_OK | MB_ICONINFORMATION);
		}
		else
		{
			ShowMessageBox(IDS_STR_MSG_CREATEERROR, m_strImgName, MB_OK | MB_ICONEXCLAMATION);
		}
	}
}


/*
нужен ини файл в котором будет храниться такая инфа
[HDIX]
Image File = (String)
Type = ATA / ATAPI - определяет, что за образ: hdd или cdrom

[HDD_ID]
тут будет храниться дамп паспорта для hdd, для сдром - пусто
0x0000 = 0x00 0x00 ... 0x00
0x0010 = 0x00 0x00 ... 0x00
вот в таком виде. имя параметра - адрес в хексе,
значение - 16 байтов в хексе.
*/
void CHDDImgMakerDlg::WorkHDIX(int nTotalSectors)
{
	MakeSysSector();
	uint8_t pSector[SECTOR_SIZEB];
	ZeroMemory(pSector, SECTOR_SIZEB);
	m_Ini.Clear();
	m_Ini.SetIniFileName(m_strImgName);
	//надо добавить строковые ресурсы. секцию и ключи
	m_Ini.SetValueString(IDS_HDIX_SECTION, IDS_HDIX_IMG_TYPE, _T("ATA"));
	//генерация дампа паспорта.
	auto pSector0 = reinterpret_cast<uint8_t *>(&m_sector0);
	CString strKey, strVal, str;
	int nAddr = 0;

	while (nAddr < SECTOR_SIZEB)
	{
		strKey.Format(_T("0x%04X"), nAddr); //формируем имя параметра
		strVal.Empty();

		for (int i = 0; i < HDIX_ID_ROW_LEN; ++i)
		{
			str.Format(_T("0x%02x"), pSector0[nAddr++]);

			if (i)
			{
				strVal += _T(" ");
			}

			strVal += str;
		}

		m_Ini.SetValueString(IDS_HDIX_IDSECTION, strKey, strVal);
	}

	if (m_bConvert)
	{
		//тут надо сконвертировать hdix файл.
		CString strName;

		if (GetTitle(m_strImgName) == GetTitle(m_strExistingImgName))
		{
			//если имена файлов совпадают, то оригинальный img
			//надо скопировать под новым именем, и дать ему расширение .bak или .orig
			CopyFile(m_strExistingImgName, m_strExistingImgName + _T(".bak"), FALSE);
			strName = GetName(m_strExistingImgName);
		}
		else
		{
			strName = GetTitle(m_strImgName) + GetExt(m_strExistingImgName);
			CopyFile(m_strExistingImgName, GetPath(m_strImgName) + strName, FALSE);
		}

		m_Ini.SetValueString(IDS_HDIX_SECTION, IDS_HDIX_IMG_FILENAME, strName);
		m_progress.SetPos(nTotalSectors);

		if (m_Ini.FlushIni())
		{
			ShowMessageBox(IDS_STR_MSG_CONVERTSUCCESS, MB_OK | MB_ICONINFORMATION);
		}
		else
		{
			ShowMessageBox(IDS_STR_MSG_CONVERTERROR, MB_OK | MB_ICONEXCLAMATION);
		}
	}
	else
	{
		//тут надо просто создать hdix файл.
		CString strName = GetTitle(m_strImgName) + strVHDDext[IMG_EXT];
		m_Ini.SetValueString(IDS_HDIX_SECTION, IDS_HDIX_IMG_FILENAME, strName);
		strName = GetPath(m_strImgName) + strName;

		if (m_file = _tfopen(strName.GetString(), _T("wb")))
		{
			for (int i = 0; i < nTotalSectors; ++i)
			{
				fwrite(pSector, SECTOR_SIZEB, 1, m_file);
				m_progress.SetPos(i);
			}

			m_progress.SetPos(nTotalSectors);
			fclose(m_file);

			if (m_Ini.FlushIni())
			{
				ShowMessageBox(IDS_STR_MSG_CREATESUCCESS, MB_OK | MB_ICONINFORMATION);
			}
			else
			{
				ShowMessageBox(IDS_STR_MSG_FILECREATEERROR, m_strImgName, MB_OK | MB_ICONINFORMATION);
			}
		}
		else
		{
			ShowMessageBox(IDS_STR_MSG_FILECREATEERROR, strName, MB_OK | MB_ICONEXCLAMATION);
		}
	}
}

void CHDDImgMakerDlg::ChangeMakeButtonText()
{
	CString str;

	if (m_bConvert)
	{
		str.LoadString(IDS_STR_BTNTEXT_CONVERT);
	}
	else
	{
		str.LoadString(IDS_STR_BTNTEXT_CREATE);
	}

	SetDlgItemText(IDC_BUTTON_MAKE, str);
}

void CHDDImgMakerDlg::ChangeFormatExistingImage()
{
	if (m_strExistingImgName.IsEmpty())
	{
		SetDlgItemText(IDC_STATIC_FORMAT, _T(""));
	}
}


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

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

//имя существующего образа изменилось.
void CHDDImgMakerDlg::OnEnKillfocusEditExistingImgname()
{
	_UpdateData();
	m_bConvert = false;
	//проверка на валидность имени. т.к. там может быть и путь, а может и не быть.
	//если пути нет, надо добавить путь по умолчанию.
	CString strDrive;
	CString strPath;
	CString strTitle;
	CString strExt;
	_tsplitpath_s(m_strExistingImgName.GetString(),
	              strDrive.GetBufferSetLength(_MAX_DRIVE), _MAX_DRIVE,
	              strPath.GetBufferSetLength(_MAX_PATH), _MAX_PATH,
	              strTitle.GetBufferSetLength(_MAX_PATH), _MAX_PATH,
	              strExt.GetBufferSetLength(_MAX_EXT), _MAX_EXT);
	strDrive.ReleaseBuffer();
	strPath.ReleaseBuffer();
	strTitle.ReleaseBuffer();
	strExt.ReleaseBuffer();

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

	if (strExt.IsEmpty())
	{
		strExt = strVHDDext[IMG_EXT];
	}

	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);
	UpdateData(FALSE);
	//теперь, после окончательного формирования выделим путь, который получился в результате
	m_strExistingImgPath = GetPath(m_strExistingImgName);
	//иначе - есть всё, значит всё нормально
	SetDlgItemText(IDC_STATIC_FORMAT, _T(""));

	//теперь открываем файл и анализируем его.
	if (m_fileExisting = _tfopen(m_strExistingImgName.GetString(), _T("rb")))
	{
		//определяем тип образа: AltPro/Samara, читаем геометрию, обновляем данные
		//и запрещаем менять размеры, если всё нормально.
		AnalyseImage(m_fileExisting);
		fclose(m_fileExisting);
	}
	else
	{
		ShowMessageBox(IDS_STR_MSG_OPENERROR, m_strExistingImgName, MB_OK | MB_ICONEXCLAMATION);
	}

	ChangeMakeButtonText();
}

void CHDDImgMakerDlg::AnalyseImage(FILE *pFile)
{
	IMGFormat imgf;
	bool bStat = ::CheckFormat(pFile, &imgf);

	if (imgf.nIOStatus == IMGIOSTATUS::IO_ERROR)
	{
		ShowMessageBox(IDS_STR_MSG_READERROR, m_strExistingImgName, MB_OK | MB_ICONEXCLAMATION);
	}
	else
	{
		if (bStat)
		{
			//всё в порядке, можно конвертировать
			m_bConvert = true;
			//обновляем данные и запрещаем менять размеры
			EnableGeometryChange(false);
			m_nCylinders = imgf.C;
			m_nHeads = imgf.H;
			m_nSectors = imgf.S;
			UpdateData(FALSE);
			CString str;
			str.LoadString((imgf.bSamara) ? IDS_STR_NAME_SAMARA : IDS_STR_NAME_ALTPRO);
			SetDlgItemText(IDC_STATIC_FORMAT, str);
			OutByteSize();
		}
		else
		{
			ShowMessageBox(IDS_STR_MSG_UNKNOWNFORMAT, MB_OK | MB_ICONINFORMATION);
		}
	}
}

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();
}




