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

#include "stdafx.h"
#include "BKDE.h"
#include "BKDEDlg.h"
#include "afxdialogex.h"
#include <afxhtml.h>
// #include <shlobj.h>
// #pragma comment(lib,"shell32")


#ifdef _DEBUG
#define new DEBUG_NEW
#endif


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

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

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

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

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

CAboutDlg::CAboutDlg()
	: CDialog(CAboutDlg::IDD)
	, m_Ie(nullptr)
{
}


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

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
END_MESSAGE_MAP()


BOOL CAboutDlg::OnInitDialog()
{
	CDialog::OnInitDialog();
	DWORD dwHandle;
	CString name = CString(theApp.m_pszAppName) + _T(".exe");
	uint32_t 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 Disk Explorer v%d.%d.%d.%d "),
			         HIWORD(lpfi->dwFileVersionMS), LOWORD(lpfi->dwFileVersionMS),
			         HIWORD(lpfi->dwFileVersionLS), LOWORD(lpfi->dwFileVersionLS));
#ifdef _WIN64
			s += _T("x64");
#else
			s += _T("x86");
#endif
			GetDlgItem(IDC_STATIC_VERS)->SetWindowText(s);
			delete [] lpData;
		}
		else
		{
			GetDlgItem(IDC_STATIC_VERS)->SetWindowText(_T("BK Disk Explorer v???"));
		}
	}

	CRect rect;
	GetDlgItem(IDC_STATIC_ABOUT)->GetWindowRect(&rect);
	ScreenToClient(&rect);
	m_Ie = (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
}


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


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

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

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

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_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipText)
	ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipText)
	ON_MESSAGE(WM_MAKE_DROP, &OnMakeDrop)
	ON_MESSAGE(WM_GET_CMDLINE, &OnGetCmdLine)
	ON_MESSAGE(WM_OUT_OF_IMAGE, &OnOutImage)
	ON_MESSAGE(WM_PUT_INTO_LOG, &OnPutIntoLog)
	ON_MESSAGE(WM_OUT_CURR_FILE_PATH, &OnOutCurrFilePath)
	ON_MESSAGE(WM_OUT_CURR_IMG_INFO, &OnOutCurrDiskInfo)
	ON_MESSAGE(WM_ENABLE_BUTTON_ADD, &OnEnableButtonAdd)
	ON_MESSAGE(WM_ENABLE_BUTTON_VIEW, &OnEnableButtonView)
	ON_MESSAGE(WM_ENABLE_BUTTON_EXTR, &OnEnableButtonExtract)
	ON_MESSAGE(WM_SEND_ERRORNUM, &OnSendErrorNum)
	ON_COMMAND(IDR_BUTTON_OPEN, &OnBnClickedButtonOpen)
	ON_COMMAND(IDR_BUTTON_EXTRACTFILES, &OnBnClickedButtonExtract)
	ON_COMMAND(IDR_BUTTON_ADDFILES, &OnBnClickedButtonAdd)
	ON_COMMAND(IDR_BUTTON_VIEWASTEXT, &OnBnClickedButtonView)
	ON_COMMAND(IDR_BUTTON_VIEWASIMAGE, &OnBnClickedButtonViewSprite)
	ON_COMMAND(IDR_BUTTON_ABOUT, &OnBnClickedButtonAbout)
	ON_BN_CLICKED(IDC_BUTTON_CLOSE, &OnBnClickedButtonClose)
	ON_BN_CLICKED(IDC_CHECK_BINEXTRACT, &OnBnClickedCheckBinextract)
	ON_BN_CLICKED(IDC_CHECK_LOGEXTRACT, &OnBnClickedCheckLogextract)
END_MESSAGE_MAP()


// обработчики сообщений 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)
	{
		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);        // Мелкий значок
	m_hAccelTable = LoadAccelerators(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDR_ACCELERATOR1));
	m_strImgPathName = _T("");
	PreUseLoadSettings();

	if (!m_ToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC)
	        || !m_ToolBar.LoadToolBar(IDR_MAINFRAME))
	{
		TRACE0("Failed to create toolbar\n");
		return -1;      // fail to create
	}

	m_ToolBar.LoadTrueColorToolBar(32,
	                               IDB_TOOLBAR_DRAW,
	                               IDB_TOOLBAR_DRAW_HOT,
	                               IDB_TOOLBAR_DRAW_DISABLED);

	// Create status bar at the bottom of the dialog window
	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_ListControl.Init();
	//и запретим кнопки, ибо пока нефиг
	DisableControls();
	//это для инициализации resizable dialog
	m_Resizer.Init(this);
	m_Resizer.AddCtrl(IDC_LIST_CONTENT, DT_LEFT | DT_TOP | DT_RIGHT | DT_BOTTOM, false, false);
	m_Resizer.AddCtrl(IDC_STATIC_PATH, DT_LEFT | DT_TOP | DT_RIGHT, false, true);
	m_Resizer.AddCtrl(IDC_CHECK_BINEXTRACT, 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_Resizer.AddCtrl(IDC_STATIC_INFO, DT_LEFT | DT_TOP | DT_RIGHT | DT_BOTTOM, false, true);

	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, если фокус не передан элементу управления
}

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);
		int w = rcWrect.Width();
		int h = rcSrect.Height();
		int x = rcSrect.left;
		int y = rcWrect.bottom - h;
		m_StatusBar.SetWindowPos(nullptr, x, y, w, h, SWP_NOACTIVATE | SWP_NOZORDER);
	}
}

BOOL CBKDEDlg::PreTranslateMessage(MSG *pMsg)
{
	if ((pMsg->message == WM_KEYDOWN) && (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::OnSetFocus(CWnd *pOldWnd)
{
	CDialogEx::OnSetFocus(pOldWnd);
	m_ListControl.SetFocus();
}

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

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

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

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

void CBKDEDlg::OnBnClickedCheckBinextract()
{
	m_ListControl.SetCheckBinExtractStatus(!!m_CheckBinExtract.GetCheck());
}

void CBKDEDlg::OnBnClickedCheckLogextract()
{
	m_ListControl.SetCheckLogExtractStatus(!!m_CheckLogExtract.GetCheck());
}

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 !!
	TOOLTIPTEXTA *pTTTA = reinterpret_cast<TOOLTIPTEXTA *>(pNMHDR);
	TOOLTIPTEXTW *pTTTW = reinterpret_cast<TOOLTIPTEXTW *>(pNMHDR);
	TCHAR szFullText[512];
	CString strTipText;
	UINT nID = UINT(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(HWND(nID));
	}

	if (nID != 0) // will be zero on a separator
	{
		AfxLoadString(nID, szFullText, 512);
		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;
}


LRESULT CBKDEDlg::OnEnableButtonAdd(WPARAM wParam, LPARAM lParam)
{
	EnableButton(IDR_BUTTON_ADDFILES, !!wParam);
	return S_OK;
}

LRESULT CBKDEDlg::OnEnableButtonExtract(WPARAM wParam, LPARAM lParam)
{
	EnableButton(IDR_BUTTON_EXTRACTFILES, !!wParam);
	return S_OK;
}

LRESULT CBKDEDlg::OnEnableButtonView(WPARAM wParam, LPARAM lParam)
{
	EnableButton(IDR_BUTTON_VIEWASTEXT, !!wParam);
	EnableButton(IDR_BUTTON_VIEWASIMAGE, !!wParam);
	return S_OK;
}

LRESULT CBKDEDlg::OnOutCurrFilePath(WPARAM wParam, LPARAM lParam)
{
	CString *str = reinterpret_cast<CString *>(lParam);
	GetDlgItem(IDC_STATIC_PATH)->SetWindowText(*str);
	return S_OK;
}

LRESULT CBKDEDlg::OnOutCurrDiskInfo(WPARAM wParam, LPARAM lParam)
{
	CString *str = reinterpret_cast<CString *>(lParam);
	GetDlgItem(IDC_STATIC_INFO)->SetWindowText(*str);
	return S_OK;
}

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

LRESULT CBKDEDlg::OnOutImage(WPARAM wParam, LPARAM lParam)
{
	if (m_ListControl.PopCurrentImg())
	{
		//вышли из логического диска
		//теперь надо получить базовое смещение и размер для текущего диска.
		//(предполагаем что возможна неограниченная вложенность лог. дисков друг в друга)
		ULONG nBaseOffset = m_ListControl.GetBaseOffset();
		m_sParseResult = m_oParseImage.ParseImage(m_strImgPathName, nBaseOffset);
		m_sParseResult.nImageSize = m_ListControl.GetImgSize();
		m_ListControl.ResetImgName(m_sParseResult);
		FillStatusBar();
	}
	else
	{
		//в стеке пусто, значит совсем вышли из образа
		m_ListControl.CloseImage();
		DisableControls();
		ClearStatusBar();
		m_strImgPathName.Empty();
	}

	return S_OK;
}

/*
операция захода в логический диск.
*/
LRESULT CBKDEDlg::OnPutIntoLog(WPARAM wParam, LPARAM lParam)
{
	long nBaseOffset = (long)wParam;
	m_ListControl.CloseCurrentImage(); //чтобы не мешать самим себе. надо закрывать файл.
	//т.к. CFile::shareDenyWrite так же запрещает и чтение но только самому себе, что странно.
	//дублирование объекта не прокатывает, т.к. при начальном открытии образа дублировать нечего.
	m_sParseResult = m_oParseImage.ParseImage(m_strImgPathName, nBaseOffset);
	/*
	для лог. дисков при выходе отсюда будет неправильный размер лог. диска.
	размер надо брать оттуда же, откуда брать базовое смещение для лог диска.
	ещё 1 проблема:
	1) для лог.диска корневой директорией должно быть имя лог.диска, и его надо где-то
	как-то хранить, куда-то передавать.
	*/
	m_sParseResult.nImageSize = (ULONG)lParam;

	if (m_sParseResult.imageOSType == IMAGE_TYPE_ERROR)
	{
		ShowMessageBox(IDS_MSG_IMGERROR);
	}
	else if (m_sParseResult.imageOSType == IMAGE_TYPE_UNKNOWN)
	{
		ShowMessageBox(IDS_MSG_IMGUNKNOWN);
	}
	else
	{
		//теперь, если образ опознался, надо создать объект, соответствующий ФС и вывести содержимое
		m_ListControl.PushCurrentImg();

		if (!m_ListControl.SetImgName(m_sParseResult, true))
		{
			ShowMessageBox(IDS_ERR_NOTENOUGHT_MEMORY);
		}

		FillStatusBar();
	}

	return S_OK;
}

LRESULT CBKDEDlg::OnMakeDrop(WPARAM wParam, LPARAM lParam)
{
	HDROP hDropInfo = reinterpret_cast<HDROP>(lParam);
	FILESAVE_STATUS fsst = FS_NO_ERROR;
	UINT nFileCount = DragQueryFile(hDropInfo, 0xFFFFFFFF, nullptr, 0);
	UINT n = 0;

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

		if (fsst != FS_NO_ERROR)
		{
			break;
		}

		n++;
	}

	ShowAddFileStatus(fsst);
	DragFinish(hDropInfo);
	return S_OK;
}
/*
Вход: wParam - номер ошибки IMAGE_ERROR_NUMS
*/
LRESULT CBKDEDlg::OnSendErrorNum(WPARAM wParam, LPARAM lParam)
{
	IMAGE_ERROR_NUMS nErr = (IMAGE_ERROR_NUMS)wParam;

	if (nErr < IMAGE_ERROR_NUMBER)
	{
		ShowMessageBox(g_ImageErrorStr[nErr], MB_OK | MB_ICONEXCLAMATION);
	}

	return S_OK;
}

void CBKDEDlg::EnableButton(UINT nID, bool bStatus)
{
	int nIndex = m_ToolBar.GetToolBarCtrl().CommandToIndex(nID); //IDR_BUTTON_ADDFILES IDR_BUTTON_EXTRACTFILES IDR_BUTTON_VIEWASTEXT IDR_BUTTON_VIEWASIMAGE

	if (bStatus)
	{
		m_ToolBar.SetButtonStyle(nIndex, m_ToolBar.GetButtonStyle(nIndex) & ~TBBS_DISABLED);
	}
	else
	{
		m_ToolBar.SetButtonStyle(nIndex, m_ToolBar.GetButtonStyle(nIndex) | TBBS_DISABLED);
	}
}


void CBKDEDlg::DisableControls()
{
	EnableButton(IDR_BUTTON_VIEWASIMAGE, false);
	EnableButton(IDR_BUTTON_VIEWASTEXT, false);
	EnableButton(IDR_BUTTON_EXTRACTFILES, false);
	EnableButton(IDR_BUTTON_ADDFILES, false);
	CString str;
	str.LoadString(IDS_INFO_NOIMAGE);
	GetDlgItem(IDC_STATIC_INFO)->SetWindowText(str);
}

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

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

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

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

		m_strImgPathName = strName;
		TCHAR pPath[_MAX_PATH];
		TCHAR pDrive[_MAX_DIR];
		_tsplitpath_s(strName, pDrive, _MAX_DIR, pPath, _MAX_PATH, nullptr, 0, nullptr, 0);
		m_strImgPathDir = CString(pDrive) + CString(pPath);
		OpenImage();
	}
}

void CBKDEDlg::OnDropFiles(HDROP hDropInfo)
{
	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_strImgPathName = File; //вот он наш файл
		OpenImage();
	}
}

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

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

		if (!m_ListControl.SetImgName(m_sParseResult, FALSE))
		{
			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), 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;
		// дескриптор окна-владельца диалога
		bi.hwndOwner = GetSafeHwnd();
		// добавление заголовка к диалогу
		CString strStr;
		strStr.LoadString(IDS_SELFLDRTTL);
		bi.lpszTitle = strStr.GetBuffer();
		// отображение текущего каталога
		bi.lpfn      = BrowseCallbackProc;
		bi.ulFlags  |= BIF_STATUSTEXT;

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

		// установка корневого каталога
		/* не нужно. и так как надо хорошо показывает. но вдруг пригодится где-нибудь.
		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;
}

void CBKDEDlg::GetFiles()
{
	CFileDialog dlg(
	    TRUE, //Set it to TRUE to construct a File Open dialog box. Set it to FALSE to construct a File Save As dialog box.
	    nullptr, //The default file name extension.
	    nullptr, //The initial file name that appears in the Filename box. If nullptr, no initial file name appears.
	    OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_ALLOWMULTISELECT | OFN_ENABLESIZING | OFN_EXPLORER, //Flags
	    nullptr, //Filters
	    this //A pointer to the parent or owner window of the file dialog box
	);
	int MaxFile = 2562;
	auto *pc = new TCHAR[MaxFile];

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

	ZeroMemory(pc, MaxFile);
	dlg.GetOFN().nMaxFile = MaxFile;
	dlg.GetOFN().lpstrFile = pc;

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

	FILESAVE_STATUS fsst = FS_NO_ERROR;

	if (dlg.DoModal() == IDOK)
	{
//      CString strSize;
//      HANDLE hSizeFile;
//      WIN32_FIND_DATA fileData;
		bool bPathExtracted = false;
		CString str;
		TCHAR pPath[_MAX_PATH];
		TCHAR pDrive[_MAX_DIR];
		int i = 0; //счётчик добавленных файлов
		POSITION pos = dlg.GetStartPosition();

		do
		{
			str = dlg.GetNextPathName(pos);

			if (!bPathExtracted)
			{
				bPathExtracted = true;
				_tsplitpath_s(str, pDrive, _MAX_DIR, pPath, _MAX_PATH, nullptr, 0, nullptr, 0);
				m_strLoadPath = CString(pDrive) + CString(pPath);
			}

//          hSizeFile = FindFirstFile(str, &fileData);
//          strSize.Format(_T("%d KB"), fileData.nFileSizeLow / 1024 + 1);
//          FindClose(hSizeFile);
//          AfxMessageBox(_T("File: ")+str+ _T("\nSize: ")+ strSize);
			fsst = m_ListControl.AddFile(str);

			if (fsst != FS_NO_ERROR)
			{
				break;
			}

			i++;
		}
		while (pos);

		ShowAddFileStatus(fsst);
	}

	delete [] pc;
}

void CBKDEDlg::ShowAddFileStatus(FILESAVE_STATUS fsst)
{
	UINT uMsgId = 0;

	switch (fsst)
	{
		default:
		case FS_NO_ERROR:
			return;

		case FS_IO_ERROR:
			uMsgId = (IDS_FSERR_IOERR);
			break;

		case FS_IMAGE_RO:
			uMsgId = (IDS_FSERR_IMGRO);
			break;

		case FS_FILE_OPENERR:
			uMsgId = (IDS_FSERR_FOPENERR);
			break;

		case FS_FILE_EXIST:
			uMsgId = (IDS_FSERR_FEXIST);
			break;

		case FS_STRUCT_ERR:
			uMsgId = (IDS_FSERR_STRUCTERR);
			break;

		case FS_CAT_FULL:
			uMsgId = (IDS_FSERR_CATFULL);
			break;

		case FS_MKD_CAT_FULL:
			uMsgId = (IDS_FSERR_MKCATFULL);
			break;

		case FS_DISK_FULL:
			uMsgId = (IDS_FSERR_DISKFULL);
			break;

		case FS_NO_IMAGE:
			uMsgId = (IDS_FSERR_NOIMAGE);
			break;
	}

	ShowMessageBox(uMsgId, MB_OK | MB_ICONEXCLAMATION);
}

void CBKDEDlg::OnBnClickedButtonExtract()
{
	auto *szOutPath = new TCHAR[_MAX_PATH];

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

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

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

		m_ListControl.ExtractSelected(m_strStorePath);
	}

	delete [] szOutPath;
	m_ListControl.SetFocus();
}

void CBKDEDlg::OnBnClickedButtonView()
{
	m_ListControl.ViewSelected(true);
	m_ListControl.SetFocus();
}

void CBKDEDlg::OnBnClickedButtonViewSprite()
{
	m_ListControl.ViewSelected(false);
	m_ListControl.SetFocus();
}

void CBKDEDlg::OnBnClickedButtonAdd()
{
	GetFiles();
}

void CBKDEDlg::FillStatusBar()
{
	//выведем имя образа
	m_StatusBar.SetPaneText(STATUSBAR_POS_IMGNAME, m_strImgPathName);
	//выведем тип ОС
	CString strOS;
	strOS.LoadString(m_ListControl.GetImgFormatName());
	m_StatusBar.SetPaneText(STATUSBAR_POS_OSTYPE, strOS);
	//выведем размер
	CString strSzf, strSz;
	strSzf.LoadString(IDS_IMAGE_SIZE);
	strSz.Format(strSzf, m_sParseResult.nImageSize / 1024);
	m_StatusBar.SetPaneText(STATUSBAR_POS_IMGSIZE, strSz);
	//выведем загрузочный или нет
	strOS = _T("");

	if (m_sParseResult.bImageBootable)
	{
		strOS.LoadString(IDS_IMGTYPE_SYSTEM);
	}

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

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::PreUseLoadSettings()
{
	m_Settings.Init();
	m_Settings.LoadStringValue(CSettings::IMAGE_PATH, m_strImgPathDir);
	m_Settings.LoadStringValue(CSettings::STORE_PATH, m_strStorePath);
	m_Settings.LoadStringValue(CSettings::LOAD_PATH, m_strLoadPath);
	m_Settings.Done();
}

void CBKDEDlg::PreCloseStoreSettings()
{
	m_Settings.Init();
	m_Settings.SaveStringValue(CSettings::IMAGE_PATH, m_strImgPathDir);
	m_Settings.SaveStringValue(CSettings::STORE_PATH, m_strStorePath);
	m_Settings.SaveStringValue(CSettings::LOAD_PATH, m_strLoadPath);
	m_Settings.Done();
}


void CBKDEDlg::ShowMessageBox(UINT strID, UINT nType)
{
	CString strMsg;
	strMsg.LoadString(strID);
	ShowMessageBox(strMsg, nType);
}

void CBKDEDlg::ShowMessageBox(CString &str, UINT nType)
{
	CString strCapt;
	strCapt.LoadString(IDS_MSGCAPT);
	MessageBox(str, strCapt, nType);
}
