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

#include "stdafx.h"
#include "BKDE.h"
#include "BKDEDlg.h"
#include "afxdialogex.h"
#include "FFPickerDlg.h"
#include "RenameDialog.h"
#include "ChangeAddrDialog.h"
#include <io.h>
#include <afxhtml.h>
#include "fdrawcmd.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

#define STATUSBAR_POS_IMGNAME 0
#define STATUSBAR_POS_OSTYPE 1
#define STATUSBAR_POS_IMGSIZE 2
#define STATUSBAR_POS_SYSTEM 3
#define STATUSBAR_POS_OPENMODE 4

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

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

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

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

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

CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
	, m_Ie(nullptr)
{
}

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

BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()

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

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

		if (lpData)
		{
			GetFileVersionInfo(name.GetString(), dwHandle, dwSize, lpData);
			UINT uBufLen;
			VS_FIXEDFILEINFO *lpfi;
			VerQueryValue(lpData, _T("\\"), reinterpret_cast<void **>(&lpfi), &uBufLen);
			CString s;
			s.Format(_T("BK Disk Explorer v%d.%d.%d.%d "),
			         HIWORD(lpfi->dwFileVersionMS), LOWORD(lpfi->dwFileVersionMS),
			         HIWORD(lpfi->dwFileVersionLS), LOWORD(lpfi->dwFileVersionLS));
#ifdef _WIN64
#define TITLE_SUFFIX _T("x64")
#else
#define TITLE_SUFFIX _T("x86")
#endif
			s += TITLE_SUFFIX;
			GetDlgItem(IDC_STATIC_VERS)->SetWindowText(s);
#undef TITLE_SUFFIX
			delete[] lpData;
		}
	}

	CRect rect;
	GetDlgItem(IDC_STATIC_ABOUT)->GetWindowRect(&rect);
	ScreenToClient(&rect);
	m_Ie = dynamic_cast<CHtmlView *>(RUNTIME_CLASS(CHtmlView)->CreateObject());
	m_Ie->Create(nullptr, _T("WebAbout"), WS_CHILD | WS_VISIBLE, rect, this, AFX_IDW_PANE_FIRST, nullptr);
	m_Ie->LoadFromResource(IDR_HTML1);
	return TRUE;  // return TRUE unless you set the focus to a control
	// Исключение: страница свойств OCX должна возвращать значение FALSE
}

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

UINT CBKDEDlg::auIDStatusBar[] =
{
	ID_SEPARATOR,
	ID_INDICATOR_DOSTYPE,
	ID_INDICATOR_IMGSIZE,
	ID_INDICATOR_SYSTEM,
	ID_INDICATOR_OPENMODE
};


CBKDEDlg::CBKDEDlg(CWnd *pParent /*=nullptr*/)
	: CDialogEx(IDD_BKDE_DIALOG, pParent)
	, m_bStatusBarCreated(false)
	, m_nStartModeItemPos(0)
	, m_ApplicationMenu(nullptr)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CBKDEDlg::DoDataExchange(CDataExchange *pDX)
{
	CDialogEx::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_LIST_CONTENT, m_ListControl);
	DDX_Control(pDX, IDC_CHECK_BINEXTRACT, m_CheckBinExtract);
	DDX_Control(pDX, IDC_CHECK_LOGEXTRACT, m_CheckLogExtract);
	DDX_Control(pDX, IDC_CHECK_LONGBIN, m_CheckLongBin);
}

BEGIN_MESSAGE_MAP(CBKDEDlg, CDialogEx)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_WM_SIZE()
	ON_WM_SETFOCUS()
	ON_WM_GETMINMAXINFO()
	ON_WM_CLOSE()
	ON_WM_DROPFILES()
	ON_WM_HELPINFO()

	ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, &CBKDEDlg::OnToolTipText)
	ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, &CBKDEDlg::OnToolTipText)

	ON_MESSAGE(WM_MAKE_DROP, &CBKDEDlg::OnMakeDrop)
	ON_MESSAGE(WM_GET_CMDLINE, &CBKDEDlg::OnGetCmdLine)
	ON_MESSAGE(WM_SEND_PROCESSING, &CBKDEDlg::OnSendItemProcessing)
	ON_MESSAGE(WM_SEND_IMGNAMEPRC, &CBKDEDlg::OnSendImageProcessing)
	ON_MESSAGE(WM_SEND_ERRORNUM, &CBKDEDlg::OnSendErrorNum)
	ON_MESSAGE(WM_SEND_MESSAGEBOX, &CBKDEDlg::OnSendMessageBox)
	ON_MESSAGE(WM_OUT_CURR_FILE_PATH, &CBKDEDlg::OnOutCurrentFilePath)
	ON_MESSAGE(WM_OUT_CURR_IMG_INFO, &CBKDEDlg::OnOutCurrentImageInfo)
	ON_MESSAGE(WM_OUT_OF_IMAGE, &CBKDEDlg::OnOutImage)
	ON_MESSAGE(WM_PUT_INTO_LD, &CBKDEDlg::OnPutIntoLD)
	ON_MESSAGE(WM_SEND_ENABLE_BUTTON, &CBKDEDlg::OnSendEnableButton)
	ON_MESSAGE(WM_SEND_ENABLE_BUTTON2, &CBKDEDlg::OnSendEnableButton2)
	ON_MESSAGE(WM_GET_ENABLE_BUTTON, &CBKDEDlg::OnGetEnableButton)

	ON_BN_CLICKED(IDC_BUTTON_ABOUT, &CBKDEDlg::OnBnClickedButtonAbout)
	ON_BN_CLICKED(IDC_MFCMENUBUTTON_OPEN, &CBKDEDlg::OnBnClickedMfcmenubuttonOpen)
	ON_BN_CLICKED(IDC_BUTTON_VIEW, &CBKDEDlg::OnBnClickedButtonView)
	ON_BN_CLICKED(IDC_BUTTON_VIEW_AS_SPR, &CBKDEDlg::OnBnClickedButtonViewSprite)
	ON_BN_CLICKED(IDC_BUTTON_ADD, &CBKDEDlg::OnBnClickedButtonAdd)
	ON_BN_CLICKED(IDC_BUTTON_EXTRACT, &CBKDEDlg::OnBnClickedButtonExtract)
	ON_BN_CLICKED(IDC_BUTTON_RENAME, &CBKDEDlg::OnBnClickedButtonRename)
	ON_BN_CLICKED(IDC_BUTTON_DELETE, &CBKDEDlg::OnBnClickedButtonDelete)

	ON_BN_CLICKED(IDC_CHECK_BINEXTRACT, &CBKDEDlg::OnBnClickedCheckBinextract)
	ON_BN_CLICKED(IDC_CHECK_LOGEXTRACT, &CBKDEDlg::OnBnClickedCheckLogextract)
	ON_BN_CLICKED(IDC_BUTTON_CLOSE, &CBKDEDlg::OnBnClickedClose)

	ON_BN_CLICKED(IDC_CHECK_LONGBIN, &CBKDEDlg::OnBnClickedCheckLongbin)

	ON_COMMAND(ID_CONTEXT_RENAME, &CBKDEDlg::OnContextMenuRename)
	ON_COMMAND(ID_CONTEXT_CHGADDR, &CBKDEDlg::OnContextChangeAddr)
	ON_COMMAND(ID_CONTEXT_DELETE, &CBKDEDlg::OnContextDelete)
	ON_COMMAND(ID_CONTEXT_EXTRACT, &CBKDEDlg::OnContextExtract)
	ON_COMMAND(ID_CONTEXT_VIEWASTEXT, &CBKDEDlg::OnContextViewastext)
	ON_COMMAND(ID_CONTEXT_VIEWASSPRITE, &CBKDEDlg::OnContextViewassprite)
END_MESSAGE_MAP()

BOOL CBKDEDlg::OnToolTipText(UINT, NMHDR *pNMHDR, LRESULT *pResult)
{
	ASSERT(pNMHDR->code == TTN_NEEDTEXTA || pNMHDR->code == TTN_NEEDTEXTW);

	// if there is a top level routing frame then let it handle the message
	if (GetRoutingFrame() != nullptr)
	{
		return FALSE;
	}

	// to be thorough we will need to handle UNICODE versions of the message also !!
	auto pTTTA = reinterpret_cast<TOOLTIPTEXTA *>(pNMHDR);
	auto pTTTW = reinterpret_cast<TOOLTIPTEXTW *>(pNMHDR);
	constexpr auto nBufSize = 512;
	TCHAR szFullText[nBufSize];
	CString strTipText;
	UINT_PTR nID = pNMHDR->idFrom;

	if (pNMHDR->code == TTN_NEEDTEXTA && (pTTTA->uFlags & TTF_IDISHWND) ||
	        pNMHDR->code == TTN_NEEDTEXTW && (pTTTW->uFlags & TTF_IDISHWND))
	{
		// idFrom is actually the HWND of the tool
		nID = ::GetDlgCtrlID(reinterpret_cast<HWND>(nID));
	}

	if (nID != 0) // will be zero on a separator
	{
		AfxLoadString(nID, szFullText, nBufSize);
		strTipText = szFullText;
#ifndef _UNICODE

		if (pNMHDR->code == TTN_NEEDTEXTA)
		{
			lstrcpyn(pTTTA->szText, strTipText, sizeof(pTTTA->szText));
		}
		else
		{
			_mbstowcsz(pTTTW->szText, strTipText, sizeof(pTTTW->szText));
		}

#else

		if (pNMHDR->code == TTN_NEEDTEXTA)
		{
			_wcstombsz(pTTTA->szText, strTipText, sizeof(pTTTA->szText));
		}
		else
		{
			lstrcpyn(pTTTW->szText, strTipText, sizeof(pTTTW->szText) / sizeof(pTTTW->szText[0]));
		}

#endif
		*pResult = 0;
		// bring the tooltip window above other popup windows
		::SetWindowPos(pNMHDR->hwndFrom, HWND_TOP, 0, 0, 0, 0,
		               SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE | SWP_NOOWNERZORDER);
		return TRUE;
	}

	return FALSE;
}

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

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

	if (pSysMenu != nullptr)
	{
		BOOL bNameValid;
		CString strAboutMenu;
		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);        // Мелкий значок
	m_strImgName = _T("");
	PreUseLoadSettings();
	m_hAccelTable = LoadAccelerators(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDR_ACCELERATOR1));

	// создаём статусбар внизу диалогового окна
	if (m_StatusBar.Create(this))
	{
		m_StatusBar.SetIndicators(auIDStatusBar, sizeof(auIDStatusBar) / sizeof(UINT));
		// Make a sunken or recessed border around the first pane
		m_StatusBar.SetPaneInfo(STATUSBAR_POS_IMGNAME, m_StatusBar.GetItemID(STATUSBAR_POS_IMGNAME), SBPS_STRETCH, 0);
		m_StatusBar.SetPaneInfo(STATUSBAR_POS_OSTYPE, m_StatusBar.GetItemID(STATUSBAR_POS_OSTYPE), SBPS_NORMAL, 70);
		m_StatusBar.SetPaneInfo(STATUSBAR_POS_IMGSIZE, m_StatusBar.GetItemID(STATUSBAR_POS_IMGSIZE), SBPS_NORMAL, 50);
		m_StatusBar.SetPaneInfo(STATUSBAR_POS_SYSTEM, m_StatusBar.GetItemID(STATUSBAR_POS_SYSTEM), SBPS_NORMAL, 70);
		m_StatusBar.SetPaneInfo(STATUSBAR_POS_OPENMODE, m_StatusBar.GetItemID(STATUSBAR_POS_OPENMODE), SBPS_NORMAL, 24);
		m_bStatusBarCreated = true;
	}

	// We need to resize the dialog to make room for control bars.
	// First, figure out how big the control bars are.
	CRect rcClientStart;
	CRect rcClientNow;
	GetClientRect(rcClientStart);
	RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST,
	               0, reposQuery, rcClientNow);
	// Now move all the controls so they are in the same relative
	// position within the remaining client area as they would be
	// with no control bars.
	CPoint ptOffset(rcClientNow.left - rcClientStart.left,
	                rcClientNow.top - rcClientStart.top);
	CRect rcChild;
	CWnd *pwndChild = GetWindow(GW_CHILD);

	while (pwndChild)
	{
		pwndChild->GetWindowRect(rcChild);
		ScreenToClient(rcChild);
		rcChild.OffsetRect(ptOffset);
		pwndChild->MoveWindow(rcChild, FALSE);
		pwndChild = pwndChild->GetNextWindow();
	}

	// Adjust the dialog window dimensions
	CRect rcWindow;
	GetWindowRect(rcWindow);
	rcWindow.right += rcClientStart.Width() - rcClientNow.Width();
	rcWindow.bottom += rcClientStart.Height() - rcClientNow.Height();
	MoveWindow(rcWindow, FALSE);
	// And position the control bars
	RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST, 0);
	m_Resizer.Init(this);
	m_Resizer.AddCtrl(IDC_STATIC_PATH, DT_LEFT | DT_TOP | DT_RIGHT, false, true);
	m_Resizer.AddCtrl(IDC_LIST_CONTENT, DT_LEFT | DT_TOP | DT_RIGHT | DT_BOTTOM, false, false);
	m_Resizer.AddCtrl(IDC_STATIC_INFO, DT_LEFT | DT_TOP | DT_RIGHT | DT_BOTTOM, false, true);
	m_Resizer.AddCtrl(IDC_CHECK_BINEXTRACT, DT_LEFT | DT_BOTTOM, true, true);
	m_Resizer.AddCtrl(IDC_CHECK_LONGBIN, DT_LEFT | DT_BOTTOM, true, true);
	m_Resizer.AddCtrl(IDC_CHECK_LOGEXTRACT, DT_LEFT | DT_BOTTOM, true, true);
	m_Resizer.AddCtrl(IDC_BUTTON_CLOSE, DT_RIGHT | DT_BOTTOM, true, true);
	m_BKImage.AttachView(&m_ListControl);
	m_ListControl.Init(CBKListCtrl::MODE_CTRL::START);
	FillMenuButton();
	// m_CheckLongBin.SetCheck(BST_CHECKED); // включаем формат длинного бин
	OnBnClickedCheckLongbin();
	OnBnClickedCheckBinextract();
	m_nStartModeItemPos = 0;
	ChangeCurrDir();
	DisableControls();

	if (lstrlen(theApp.m_lpCmdLine)) // если в командной строке что-то есть,
	{
		// AfxMessageBox(theApp.m_lpCmdLine);
		CString str = CString(theApp.m_lpCmdLine);
		str.Trim();
		str.Trim(_T('"'));

		if (!str.IsEmpty()) // тут просто уберём кавычки, чтобы проверить, а не пустая ли командная строка?
		{
			PostMessage(WM_GET_CMDLINE); // сообщим, что это что-то надо бы попробовать открыть.
			// но если там какая-то херня, то прога будет ругаться.
		}
	}

	return TRUE;  // возврат значения TRUE, если фокус не передан элементу управления
}

BOOL CBKDEDlg::PreTranslateMessage(MSG *pMsg)
{
	if (pMsg->message == WM_KEYDOWN)
	{
		if (pMsg->wParam == VK_RETURN)
		{
			m_ListControl.OnKeyDown(UINT(pMsg->wParam), 0, 0);
			return TRUE;
		}
	}

	if (m_hAccelTable)
	{
		if (::TranslateAccelerator(m_hWnd, m_hAccelTable, pMsg))
		{
			return TRUE;
		}
	}

	return CDialogEx::PreTranslateMessage(pMsg);
}

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

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

void CBKDEDlg::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 CBKDEDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}

void CBKDEDlg::OnSize(UINT nType, int cx, int cy)
{
	CDialogEx::OnSize(nType, cx, cy);
	m_Resizer.OnSize(cx, cy);

	if (m_bStatusBarCreated)
	{
		// вместе с изменением размеров окна, изменяем размеры и положение статус бара
		// чтобы он всегда внизу окна был
		CRect rcWrect, rcSrect;
		GetClientRect(rcWrect);
		m_StatusBar.GetClientRect(rcSrect);
		const int w = rcWrect.Width();
		const int h = rcSrect.Height();
		const int x = rcSrect.left;
		const int y = rcWrect.bottom - h;
		m_StatusBar.SetWindowPos(nullptr, x, y, w, h, SWP_NOACTIVATE | SWP_NOZORDER);
	}
}


void CBKDEDlg::OnGetMinMaxInfo(MINMAXINFO *lpMMI)
{
	m_Resizer.GetMinMaxInfo(lpMMI);
}

void CBKDEDlg::OnClose()
{
	PreCloseStoreSettings();
	CDialogEx::OnClose();
}

void CBKDEDlg::OnBnClickedClose()
{
	PreCloseStoreSettings();
	EndDialog(IDOK);
}

void CBKDEDlg::OnBnClickedCheckBinextract()
{
	const bool bStatus = !!m_CheckBinExtract.GetCheck();
	m_BKImage.SetCheckBinExtractStatus(bStatus);
	m_CheckLongBin.EnableWindow(bStatus);
	m_ListControl.SetFocus();
}
void CBKDEDlg::OnBnClickedCheckLongbin()
{
	m_BKImage.SetCheckUseLongBinStatus(!!m_CheckLongBin.GetCheck());
	m_ListControl.SetFocus();
}
void CBKDEDlg::OnBnClickedCheckLogextract()
{
	m_BKImage.SetCheckLogExtractStatus(!!m_CheckLogExtract.GetCheck());
	m_ListControl.SetFocus();
}

void CBKDEDlg::OnSetFocus(CWnd *pOldWnd)
{
	CDialogEx::OnSetFocus(pOldWnd);
	m_ListControl.SetFocus();
}

int CBKDEDlg::ShowMessageBox(UINT strID, UINT nType)
{
	CString strMsg = CString(MAKEINTRESOURCE(strID));
	return ShowMessageBox(strMsg, nType);
}

int CBKDEDlg::ShowMessageBox(CString &str, UINT nType)
{
	CString strCapt = CString(MAKEINTRESOURCE(IDS_MSGCAPT));
	return MessageBox(str, strCapt, nType | MB_SETFOREGROUND);
}

void CBKDEDlg::PreUseLoadSettings()
{
	m_Settings.Init();
	m_Settings.LoadStringValue(CSettings::IMAGE_PATH, m_strImageDir);
	m_Settings.LoadStringValue(CSettings::STORE_PATH, m_strStorePath);
	m_Settings.LoadStringValue(CSettings::LOAD_PATH, m_strLoadPath);
	CString strStatus;
	m_Settings.LoadStringValue(CSettings::USE_LONGBIN, strStatus);

	if (strStatus.CompareNoCase(_T("Yes")) && strStatus.CompareNoCase(_T("1")))
	{
		m_CheckLongBin.SetCheck(BST_UNCHECKED);
	}
	else
	{
		m_CheckLongBin.SetCheck(BST_CHECKED);
	}

	OnBnClickedCheckLongbin();
	m_Settings.LoadStringValue(CSettings::USE_BIN_FORMAT, strStatus);

	if (strStatus.CompareNoCase(_T("Yes")) && strStatus.CompareNoCase(_T("1")))
	{
		m_CheckBinExtract.SetCheck(BST_UNCHECKED);
	}
	else
	{
		m_CheckBinExtract.SetCheck(BST_CHECKED);
	}

	OnBnClickedCheckBinextract();
	m_Settings.LoadStringValue(CSettings::USE_LOG_EXTRACT, strStatus);

	if (strStatus.CompareNoCase(_T("Yes")) && strStatus.CompareNoCase(_T("1")))
	{
		m_CheckLogExtract.SetCheck(BST_UNCHECKED);
	}
	else
	{
		m_CheckLogExtract.SetCheck(BST_CHECKED);
	}

	OnBnClickedCheckLogextract();
	m_Settings.Done();
}

void CBKDEDlg::PreCloseStoreSettings()
{
	m_Settings.Init();
	m_Settings.SaveStringValue(CSettings::IMAGE_PATH, m_strImageDir);
	m_Settings.SaveStringValue(CSettings::STORE_PATH, m_strStorePath);
	m_Settings.SaveStringValue(CSettings::LOAD_PATH, m_strLoadPath);
	CString strStatus = (!!m_CheckBinExtract.GetCheck() ? _T("Yes") : _T("No"));
	m_Settings.SaveStringValue(CSettings::USE_BIN_FORMAT, strStatus);
	strStatus = (!!m_CheckLongBin.GetCheck() ? _T("Yes") : _T("No"));
	m_Settings.SaveStringValue(CSettings::USE_LONGBIN, strStatus);
	strStatus = (!!m_CheckLogExtract.GetCheck() ? _T("Yes") : _T("No"));
	m_Settings.SaveStringValue(CSettings::USE_LOG_EXTRACT, strStatus);
	m_Settings.Done();
	m_BKImage.Close();

	if (m_ApplicationMenu)
	{
		DestroyMenu(m_ApplicationMenu);
		m_ApplicationMenu = nullptr;
	}
}

void CBKDEDlg::FillStatusBar()
{
	// выведем имя образа
	m_StatusBar.SetPaneText(STATUSBAR_POS_IMGNAME, m_strImgName);
	// выведем тип ОС
	CString str;
	str.LoadString(m_BKImage.GetImgFormatName());
	m_StatusBar.SetPaneText(STATUSBAR_POS_OSTYPE, str);
	// выведем размер
	CString strF = CString(MAKEINTRESOURCE(IDS_IMAGE_SIZE));
	str.Format(strF, m_sParseResult.nImageSize / 1024);
	m_StatusBar.SetPaneText(STATUSBAR_POS_IMGSIZE, str);
	// выведем загрузочный или нет
	str.Empty();

	if (m_sParseResult.bImageBootable)
	{
		str = CString(MAKEINTRESOURCE(IDS_IMGTYPE_SYSTEM));
	}

	m_StatusBar.SetPaneText(STATUSBAR_POS_SYSTEM, str);
	str = (m_BKImage.GetImageOpenStatus()) ? _T("R") : _T("RW");
	m_StatusBar.SetPaneText(STATUSBAR_POS_OPENMODE, str);
}

void CBKDEDlg::ClearStatusBar()
{
	m_StatusBar.SetPaneText(STATUSBAR_POS_IMGNAME, _T(""));
	m_StatusBar.SetPaneText(STATUSBAR_POS_OSTYPE, _T(""));
	m_StatusBar.SetPaneText(STATUSBAR_POS_IMGSIZE, _T(""));
	m_StatusBar.SetPaneText(STATUSBAR_POS_SYSTEM, _T(""));
	m_StatusBar.SetPaneText(STATUSBAR_POS_OPENMODE, _T(""));
}


void CBKDEDlg::EnableButton(UINT nID, BOOL bStatus)
{
	switch (nID)
	{
		case ID_CONTEXT_CHGADDR: // не диалоговый контрол.
			break;

		default:
			GetDlgItem(nID)->EnableWindow(bStatus);
	}

	m_BtnCurrStatus.insert_or_assign(nID, bStatus);
}


void CBKDEDlg::DisableControls()
{
	EnableButton(IDC_BUTTON_VIEW, FALSE);
	EnableButton(IDC_BUTTON_VIEW_AS_SPR, FALSE);
	EnableButton(IDC_BUTTON_ADD, FALSE);
	EnableButton(IDC_BUTTON_RENAME, FALSE);
	EnableButton(IDC_BUTTON_EXTRACT, FALSE);
	EnableButton(IDC_BUTTON_DELETE, FALSE);
	CString str = CString(MAKEINTRESOURCE(IDS_INFO_NOIMAGE));
	GetDlgItem(IDC_STATIC_INFO)->SetWindowText(str);
}

// разрешение/запрещение кнопок.
// вход: wParam - ID контрола
//      lParam - состояние: true - разрешить, false - запретить
LRESULT CBKDEDlg::OnSendEnableButton(WPARAM wParam, LPARAM lParam)
{
	const auto n = static_cast<UINT>(wParam);
	const auto f = static_cast<BOOL>(lParam);

	if (m_ButtonsStatus.count(n)) // если ключ есть
	{
		if (m_ButtonsStatus[n]) // и флаг разрешения есть
		{
			EnableButton(n, f); // то только тогда выполняем
		}
	}
	else
	{
		EnableButton(n, f); // если ключа нету, выполняем всегда
	}

	return S_OK;
}

// разрешение/запрещение кнопок.
// вход: wParam - ID контрола
//      lParam - состояние: true - разрешить, false - запретить
LRESULT CBKDEDlg::OnSendEnableButton2(WPARAM wParam, LPARAM lParam)
{
	const auto n = static_cast<UINT>(wParam);
	const auto f = static_cast<BOOL>(lParam);
	m_ButtonsStatus.insert(std::make_pair(n, f));
	EnableButton(n, f);
	return S_OK;
}

LRESULT CBKDEDlg::OnGetEnableButton(WPARAM wParam, LPARAM lParam)
{
	const auto n = static_cast<UINT>(wParam);
	const auto r = static_cast<LRESULT>(m_BtnCurrStatus[n]);
	return r;
}


void CBKDEDlg::OnBnClickedButtonAbout()
{
	CAboutDlg dlgAbout;
	dlgAbout.DoModal();
	m_ListControl.SetFocus();
}

void CBKDEDlg::OnBnClickedButtonOpen()
{
	CString strFilterImg = CString(MAKEINTRESOURCE(IDS_FILEFILTER_IMG));
	CFileDialog dlg(TRUE, nullptr, nullptr,
	                OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_NOCHANGEDIR | OFN_EXPLORER,
	                strFilterImg, this);

	if (!m_strImageDir.IsEmpty()) // зададим начальную директорию.
	{
		dlg.GetOFN().lpstrInitialDir = m_strImageDir.GetString();    // это будет прошлая директория
	}

	if (dlg.DoModal() == IDOK)
	{
		const CString strName = dlg.GetPathName();

		if (m_strImgName == strName)
		{
			// этот файл уже открыт, незачем второй раз открывать.
			return;
		}

		m_strImgName = strName;
		CString strPath, strDrive;
		_tsplitpath_s(strName, strDrive.GetBufferSetLength(_MAX_DIR), _MAX_DIR, strPath.GetBufferSetLength(_MAX_PATH), _MAX_PATH, nullptr, 0, nullptr, 0);
		strPath.ReleaseBuffer();
		strDrive.ReleaseBuffer();
		m_strImageDir = strDrive + strPath;
		OpenImage();
	}

	m_ListControl.SetFocus();
}

void CBKDEDlg::OnBnClickedButtonView()
{
	// акселераторам пофиг на статус disabled windows
	// они всё равно вызывают эти функции
	if (GetDlgItem(IDC_BUTTON_VIEW)->IsWindowEnabled())
	{
		m_BKImage.ViewSelected(true);
	}

	m_ListControl.SetFocus();
}

void CBKDEDlg::OnBnClickedButtonViewSprite()
{
	if (GetDlgItem(IDC_BUTTON_VIEW_AS_SPR)->IsWindowEnabled())
	{
		m_BKImage.ViewSelected(false);
	}

	m_ListControl.SetFocus();
}

void CBKDEDlg::OnBnClickedButtonAdd()
{
	if (GetDlgItem(IDC_BUTTON_ADD)->IsWindowEnabled())
	{
		CFFPickerDlg dlg(this);

		if (!m_strLoadPath.IsEmpty())
		{
			// установка каталога по умолчанию
			dlg.SetStartFolder(m_strLoadPath);
		}

		if (dlg.DoModal() == IDOK)
		{
			intptr_t hFile;
			struct sendfiledata f_file;
			bool bPathExtracted = false;
			CString strPath, strDrive;
			ADDOP_RESULT res;
			const INT_PTR nSel = dlg.m_SelectedItemList.GetCount();

			if (nSel)
			{
				CBKImage::ItemPanePos pp = m_BKImage.GetTopItemIndex();

				for (INT_PTR i = 0; i < nSel; ++i)
				{
					const CString str = dlg.m_SelectedItemList[i];

					if (!bPathExtracted)
					{
						bPathExtracted = true;
						_tsplitpath_s(str, strDrive.GetBufferSetLength(_MAX_DIR), _MAX_DIR, strPath.GetBufferSetLength(_MAX_PATH), _MAX_PATH, nullptr, 0, nullptr, 0);
						strPath.ReleaseBuffer();
						strDrive.ReleaseBuffer();
						m_strLoadPath = strDrive + strPath;
					}

					// GetFileAttributes(str.GetString());

					if ((hFile = _tfindfirst(str.GetString(), &f_file.findFile)) != -1L)
					{
						_findclose(hFile);
						f_file.strName = std::wstring(str.GetString());
						res = SendObject(f_file);

						if (res.bFatal)
						{
							break;
						}
					}
				}

				m_ListControl.DeleteAllItems();
				m_BKImage.ReadCurrentDir(pp);
			}
		}
	}

	m_ListControl.SetFocus();
}

void CBKDEDlg::OnBnClickedButtonRename()
{
	if (GetDlgItem(IDC_BUTTON_RENAME)->IsWindowEnabled())
	{
		const UINT uSelectedCount = m_ListControl.GetSelectedCount();
		int  nItem = -1;

		if (uSelectedCount > 0)
		{
			// если что-то выделенное есть
			for (UINT i = 0; i < uSelectedCount; ++i) // обработаем все выделенные элементы
			{
				nItem = m_ListControl.GetNextItem(nItem, LVNI_SELECTED);
				// и просто переименуем вручную, без всяких масок и шаблонов
				const auto fr = reinterpret_cast<AbstractFileRecord *>(m_ListControl.GetItemData(nItem));
				CRenameDlg dlg(fr->strName);

				if (dlg.DoModal() == IDOK)
				{
					const std::wstring newName = dlg.GetName();

					if (!newName.empty() && (newName != fr->strName))
					{
						fr->strName = newName;
						m_BKImage.RenameRecord(fr);
					}
				}
			}
		}
	}

	m_ListControl.SetFocus();
}

void CBKDEDlg::OnBnClickedButtonExtract()
{
	if (GetDlgItem(IDC_BUTTON_EXTRACT)->IsWindowEnabled())
	{
		const auto szOutPath = new TCHAR[_MAX_PATH];

		if (szOutPath == nullptr)
		{
			ShowMessageBox(IDS_ERR_NOTENOUGHT_MEMORY);
			return;
		}

		if (GetFolder(szOutPath))
		{
			m_strStorePath = CString(szOutPath);

			if (!m_strStorePath.IsEmpty())
			{
				m_strStorePath.TrimRight(_T('\\'));
				m_strStorePath += _T('\\'); // нормализуем путь - чтобы в конце всегда был '\'
			}

			m_BKImage.ExtractSelected(std::wstring(m_strStorePath));
		}

		delete[] szOutPath;
	}

	m_ListControl.SetFocus();
}

void CBKDEDlg::OnBnClickedButtonDelete()
{
	if (GetDlgItem(IDC_BUTTON_DELETE)->IsWindowEnabled())
	{
		m_BKImage.DeleteSelected();
	}

	m_ListControl.SetFocus();
}


// рекурсивно передаём всё, что можем
ADDOP_RESULT CBKDEDlg::SendObject(struct sendfiledata &f_file)
{
	ADDOP_RESULT res, dir_res;

	if (f_file.findFile.attrib & _A_SUBDIR)
	{
		// если директория
		if (lstrcmp(f_file.findFile.name, _T("..")) && lstrcmp(f_file.findFile.name, _T(".")))
		{
			// сперва передадим имя директории, по идее оно должно создаться в образе
			bool bDirExist = false;
l_repeatDir:
			dir_res = res = m_BKImage.AddObject(f_file, bDirExist); // добавим директорию

			if (res.bFatal)
			{
				// если фатальная - то просто выведем сообщение.
				goto l_fatal; // всё равно тут всё на goto построено. одним больше, одним меньше
			}
			else if (res.nError != ADD_ERROR::OK_NOERROR) // если какая-то ошибка
			{
				// два варианта - директории не поддерживаются и пытались создать уже существующую директорию
				switch (res.nImageErrorNumber)
				{
					case IMAGE_ERROR::FS_DIR_EXIST:
						bDirExist = true; // устанавливаем флаг игнорирования ошибки создания существующей директории
						goto l_repeatDir; // и повторим. Теоретически тут не должно быть зацикливания, т.к. эта ошибка уже никак не может возникнуть

					case IMAGE_ERROR::FS_NOT_SUPPORT_DIRS:
						res.nError = ADD_ERROR::OK_NOERROR; // игнорируем эту ошибку
						res.nImageErrorNumber = IMAGE_ERROR::OK_NOERRORS;
						break;

					default:
						OnSendErrorNum(WPARAM(MB_ICONERROR), static_cast<LPARAM>(res.nImageErrorNumber));
						ASSERT(FALSE); // что-то не так.
						return res;
				}
			}

			// затем заходим в директорию, и передаём всё содержимое.
			if (0 == _tchdir(f_file.strName.c_str()))
			{
				struct sendfiledata find;
				intptr_t hFile;

				if ((hFile = _tfindfirst(_T("*.*"), &find.findFile)) != -1L)
				{
					do
					{
						if (lstrcmp(find.findFile.name, _T("..")) && lstrcmp(find.findFile.name, _T(".")))
						{
							find.strName = f_file.strName + _T('\\') + std::wstring(find.findFile.name);
							res = SendObject(find);

							// здесь все ошибки уже обработаны, надо отлавливать фатальную ситуацию,
							// чтобы прервать цикл.
							if (res.bFatal)
							{
								break;
							}
						}
					}
					while (_tfindnext(hFile, &find.findFile) == 0);

					_findclose(hFile);
				}

				// теперь выйдем обратно
				_tchdir(_T(".."));
			}

			m_BKImage.OutFromDirObject(&dir_res.afr); // и выйдем из директории в образе
		}
	}
	else
	{
l_repeatFile:
		// если не директория
		res = m_BKImage.AddObject(f_file);

		// обработаем ошибки
		if (res.bFatal)
		{
l_fatal:

			// если фатальная - то просто выведем сообщение.
			if (res.nError == ADD_ERROR::IMAGE_ERROR)
			{
				OnSendErrorNum(WPARAM(MB_ICONERROR), static_cast<LPARAM>(res.nImageErrorNumber));
			}
			else
			{
				OnSendErrorNum(WPARAM(MB_ICONERROR), static_cast<LPARAM>(0x10000 | static_cast<int>(res.nError)));
			}
		}
		else if (res.nError != ADD_ERROR::OK_NOERROR) // если какая-то ошибка
		{
			// если не фатальная, то
			if (res.nImageErrorNumber == IMAGE_ERROR::FS_FILE_EXIST)
			{
				CString str;
				CString strf = CString(MAKEINTRESOURCE(IDS_INFO_FILE_EXIST));
				str.Format(strf, res.afr.strName.c_str());
				int definite = ShowMessageBox(str, MB_YESNOCANCEL | MB_ICONINFORMATION);

				switch (definite)
				{
					case IDYES:
					{
						// удаляем существующий файл и повторяем попытку
						ADDOP_RESULT res2 = m_BKImage.DeleteObject(&res.afr, false); // удаляем пока без форсирования

						// если файл удалить не получается
						if (res2.bFatal)
						{
							goto l_fatal;
						}
						else if (res2.nError != ADD_ERROR::OK_NOERROR)
						{
							if (res2.nImageErrorNumber == IMAGE_ERROR::FS_FILE_PROTECTED) // если удалить не получается из-за атрибутов
							{
								CString str;
								CString strf = CString(MAKEINTRESOURCE(IDS_INFO_FILE_PROTECT));
								str.Format(strf, res.afr.strName.c_str());
								int definite2 = ShowMessageBox(str, MB_YESNO | MB_ICONINFORMATION);

								switch (definite2)
								{
									case IDYES:
									{
										ADDOP_RESULT res3 = m_BKImage.DeleteObject(&res.afr, true);  // удаляем с форсированием

										if (res3.nError == ADD_ERROR::OK_NOERROR) // если удалилось
										{
											goto l_repeatFile; // то идём снова добавлять файл
										}
										else
										{
											goto l_fatal;
										}
									}

									case IDNO:
										goto l_cancelFile;
								}
							}
							else
							{
								goto l_fatal;
							}
						}
						else
						{
							goto l_repeatFile; // то идём снова добавлять файл
						}

						break;
					}

					case IDNO:
						// TODO: надо предложить переименовать файл. Нарисовать диалог и функционал редактирования Edit Control
						break;

					case IDCANCEL:
l_cancelFile:
						res.bFatal = true;
						res.nError = ADD_ERROR::USER_CANCEL;
						break;
				}
			}
			else
			{
				OnSendErrorNum(WPARAM(MB_ICONERROR), static_cast<LPARAM>(res.nImageErrorNumber));
				ASSERT(FALSE); // что-то не так.
			}
		}
	}

	return res;
}

void CBKDEDlg::OnDropFiles(HDROP hDropInfo)
{
	const UINT nFileCount = DragQueryFile(hDropInfo, 0xFFFFFFFF, nullptr, 0);

	if (nFileCount >= 1) // дропнули какие-то файлы
	{
		CString File;
		DragQueryFile(hDropInfo, 0, File.GetBufferSetLength(_MAX_PATH), _MAX_PATH); // получим первый дропнутый файл.
		// всё равно нельзя за раз открыть больше одного образа.
		File.ReleaseBuffer();
		DragFinish(hDropInfo);
		m_strImgName = File; // вот он наш файл
		OpenImage();
	}
}

// выбор образа из списка выведенных образов текущей директории
// вход - lParam указатель на имя файла
//      wParam - номер итема, на котором стоял курсор
LRESULT CBKDEDlg::OnSendImageProcessing(WPARAM wParam, LPARAM lParam)
{
	// получим гарантированно валидное имя
	m_nStartModeItemPos = static_cast<int>(wParam);
	const auto nType = static_cast<AbstractRecordType>((m_ListControl.GetItemData(m_nStartModeItemPos) >> 16) & 0xffff);

	if (nType == AbstractRecordType::FILE)
	{
		m_strImgName = m_strImageDir + *(reinterpret_cast<CString *>(lParam));
		OpenImage();
		return S_OK;
	}

	if (nType == AbstractRecordType::UP || nType == AbstractRecordType::DIR)
	{
		if (nType == AbstractRecordType::UP)
		{
			// выходим из директории
			m_strImageDir.TrimRight(_T('\\'));
			const int pos = m_strImageDir.ReverseFind(_T('\\'));

			if (pos > 0)
			{
				m_strImageDir = m_strImageDir.Left(pos + 1);
			}

			if (!m_vecSMIPos.empty())
			{
				m_nStartModeItemPos = m_vecSMIPos.back();
				m_vecSMIPos.pop_back();
			}
			else
			{
				m_nStartModeItemPos = 0;
			}
		}
		else
		{
			m_vecSMIPos.push_back(m_nStartModeItemPos);
			m_nStartModeItemPos = 0;
			// заходим внутрь директории
			m_strImageDir += *(reinterpret_cast<CString *>(lParam));
			m_strImageDir.TrimRight(_T('\\'));
			m_strImageDir += _T('\\');
		}

		m_ListControl.DeleteAllItems();
		ChangeCurrDir();
	}

	return S_OK;
}

void CBKDEDlg::ChangeCurrDir()
{
	if (ScanCurrDir())
	{
		if (m_nStartModeItemPos < 0)
		{
			m_nStartModeItemPos = 0;
		}

		m_ListControl.SetFocus();
		m_ListControl.SetItemState(m_nStartModeItemPos, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED);
		m_ListControl.SetHotItem(m_nStartModeItemPos);

		// и убедимся, чтобы выделенный элемент всегда был на виду
		if (!m_ListControl.EnsureVisible(m_nStartModeItemPos, FALSE))
		{
			CRect r;
			m_ListControl.GetItemRect(m_nStartModeItemPos, &r, LVIR_BOUNDS);
			m_ListControl.Scroll(CSize(0, r.Height()));
		}
	}
	else // если ничего не вывелось
	{
		m_ListControl.Init(CBKListCtrl::MODE_CTRL::MAIN); // переключимся в основной режим.
	}
}


LRESULT CBKDEDlg::OnGetCmdLine(WPARAM wParam, LPARAM lParam)
{
	m_strImgName = CString(theApp.m_lpCmdLine); // вот он наш файл. но вообще-то там может быть всё что угодно.
	OpenImage();
	return S_OK;
}

LRESULT CBKDEDlg::OnSendItemProcessing(WPARAM wParam, LPARAM lParam)
{
	m_BKImage.ItemProcessing(static_cast<int>(wParam), reinterpret_cast<AbstractFileRecord *>(lParam));
	return S_OK;
}

LRESULT CBKDEDlg::OnOutImage(WPARAM wParam, LPARAM lParam)
{
	if (m_BKImage.PopCurrentImg())
	{
		// вышли из логического диска
		// теперь надо получить базовое смещение и размер для текущего диска.
		// (предполагаем что возможна неограниченная вложенность лог. дисков друг в друга)
		const unsigned long nBaseOffset = m_BKImage.GetBaseOffset();
		m_sParseResult = m_ParserImage.ParseImage(std::wstring(m_strImgName), nBaseOffset);
		m_sParseResult.nImageSize = m_BKImage.GetImgSize();
		m_BKImage.ReOpen();
		FillStatusBar();
		return S_OK;
	}
	else
	{
		// в стеке пусто, значит совсем вышли из образа
		m_BKImage.Close();
		DisableControls();
		ClearStatusBar();
		m_strImgName.Empty();
		m_ListControl.Init(CBKListCtrl::MODE_CTRL::START);
		ChangeCurrDir();
	}

	return LRESULT(1L);
}

/*
операция захода в логический диск.
Входные параметры:
wParam - базовое смещение логического диска в образе
lParam - размер образа
*/
LRESULT CBKDEDlg::OnPutIntoLD(WPARAM wParam, LPARAM lParam)
{
	auto ret = LRESULT(1L);
	const auto nBaseOffset = static_cast<unsigned long>(wParam);
	m_sParseResult = m_ParserImage.ParseImage(std::wstring(m_strImgName.GetString()), nBaseOffset);
	/*
	для лог. дисков при выходе отсюда будет неправильный размер лог. диска.
	размер надо брать оттуда же, откуда брать базовое смещение для лог диска.
	ещё 1 проблема:
	1) для лог.диска корневой директорией должно быть имя лог.диска, и его надо где-то
	как-то хранить, куда-то передавать.
	*/
	m_sParseResult.nImageSize = static_cast<unsigned long>(lParam);

	if (m_sParseResult.imageOSType == IMAGE_TYPE::ERROR_NOIMAGE)
	{
		ShowMessageBox(IDS_MSG_IMGERROR);
	}
	else if (m_sParseResult.imageOSType == IMAGE_TYPE::UNKNOWN)
	{
		ShowMessageBox(IDS_MSG_IMGUNKNOWN);
	}
	else
	{
		// теперь, если образ опознался, надо создать объект, соответствующий ФС и вывести содержимое
		if (!m_BKImage.Open(m_sParseResult, true))
		{
			ShowMessageBox(IDS_ERR_NOTENOUGHT_MEMORY);
		}
		else
		{
			m_ListControl.DeleteAllItems();
			CBKImage::ItemPanePos pp(0, 0);
			m_BKImage.ReadCurrentDir(pp);
			ret = S_OK;
		}

		FillStatusBar();
	}

	return ret;
}


/*
Вход:
wParam - индекс иконки
lParam -
старшее слово - тип ошибки:
0 - IMAGE_ERROR_NUMS
1 - ADDOP_ERRORS
младшее слово - код ошибки.
*/
LRESULT CBKDEDlg::OnSendErrorNum(WPARAM wParam, LPARAM lParam)
{
	auto nIcon = static_cast<UINT>(wParam);

	if (nIcon == 0)
	{
		nIcon = MB_ICONEXCLAMATION;
	}

	int nErrType = HIWORD(lParam);

	switch (nErrType)
	{
		case 0:
		{
			const auto nErr = static_cast<IMAGE_ERROR>(LOWORD(lParam));

			if (nErr < IMAGE_ERROR::NUMBERS)
			{
				ShowMessageBox(CString(g_ImageErrorStr[static_cast<int>(nErr)].c_str()), MB_OK | nIcon);
			}
			else
			{
				ShowMessageBox(CString(_T("Ошибочный параметр в IMAGE_ERROR_NUMBER")), MB_OK | nIcon);
			}

			break;
		}

		case 1:
		{
			const auto nErr = static_cast<ADD_ERROR>(LOWORD(lParam));

			if (nErr < ADD_ERROR::NUMBERS)
			{
				ShowMessageBox(CString(g_AddOpErrorStr[static_cast<int>(nErr)].c_str()), MB_OK | MB_ICONERROR);
			}
			else
			{
				ShowMessageBox(CString(_T("Ошибочный параметр в ADDOP_ERRORS")), MB_OK | nIcon);
			}

			break;
		}

		default:
			ASSERT(FALSE);
			break;
	}

	return S_OK;
}

/*
Входные параметры:
wParam - индекс иконки
lParam - указатель на текст типа TCHAR*
Выход: результат мессагебокса, обычно - код нажатой кнопки
*/
LRESULT CBKDEDlg::OnSendMessageBox(WPARAM wParam, LPARAM lParam)
{
	auto nIcon = static_cast<UINT>(wParam);

	if (nIcon == 0)
	{
		nIcon = MB_OK | MB_ICONEXCLAMATION;
	}

	return LRESULT(ShowMessageBox(CString(reinterpret_cast<TCHAR *>(lParam)), nIcon));
}

LRESULT CBKDEDlg::OnOutCurrentFilePath(WPARAM wParam, LPARAM lParam)
{
	const auto pstr = reinterpret_cast<std::wstring *>(lParam);
	GetDlgItem(IDC_STATIC_PATH)->SetWindowText(pstr->c_str());
	return S_OK;
}

LRESULT CBKDEDlg::OnOutCurrentImageInfo(WPARAM wParam, LPARAM lParam)
{
	const auto pstr = reinterpret_cast<std::wstring *>(lParam);
	GetDlgItem(IDC_STATIC_INFO)->SetWindowText(pstr->c_str());
	return LRESULT();
}

LRESULT CBKDEDlg::OnMakeDrop(WPARAM wParam, LPARAM lParam)
{
	if (m_BKImage.IsImageOpen())
	{
		const auto hDropInfo = reinterpret_cast<HDROP>(lParam);
		const UINT nFileCount = DragQueryFile(hDropInfo, -1, nullptr, 0);
		UINT n = 0;
		intptr_t hFile;
		struct sendfiledata f_file;
		ADDOP_RESULT res;
		CBKImage::ItemPanePos pp = m_BKImage.GetTopItemIndex();

		while (n < nFileCount) // дропнули какие-то файлы
		{
			CString File;
			DragQueryFile(hDropInfo, n, File.GetBufferSetLength(_MAX_PATH), _MAX_PATH); // получим очередной дропнутый файл.
			File.ReleaseBuffer();

			if ((hFile = _tfindfirst(File.GetString(), &f_file.findFile)) != -1L)
			{
				_findclose(hFile);
				f_file.strName = std::wstring(File.GetString());
				res = SendObject(f_file);
				// здесь все ошибки уже обработаны, надо отлавливать фатальную ситуацию,
				// чтобы прервать цикл.
			}

			if (res.bFatal)
			{
				break;
			}

			n++;
		}

		m_ListControl.DeleteAllItems();
		m_BKImage.ReadCurrentDir(pp);
		m_ListControl.SetFocus();
		DragFinish(hDropInfo);
	}
	else
	{
		OnSendErrorNum(WPARAM(MB_ICONERROR), static_cast<LPARAM>(0x10000 | static_cast<int>(ADD_ERROR::IMAGE_NOT_OPEN)));
	}

	return S_OK;
}


/*
операция выбора/открытия нового файла образа.
*/
void CBKDEDlg::OpenImage()
{
	m_strImgName.Trim();
	m_strImgName.Trim(_T('"')); // перед открытием надо убрать кавычки, иначе файл с полным путём считается
	// именем файла и добавляется к пути по умолчанию
//  AfxMessageBox(m_strImgPathName);
	m_sParseResult = m_ParserImage.ParseImage(std::wstring(m_strImgName), 0);

	if (m_sParseResult.imageOSType == IMAGE_TYPE::ERROR_NOIMAGE)
	{
		ShowMessageBox(IDS_MSG_IMGERROR);
	}
	else if (m_sParseResult.imageOSType == IMAGE_TYPE::UNKNOWN)
	{
		ShowMessageBox(IDS_MSG_IMGUNKNOWN);
	}
	else
	{
		m_ListControl.Init(CBKListCtrl::MODE_CTRL::MAIN);
		// теперь, если образ опознался, надо создать объект, соответствующий ФС и вывести содержимое
		m_BKImage.ClearImgVector();

		if (m_BKImage.Open(m_sParseResult))
		{
			CBKImage::ItemPanePos pp(0, 0);
			m_BKImage.ReadCurrentDir(pp);
		}
		else
		{
			ShowMessageBox(IDS_ERR_NOTENOUGHT_MEMORY);
		}

		FillStatusBar();
	}
}

int CALLBACK CBKDEDlg::BrowseCallbackProc(HWND hWnd, UINT uMsg, LPARAM lParam, LPARAM lpData)
{
	TCHAR szPath[_MAX_PATH];

	switch (uMsg)
	{
		case BFFM_INITIALIZED:
			if (lpData)
			{
				::SendMessage(hWnd, BFFM_SETSELECTION, TRUE, lpData);
			}

			break;

		case BFFM_SELCHANGED:
			SHGetPathFromIDList(LPITEMIDLIST(lParam), szPath);
			::SendMessage(hWnd, BFFM_SETSTATUSTEXT, WPARAM(0), reinterpret_cast<LPARAM>(szPath));
			break;
	}

	return 0;
}

// диалог выбора директории для сохранения извлекаемых файлов
bool CBKDEDlg::GetFolder(LPTSTR szPath)
{
	szPath[0] = 0;
	bool result = false;
	LPMALLOC pMalloc;

	if (::SHGetMalloc(&pMalloc) == NOERROR)
	{
		BROWSEINFO bi;
		::ZeroMemory(&bi, sizeof bi);
		bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_SHAREABLE | BIF_USENEWUI | BIF_UAHINT | BIF_NEWDIALOGSTYLE | BIF_BROWSEINCLUDEURLS; // показывать файлы -  | BIF_BROWSEINCLUDEFILES | BIF_BROWSEFILEJUNCTIONS;
		// дескриптор окна-владельца диалога
		bi.hwndOwner = GetSafeHwnd();
		// добавление заголовка к диалогу
		CString strStr = MAKEINTRESOURCE(IDS_SELFLDRTTL);
		bi.lpszTitle = strStr.GetString();
		// отображение текущего каталога
		bi.lpfn      = BrowseCallbackProc;
		bi.ulFlags  |= BIF_STATUSTEXT;

		// установка каталога по умолчанию
		if (!m_strStorePath.IsEmpty())
		{
			// установка каталога по умолчанию
			bi.lParam = reinterpret_cast<LPARAM>(m_strStorePath.GetString());
		}

		// установка корневого каталога
		/* не нужно. и так как надо хорошо показывает. но вдруг пригодится где-нибудь.
		IShellFolder *pDF;
		if (SHGetDesktopFolder(&pDF) == NOERROR)
		{
		    LPITEMIDLIST pIdl = nullptr;
		    ULONG        chEaten;
		    ULONG        dwAttributes;

		    USES_CONVERSION;
		    LPOLESTR oleStr = T2OLE(_T("C:"));

		    pDF->ParseDisplayName(nullptr ,nullptr, oleStr, &chEaten, &pIdl, &dwAttributes);
		    pDF->Release();

		    bi.pidlRoot = pIdl;
		} */
		LPITEMIDLIST pidl = ::SHBrowseForFolder(&bi);

		if (pidl != nullptr)
		{
			if (::SHGetPathFromIDList(pidl, szPath))
			{
				result = true;
			}

			pMalloc->Free(pidl);
		}

		/*
		if (bi.pidlRoot != nullptr)
		{
		    pMalloc->Free((void*)bi.pidlRoot);
		}*/
		pMalloc->Release();
	}

	return result;
}

bool CBKDEDlg::ScanCurrDir()
{
	struct _tfinddata_t scanFile;
	intptr_t hFile;
	TCHAR pExt[_MAX_EXT];

	// если начальная директория, где лежат образы пуста
	if (m_strImageDir.IsEmpty())
	{
		return false; // то ничего не делать
	}

	// зайдём в нужный каталог
	if (_tchdir(m_strImageDir) != 0)
	{
		return false; // если не смогли, то просто выйдем
	}

	bool bOut = false; // флаг, что хоть что-то вывели
	// отобразим путь к директории, которую будем отображать
	GetDlgItem(IDC_STATIC_PATH)->SetWindowText(m_strImageDir);

	if ((hFile = _tfindfirst(_T("*.*"), &scanFile)) == -1L)
	{
		// в каталоге пусто. Вообще пусто.
	}
	else
	{
		int item = 0;

		do
		{
			if (scanFile.attrib & _A_SUBDIR)
			{
				// все директории игнорируем
				if (CString(scanFile.name).Compare(L".") == 0)
				{
					continue;
				}

				m_ListControl.InsertItem(item, scanFile.name);

				if (CString(scanFile.name).Compare(L"..") == 0)
				{
					m_ListControl.SetItemText(item, CBKListCtrl::LC_SIZE_ST, g_strUp.c_str());
					m_ListControl.SetItemData(item, static_cast<DWORD_PTR>((static_cast<int>(AbstractRecordType::UP) << 16) | item));
				}
				else
				{
					m_ListControl.SetItemText(item, CBKListCtrl::LC_SIZE_ST, g_strDir.c_str());
					m_ListControl.SetItemData(item, static_cast<DWORD_PTR>((static_cast<int>(AbstractRecordType::DIR) << 16) | item));
				}

				item++;
				bOut = true;
			}
			else
			{
				// если файл - достанем расширение
				_tsplitpath_s(scanFile.name, nullptr, 0, nullptr, 0, nullptr, 0, pExt, _MAX_EXT);

				for (const auto &pstrExt : g_pstrExts)  // теперь по списку известных расширений
				{
					if (CString(pExt).CompareNoCase(pstrExt.c_str()) == 0) // если совпало
					{
						// нужно определить что это.
						const PARSE_RESULT pr = m_ParserImage.ParseImage(std::wstring(scanFile.name), 0);
						CString str;
						m_ListControl.InsertItem(item, scanFile.name);
						str.Format(_T("%d"), pr.nImageSize);
						m_ListControl.SetItemText(item, CBKListCtrl::LC_SIZE_ST, str.GetString());
						str.LoadString(m_BKImage.GetImgFormatName(pr.imageOSType));
						m_ListControl.SetItemText(item, CBKListCtrl::LC_OSTYPE_ST, str.GetString());
						m_ListControl.SetItemText(item, CBKListCtrl::LC_SYSTYPE_ST, (pr.bImageBootable ? _T("Да") : _T("Нет")));
						m_ListControl.SetItemData(item, static_cast<DWORD_PTR>((static_cast<int>(AbstractRecordType::FILE) << 16) | item));
						item++;
						bOut = true;
						break;
					}
				}
			}
		}
		while (_tfindnext(hFile, &scanFile) == 0);

		_findclose(hFile);
		m_ListControl.SortItems(CBKListCtrl::MyCompareProc2, reinterpret_cast<DWORD_PTR>(&m_ListControl));
	}

	return bOut;
}


BOOL CBKDEDlg::OnHelpInfo(HELPINFO *pHelpInfo)
{
	// чтоб не вылазила справка, сделаем так
	return FALSE; // CDialogEx::OnHelpInfo(pHelpInfo);
}




////////////////////////////////////////////////////////// /
// крайне экспериментальная часть,
// которую придётся удалить, если ничего не получится.

// тут надо немного улучшить код и универсальности добавить


void CBKDEDlg::FillMenuButton()
{
	static TCHAR *pMenu[2] =
	{
		L"A:\0",
		L"B:\0",
	};
	// Load application list into menu button
	m_ApplicationMenu = CreatePopupMenu();
	MENUITEMINFO MenuInfo;
	memset(&MenuInfo, 0, sizeof(MENUITEMINFO));
	MenuInfo.cbSize = sizeof(MENUITEMINFO);
	MenuInfo.fMask = MIIM_ID | MIIM_STRING | MIIM_DATA;
	MenuInfo.wID = 1;

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

	// Attach menu to CMFCMenuButton
	auto m_ApplicationList = reinterpret_cast<CMFCMenuButton *>(GetDlgItem(IDC_MFCMENUBUTTON_OPEN));
	m_ApplicationList->m_bOSMenu = FALSE;
	m_ApplicationList->m_bRightArrow = FALSE;
	m_ApplicationList->m_bStayPressed = TRUE;
	m_ApplicationList->m_bDefaultClick = TRUE;
	m_ApplicationList->m_hMenu = m_ApplicationMenu;
}

void CBKDEDlg::OnBnClickedMfcmenubuttonOpen()
{
	auto m_ApplicationList = reinterpret_cast<CMFCMenuButton *>(GetDlgItem(IDC_MFCMENUBUTTON_OPEN));
	int nDrive = m_ApplicationList->m_nMenuResult;

	if (nDrive == 0)
	{
		return OnBnClickedButtonOpen();
	}

	nDrive--; // номер привода.
	wchar_t szDevice[32];
	DWORD dwVersion = 0;
	DWORD dwRet;
	HANDLE h = CreateFile(L"\\\\.\\fdrawcmd", GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr);

	if (h != INVALID_HANDLE_VALUE)
	{
		DeviceIoControl(h, IOCTL_FDRAWCMD_GET_VERSION, nullptr, 0, &dwVersion, sizeof(dwVersion), &dwRet, nullptr);
		CloseHandle(h);
	}

	bool bFDRaw = true;

	if (!dwVersion)
	{
		// ("fdrawcmd.sys не установлен, смотрите: http://simonowen.com/fdrawcmd/\n");
		bFDRaw = false;
	}
	else if (HIWORD(dwVersion) != HIWORD(FDRAWCMD_VERSION))
	{
		// ("Установленный fdrawcmd.sys не совместим с этой программой.\n");
		bFDRaw = false;
	}

	if (bFDRaw)
	{
		wsprintf(szDevice, L"\\\\.\\fdraw%u", nDrive);
		h = CreateFile(szDevice, GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr);

		if (h == INVALID_HANDLE_VALUE)
		{
			// error
			bFDRaw = false;
		}
		else
		{
			int DISK_DATARATE = FD_RATE_250K;          // 2 is 250 kbit/sec
			DeviceIoControl(h, IOCTL_FD_SET_DATA_RATE, &DISK_DATARATE, sizeof(DISK_DATARATE), nullptr, 0, &dwRet, nullptr);
			CloseHandle(h);
		}
	}

	// вот такая штука, чтобы без goto обойтись.
	if (!bFDRaw)
	{
		wchar_t szDrive[4] = L"A:\0";
		szDrive[0] += nDrive;
		wsprintf(szDevice, L"\\\\.\\%s", szDrive);
	}

	// !!!оказывается \\\\.\\fdraw через  fopen  не открывается!!! только через HANDLE
	m_strImgName = CString(szDevice);
	OpenImage();
	m_ListControl.SetFocus();
}



void CBKDEDlg::OnContextMenuRename()
{
	OnBnClickedButtonRename();
}



void CBKDEDlg::OnContextChangeAddr()
{
	const size_t uSelectedCount = m_ListControl.GetSelectedCount();
	int  nItem = -1;

	if (uSelectedCount > 0)
	{
		// если что-то выделенное есть
		for (size_t i = 0; i < uSelectedCount; ++i) // обработаем все выделенные элементы
		{
			nItem = m_ListControl.GetNextItem(nItem, LVNI_SELECTED);
			// и просто переименуем вручную, без всяких масок и шаблонов
			const auto fr = reinterpret_cast<AbstractFileRecord *>(m_ListControl.GetItemData(nItem));

			if (fr->nAttr & (FR_ATTR::DIR | FR_ATTR::LINK | FR_ATTR::LOGDISK))
			{
				// для этих нельзя поменять адрес
			}
			else
			{
				CChangeAddrDlg dlg(fr->nAddress);

				if (dlg.DoModal() == IDOK)
				{
					const int newAddr = dlg.GetAddr();

					if (newAddr != fr->nAddress)
					{
						fr->nAddress = newAddr;
						m_BKImage.RenameRecord(fr);
					}
				}
			}
		}
	}

	m_ListControl.SetFocus();
}


void CBKDEDlg::OnContextDelete()
{
	OnBnClickedButtonDelete();
}


void CBKDEDlg::OnContextExtract()
{
	OnBnClickedButtonExtract();
}


void CBKDEDlg::OnContextViewastext()
{
	OnBnClickedButtonView();
}


void CBKDEDlg::OnContextViewassprite()
{
	OnBnClickedButtonViewSprite();
}
