851 lines
25 KiB
C++
851 lines
25 KiB
C++
|
|
/* ArduinoFloppyReaderWin
|
||
|
|
*
|
||
|
|
* Copyright (C) 2017-2021 Robert Smith (@RobSmithDev)
|
||
|
|
* https://amiga.robsmithdev.co.uk
|
||
|
|
*
|
||
|
|
* This program is free software; you can redistribute it and/or
|
||
|
|
* modify it under the terms of the GNU Library General Public
|
||
|
|
* License as published by the Free Software Foundation; either
|
||
|
|
* version 3 of the License, or (at your option) any later version.
|
||
|
|
*
|
||
|
|
* This library is distributed in the hope that it will be useful,
|
||
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
|
|
* Library General Public License for more details.
|
||
|
|
*
|
||
|
|
* You should have received a copy of the GNU Library General Public
|
||
|
|
* License along with this program; if not see http://www.gnu.org/licenses/
|
||
|
|
*/
|
||
|
|
|
||
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||
|
|
// Simple Windows application using the libraries used in the console app //
|
||
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||
|
|
|
||
|
|
#include "stdafx.h"
|
||
|
|
#include <afxwin.h>
|
||
|
|
#include "ArduinoFloppyReaderWin.h"
|
||
|
|
#include "ArduinoFloppyReaderWinDlg.h"
|
||
|
|
#include "afxdialogex.h"
|
||
|
|
#include <mmsystem.h>
|
||
|
|
#include "..\lib\ADFWriter.h"
|
||
|
|
#include <Windows.h>
|
||
|
|
|
||
|
|
#ifdef _DEBUG
|
||
|
|
#define new DEBUG_NEW
|
||
|
|
#endif
|
||
|
|
|
||
|
|
|
||
|
|
#pragma region ABOUT_DIALOG
|
||
|
|
|
||
|
|
// CAboutDlg dialog used for App About
|
||
|
|
class CAboutDlg : public CDialogEx
|
||
|
|
{
|
||
|
|
public:
|
||
|
|
CAboutDlg() : CDialogEx(IDD_ABOUTBOX) {};
|
||
|
|
|
||
|
|
// Dialog Data
|
||
|
|
#ifdef AFX_DESIGN_TIME
|
||
|
|
enum { IDD = IDD_ABOUTBOX };
|
||
|
|
#endif
|
||
|
|
protected:
|
||
|
|
virtual void DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); };
|
||
|
|
|
||
|
|
protected:
|
||
|
|
DECLARE_MESSAGE_MAP()
|
||
|
|
};
|
||
|
|
|
||
|
|
BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
|
||
|
|
END_MESSAGE_MAP()
|
||
|
|
|
||
|
|
#pragma endregion ABOUT_DIALOG
|
||
|
|
|
||
|
|
|
||
|
|
#pragma region DIAGNOSTICS_DIALOG
|
||
|
|
|
||
|
|
// CDiagnosticsDialog dialog used diagnostics status window
|
||
|
|
class CDiagnosticsDialog : public CDialogEx
|
||
|
|
{
|
||
|
|
public:
|
||
|
|
CDiagnosticsDialog(const std::wstring& comport) : CDialogEx(IDD_DIAGNOSTICS), m_comPort(comport), m_mainThread(nullptr) {};
|
||
|
|
virtual ~CDiagnosticsDialog() {
|
||
|
|
if (m_mainThread) {
|
||
|
|
if (m_mainThread->joinable()) m_mainThread->join();
|
||
|
|
delete m_mainThread;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public:
|
||
|
|
CEdit m_results;
|
||
|
|
|
||
|
|
// Dialog Data
|
||
|
|
#ifdef AFX_DESIGN_TIME
|
||
|
|
enum { IDD = IDD_DIAGNOSTICS };
|
||
|
|
#endif
|
||
|
|
|
||
|
|
protected:
|
||
|
|
virtual void DoDataExchange(CDataExchange* pDX) {
|
||
|
|
CDialogEx::DoDataExchange(pDX);
|
||
|
|
DDX_Control(pDX, IDC_EDIT1, m_results);
|
||
|
|
};
|
||
|
|
|
||
|
|
|
||
|
|
BOOL OnInitDialog()
|
||
|
|
{
|
||
|
|
CDialogEx::OnInitDialog();
|
||
|
|
m_threadRunning = true;
|
||
|
|
|
||
|
|
WCHAR buffer[200];
|
||
|
|
swprintf_s(buffer, L"Running Diagnostics on %s", m_comPort.c_str());
|
||
|
|
SetWindowText(buffer);
|
||
|
|
|
||
|
|
CMenu* mnu = this->GetSystemMenu(FALSE);
|
||
|
|
mnu->EnableMenuItem(SC_CLOSE, MF_BYCOMMAND | MF_GRAYED | MF_DISABLED);
|
||
|
|
|
||
|
|
// Main processing thread
|
||
|
|
m_mainThread = new std::thread([this]()->void {
|
||
|
|
runDiagnostics();
|
||
|
|
m_threadRunning = false;
|
||
|
|
});
|
||
|
|
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
protected:
|
||
|
|
DECLARE_MESSAGE_MAP()
|
||
|
|
|
||
|
|
private:
|
||
|
|
const std::wstring m_comPort;
|
||
|
|
bool m_threadRunning = true;
|
||
|
|
ArduinoFloppyReader::ADFWriter writer;
|
||
|
|
std::thread* m_mainThread;
|
||
|
|
|
||
|
|
// This is ran under a thread
|
||
|
|
void runDiagnostics() {
|
||
|
|
writer.runDiagnostics(m_comPort, [this](bool isError, const std::string message)->void {
|
||
|
|
CString strLine;
|
||
|
|
|
||
|
|
if (isError) strLine = "DIAGNOSTICS FAILED: ";
|
||
|
|
strLine += message.c_str();
|
||
|
|
strLine += "\r\n";
|
||
|
|
|
||
|
|
// get the initial text length
|
||
|
|
int nLength = m_results.GetWindowTextLength();
|
||
|
|
// put the selection at the end of text
|
||
|
|
m_results.SetSel(nLength, nLength);
|
||
|
|
// replace the selection
|
||
|
|
m_results.ReplaceSel(strLine);
|
||
|
|
|
||
|
|
}, [this](bool isQuestion, const std::string question)->bool {
|
||
|
|
if (isQuestion)
|
||
|
|
return MessageBoxA(GetSafeHwnd(), (LPCSTR)question.c_str(), "Diagnostics Question", MB_YESNO | MB_ICONQUESTION) == IDYES;
|
||
|
|
else
|
||
|
|
return MessageBoxA(GetSafeHwnd(), (LPCSTR)question.c_str(), "Diagnostics Prompt", MB_OKCANCEL | MB_ICONINFORMATION) == IDOK;
|
||
|
|
});
|
||
|
|
|
||
|
|
writer.closeDevice();
|
||
|
|
|
||
|
|
CMenu* mnu = this->GetSystemMenu(FALSE);
|
||
|
|
mnu->EnableMenuItem(SC_CLOSE, MF_BYCOMMAND | MF_ENABLED);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Prevent Enter and ESC closing the dialog
|
||
|
|
BOOL PreTranslateMessage(MSG* pMsg)
|
||
|
|
{
|
||
|
|
if (pMsg->message == WM_KEYDOWN)
|
||
|
|
{
|
||
|
|
if (pMsg->wParam == VK_RETURN || pMsg->wParam == VK_ESCAPE)
|
||
|
|
{
|
||
|
|
return !m_threadRunning; // Do not process further
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return CWnd::PreTranslateMessage(pMsg);
|
||
|
|
}
|
||
|
|
|
||
|
|
};
|
||
|
|
|
||
|
|
BEGIN_MESSAGE_MAP(CDiagnosticsDialog, CDialogEx)
|
||
|
|
END_MESSAGE_MAP()
|
||
|
|
|
||
|
|
#pragma endregion DIAGNOSTICS_DIALOG
|
||
|
|
|
||
|
|
|
||
|
|
#pragma region COMPLETE_DIALOG
|
||
|
|
|
||
|
|
// CCompleteDialog dialog used to show copy/write completion
|
||
|
|
class CCompleteDialog : public CDialogEx
|
||
|
|
{
|
||
|
|
private:
|
||
|
|
void* m_sfx;
|
||
|
|
HBITMAP m_currentBitmap;
|
||
|
|
public:
|
||
|
|
CCompleteDialog(void* sfx, bool partial, bool writeMode, bool SCPMode);
|
||
|
|
virtual ~CCompleteDialog();
|
||
|
|
|
||
|
|
// Dialog Data
|
||
|
|
#ifdef AFX_DESIGN_TIME
|
||
|
|
enum { IDD = IDD_DIALOG };
|
||
|
|
#endif
|
||
|
|
|
||
|
|
protected:
|
||
|
|
virtual BOOL OnInitDialog();
|
||
|
|
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
|
||
|
|
LRESULT OnNcHitTest(CPoint point);
|
||
|
|
// Implementation
|
||
|
|
protected:
|
||
|
|
DECLARE_MESSAGE_MAP()
|
||
|
|
public:
|
||
|
|
|
||
|
|
virtual INT_PTR DoModal() override;
|
||
|
|
CStatic m_dialogImage;
|
||
|
|
};
|
||
|
|
|
||
|
|
CCompleteDialog::~CCompleteDialog() {
|
||
|
|
if (m_currentBitmap) DeleteObject(m_currentBitmap);
|
||
|
|
}
|
||
|
|
|
||
|
|
CCompleteDialog::CCompleteDialog(void* sfx, bool partial, bool writeMode, bool SCPMode) : CDialogEx(IDD_DIALOG)
|
||
|
|
{
|
||
|
|
m_sfx = sfx;
|
||
|
|
int id;
|
||
|
|
if (SCPMode) id = IDB_SCPREAD; else
|
||
|
|
if (writeMode)
|
||
|
|
id = partial ? IDB_DIALOG_WARNING_WRITE : IDB_DIALOG_OK_WRITE;
|
||
|
|
else id = partial ? IDB_DIALOG_WARNING : IDB_DIALOG_OK;
|
||
|
|
m_currentBitmap = LoadBitmap(AfxGetInstanceHandle(), MAKEINTRESOURCE(id));
|
||
|
|
}
|
||
|
|
|
||
|
|
BOOL CCompleteDialog::OnInitDialog()
|
||
|
|
{
|
||
|
|
CDialogEx::OnInitDialog();
|
||
|
|
m_dialogImage.SetBitmap(m_currentBitmap);
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
void CCompleteDialog::DoDataExchange(CDataExchange* pDX)
|
||
|
|
{
|
||
|
|
CDialogEx::DoDataExchange(pDX);
|
||
|
|
DDX_Control(pDX, IDC_DIALOGIMAGE, m_dialogImage);
|
||
|
|
}
|
||
|
|
|
||
|
|
INT_PTR CCompleteDialog::DoModal()
|
||
|
|
{
|
||
|
|
sndPlaySound((LPCWSTR)m_sfx, SND_ASYNC | SND_MEMORY | SND_NODEFAULT);
|
||
|
|
return CDialogEx::DoModal();
|
||
|
|
}
|
||
|
|
|
||
|
|
LRESULT CCompleteDialog::OnNcHitTest(CPoint point)
|
||
|
|
{
|
||
|
|
CPoint pt = point;
|
||
|
|
ScreenToClient(&pt);
|
||
|
|
|
||
|
|
if (pt.y < 24) return HTCAPTION;
|
||
|
|
return CDialog::OnNcHitTest(point);
|
||
|
|
}
|
||
|
|
BEGIN_MESSAGE_MAP(CCompleteDialog, CDialogEx)
|
||
|
|
ON_WM_NCHITTEST()
|
||
|
|
END_MESSAGE_MAP()
|
||
|
|
|
||
|
|
|
||
|
|
#pragma endregion COMPLETE_DIALOG
|
||
|
|
|
||
|
|
#pragma region MAIN_DIALOG
|
||
|
|
|
||
|
|
|
||
|
|
// CArduinoFloppyReaderWinDlg dialog
|
||
|
|
CArduinoFloppyReaderWinDlg::CArduinoFloppyReaderWinDlg(CWnd* pParent /*=NULL*/)
|
||
|
|
: CDialogEx(IDD_ARDUINOFLOPPYREADERWIN_DIALOG, pParent)
|
||
|
|
{
|
||
|
|
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
|
||
|
|
m_iothread = nullptr;
|
||
|
|
}
|
||
|
|
|
||
|
|
void CArduinoFloppyReaderWinDlg::DoDataExchange(CDataExchange* pDX)
|
||
|
|
{
|
||
|
|
CDialogEx::DoDataExchange(pDX);
|
||
|
|
DDX_Control(pDX, IDC_COMPORT, m_comport);
|
||
|
|
DDX_Control(pDX, IDC_FILENAME, m_outputADF);
|
||
|
|
DDX_Control(pDX, IDC_PROGRESS, m_progressbar);
|
||
|
|
DDX_Control(pDX, IDC_TRACKNUMBER, m_statusTrack);
|
||
|
|
DDX_Control(pDX, IDC_DISKSIDE, m_statusSide);
|
||
|
|
DDX_Control(pDX, IDC_GOODSECTORS, m_statusGood);
|
||
|
|
DDX_Control(pDX, IDC_BADSECTORS, m_statusPartial);
|
||
|
|
DDX_Control(pDX, IDC_BROWSE, m_browseButton);
|
||
|
|
DDX_Control(pDX, IDC_STARTSTOP, m_copyButton);
|
||
|
|
DDX_Control(pDX, IDC_STATUS, m_statusText);
|
||
|
|
DDX_Control(pDX, IDC_FILENAME2, m_inputADF);
|
||
|
|
DDX_Control(pDX, IDC_BROWSE2, m_browseButton2);
|
||
|
|
DDX_Control(pDX, IDC_STARTSTOP2, m_writeButton);
|
||
|
|
DDX_Control(pDX, IDC_CHECK1, m_verify);
|
||
|
|
DDX_Control(pDX, IDC_CHECK3, m_precomp);
|
||
|
|
DDX_Control(pDX, IDC_STARTSTOP3, m_diagnostics);
|
||
|
|
DDX_Control(pDX, IDC_DISKFORMAT, m_fileFormat);
|
||
|
|
DDX_Control(pDX, IDC_TRK80, m_trk80);
|
||
|
|
DDX_Control(pDX, IDC_TRK82, m_trk82);
|
||
|
|
}
|
||
|
|
|
||
|
|
BEGIN_MESSAGE_MAP(CArduinoFloppyReaderWinDlg, CDialogEx)
|
||
|
|
ON_WM_SYSCOMMAND()
|
||
|
|
ON_WM_PAINT()
|
||
|
|
ON_WM_QUERYDRAGICON()
|
||
|
|
ON_BN_CLICKED(IDC_STARTSTOP, &CArduinoFloppyReaderWinDlg::OnBnClickedStartstop)
|
||
|
|
ON_BN_CLICKED(IDC_BROWSE, &CArduinoFloppyReaderWinDlg::OnBnClickedBrowse)
|
||
|
|
ON_MESSAGE(WM_USER, &CArduinoFloppyReaderWinDlg::OnUserMessage)
|
||
|
|
ON_BN_CLICKED(IDC_BROWSE2, &CArduinoFloppyReaderWinDlg::OnBnClickedBrowse2)
|
||
|
|
ON_BN_CLICKED(IDC_STARTSTOP2, &CArduinoFloppyReaderWinDlg::OnBnClickedStartstop2)
|
||
|
|
ON_BN_CLICKED(IDC_STARTSTOP3, &CArduinoFloppyReaderWinDlg::OnBnClickedStartstop3)
|
||
|
|
ON_MESSAGE(WM_DEVICECHANGE, &CArduinoFloppyReaderWinDlg::OnDevicechange)
|
||
|
|
ON_CBN_SELCHANGE(IDC_DISKFORMAT, &CArduinoFloppyReaderWinDlg::OnCbnSelchangeDiskformat)
|
||
|
|
END_MESSAGE_MAP()
|
||
|
|
|
||
|
|
|
||
|
|
// CArduinoFloppyReaderWinDlg message handlers
|
||
|
|
|
||
|
|
void CArduinoFloppyReaderWinDlg::enumComPorts() {
|
||
|
|
std::vector<std::wstring> portList;
|
||
|
|
ArduinoFloppyReader::ArduinoInterface::enumeratePorts(portList);
|
||
|
|
|
||
|
|
m_comport.ResetContent();
|
||
|
|
for (const std::wstring& port : portList)
|
||
|
|
m_comport.AddString(port.c_str());
|
||
|
|
}
|
||
|
|
|
||
|
|
BOOL CArduinoFloppyReaderWinDlg::OnInitDialog()
|
||
|
|
{
|
||
|
|
CDialogEx::OnInitDialog();
|
||
|
|
|
||
|
|
// Add "About..." menu item to system menu.
|
||
|
|
|
||
|
|
// IDM_ABOUTBOX must be in the system command range.
|
||
|
|
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
|
||
|
|
ASSERT(IDM_ABOUTBOX < 0xF000);
|
||
|
|
|
||
|
|
CMenu* pSysMenu = GetSystemMenu(FALSE);
|
||
|
|
if (pSysMenu != NULL)
|
||
|
|
{
|
||
|
|
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);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Set the icon for this dialog. The framework does this automatically
|
||
|
|
// when the application's main window is not a dialog
|
||
|
|
SetIcon(m_hIcon, TRUE); // Set big icon
|
||
|
|
SetIcon(m_hIcon, FALSE); // Set small icon
|
||
|
|
|
||
|
|
enumComPorts();
|
||
|
|
|
||
|
|
WCHAR buf[10];
|
||
|
|
buf[0] = '\0';
|
||
|
|
LONG len = 10;
|
||
|
|
memset(buf, 0, len * 2);
|
||
|
|
RegQueryValueW(HKEY_CURRENT_USER, L"Software\\ArduinoFloppyReader\\ComPort", buf, &len);
|
||
|
|
int port = wcstol(buf, nullptr, 10);
|
||
|
|
swprintf_s(buf, L"COM%i", port);
|
||
|
|
setComPort(buf);
|
||
|
|
|
||
|
|
m_fileFormat.AddString(L"ADF");
|
||
|
|
m_fileFormat.AddString(L"SCP");
|
||
|
|
buf[0] = '\0';
|
||
|
|
len = 10;
|
||
|
|
RegQueryValueW(HKEY_CURRENT_USER, L"Software\\ArduinoFloppyReader\\fileFormat", buf, &len);
|
||
|
|
int index = wcstol(buf, nullptr, 10);
|
||
|
|
m_fileFormat.SetCurSel(index);
|
||
|
|
|
||
|
|
buf[0] = '\0';
|
||
|
|
len = 10;
|
||
|
|
RegQueryValueW(HKEY_CURRENT_USER, L"Software\\ArduinoFloppyReader\\tracks", buf, &len);
|
||
|
|
int numTracks = wcstol(buf, nullptr, 10);
|
||
|
|
m_trk82.SetCheck(numTracks == 82);
|
||
|
|
m_trk80.SetCheck(numTracks != 82);
|
||
|
|
|
||
|
|
|
||
|
|
HRSRC res = FindResource(NULL, MAKEINTRESOURCE(IDR_COMPLETE), L"WAVE");
|
||
|
|
m_sfx = nullptr;
|
||
|
|
if (res) {
|
||
|
|
HGLOBAL resource = LoadResource(NULL, res);
|
||
|
|
if (resource) {
|
||
|
|
int resSize = SizeofResource(NULL, res);
|
||
|
|
m_sfx = malloc(resSize);
|
||
|
|
|
||
|
|
if (m_sfx) {
|
||
|
|
void* mem = LockResource(resource);
|
||
|
|
if (mem) memcpy(m_sfx, mem, resSize);
|
||
|
|
UnlockResource(resource);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
FreeResource(resource);
|
||
|
|
}
|
||
|
|
m_precomp.SetCheck(TRUE);
|
||
|
|
m_verify.SetCheck(TRUE);
|
||
|
|
|
||
|
|
|
||
|
|
return TRUE; // return TRUE unless you set the focus to a control
|
||
|
|
}
|
||
|
|
|
||
|
|
std::wstring CArduinoFloppyReaderWinDlg::getComPort() {
|
||
|
|
int index = m_comport.GetCurSel();
|
||
|
|
if (index < 0) return L"";
|
||
|
|
CString buf;
|
||
|
|
|
||
|
|
m_comport.GetWindowTextW(buf);
|
||
|
|
|
||
|
|
return std::wstring(buf);
|
||
|
|
}
|
||
|
|
|
||
|
|
void CArduinoFloppyReaderWinDlg::setComPort(const std::wstring& comport) {
|
||
|
|
int index = m_comport.FindString(0, comport.c_str());
|
||
|
|
if (index < 0) index = 0;
|
||
|
|
m_comport.SetCurSel(index);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
CArduinoFloppyReaderWinDlg::~CArduinoFloppyReaderWinDlg() {
|
||
|
|
m_cancelButtonPressed = true;
|
||
|
|
|
||
|
|
if (m_sfx) free(m_sfx);
|
||
|
|
}
|
||
|
|
|
||
|
|
// On Sys command
|
||
|
|
void CArduinoFloppyReaderWinDlg::OnSysCommand(UINT nID, LPARAM lParam)
|
||
|
|
{
|
||
|
|
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
|
||
|
|
{
|
||
|
|
CAboutDlg dlgAbout;
|
||
|
|
dlgAbout.DoModal();
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
CDialogEx::OnSysCommand(nID, lParam);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// If you add a minimize button to your dialog, you will need the code below
|
||
|
|
// to draw the icon. For MFC applications using the document/view model,
|
||
|
|
// this is automatically done for you by the framework.
|
||
|
|
void CArduinoFloppyReaderWinDlg::OnPaint()
|
||
|
|
{
|
||
|
|
if (IsIconic())
|
||
|
|
{
|
||
|
|
CPaintDC dc(this); // device context for painting
|
||
|
|
|
||
|
|
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
|
||
|
|
|
||
|
|
// Center icon in client rectangle
|
||
|
|
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;
|
||
|
|
|
||
|
|
// Draw the icon
|
||
|
|
dc.DrawIcon(x, y, m_hIcon);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
CDialogEx::OnPaint();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// The system calls this function to obtain the cursor to display while the user drags
|
||
|
|
// the minimized window.
|
||
|
|
HCURSOR CArduinoFloppyReaderWinDlg::OnQueryDragIcon()
|
||
|
|
{
|
||
|
|
return static_cast<HCURSOR>(m_hIcon);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Main thread
|
||
|
|
bool CArduinoFloppyReaderWinDlg::runThreadRead() {
|
||
|
|
ArduinoFloppyReader::ADFWriter writer;
|
||
|
|
|
||
|
|
const std::wstring comPort = getComPort();
|
||
|
|
bool isSCP = m_fileFormat.GetCurSel() == 1;
|
||
|
|
|
||
|
|
// Try to open the com port and talk to the device
|
||
|
|
m_statusText.SetWindowText(L"Opening COM port and setting up device...");
|
||
|
|
if (!writer.openDevice(comPort)) {
|
||
|
|
std::string msg = "Unable to open COM port:\r\n\r\n";
|
||
|
|
msg += writer.getLastError();
|
||
|
|
MessageBoxA(GetSafeHwnd(), msg.c_str(), "Error", MB_OK | MB_ICONEXCLAMATION);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Get the current firmware version. Only valid if openDevice is successful
|
||
|
|
const ArduinoFloppyReader::FirmwareVersion v = writer.getFirwareVersion();
|
||
|
|
if ((v.major == 1) && (v.minor < 8)) {
|
||
|
|
static bool hasBeenWarnedAboutVersion = false;
|
||
|
|
// improved disk timings in 1.8, so make them aware
|
||
|
|
if (!hasBeenWarnedAboutVersion) {
|
||
|
|
hasBeenWarnedAboutVersion = true;
|
||
|
|
MessageBox(L"Rob strongly recommends updating the firmware on your Arduino to at least V1.8.\nThat version is even better at reading old disks.", L"Firmware Upgrade Note", MB_OK | MB_ICONINFORMATION);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Analysis was complete and found some data. Run the reader
|
||
|
|
m_statusText.SetWindowText(isSCP ? L"Reading disk to SCP file..." : L"Reading disk to ADF file...");
|
||
|
|
CString filename;
|
||
|
|
const unsigned int lastTrack = m_trk82.GetCheck() ? 82 : 80;
|
||
|
|
m_progressbar.SetRange(0, lastTrack * 2);
|
||
|
|
m_progressbar.SetPos(0);
|
||
|
|
|
||
|
|
auto callback = [this](const int currentTrack, const ArduinoFloppyReader::DiskSurface currentSide, const int retryCounter, const int sectorsFound, const int badSectorsFound) -> ArduinoFloppyReader::WriteResponse {
|
||
|
|
|
||
|
|
if (m_cancelButtonPressed) return ArduinoFloppyReader::WriteResponse::wrAbort;
|
||
|
|
|
||
|
|
CString str;
|
||
|
|
str.Format(L"%i", currentTrack);
|
||
|
|
m_statusTrack.SetWindowText(str);
|
||
|
|
m_statusSide.SetWindowText((currentSide == ArduinoFloppyReader::DiskSurface::dsUpper) ? L"Upper" : L"Lower");
|
||
|
|
str.Format(L"%i", sectorsFound);
|
||
|
|
m_statusGood.SetWindowText(str);
|
||
|
|
str.Format(L"%i", badSectorsFound);
|
||
|
|
m_statusPartial.SetWindowText(str);
|
||
|
|
|
||
|
|
m_progressbar.SetPos((currentTrack * 2) + ((currentSide == ArduinoFloppyReader::DiskSurface::dsUpper) ? 1 : 0));
|
||
|
|
|
||
|
|
if (retryCounter > 20) {
|
||
|
|
switch (MessageBox(L"Disk has checksum errors/missing/damaged data.\r\n\r\n", L"Disk Read Errors", MB_ABORTRETRYIGNORE)) {
|
||
|
|
case IDABORT: return ArduinoFloppyReader::WriteResponse::wrAbort;
|
||
|
|
case IDRETRY: return ArduinoFloppyReader::WriteResponse::wrRetry;
|
||
|
|
case IDIGNORE: return ArduinoFloppyReader::WriteResponse::wrSkipBadChecksums;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Just continue
|
||
|
|
return ArduinoFloppyReader::WriteResponse::wrContinue;
|
||
|
|
};
|
||
|
|
|
||
|
|
m_outputADF.GetWindowText(filename);
|
||
|
|
|
||
|
|
ArduinoFloppyReader::ADFResult readerResult;
|
||
|
|
|
||
|
|
|
||
|
|
readerResult = isSCP ? writer.DiskToSCP(filename.GetBuffer(), lastTrack, 3, callback) : writer.DiskToADF(filename.GetBuffer(), lastTrack, callback);
|
||
|
|
|
||
|
|
// Handle the result
|
||
|
|
switch (readerResult) {
|
||
|
|
case ArduinoFloppyReader::ADFResult::adfrComplete: m_partial = false; return true;
|
||
|
|
case ArduinoFloppyReader::ADFResult::adfrAborted: return false;
|
||
|
|
case ArduinoFloppyReader::ADFResult::adfrFileError: MessageBox(L"Unable to open the specified file to write to it.", L"Output File Error", MB_OK | MB_ICONEXCLAMATION); return false;
|
||
|
|
case ArduinoFloppyReader::ADFResult::adfrFileIOError: MessageBox(L"An error occured writing to the specified file.", L"Output File Error", MB_OK | MB_ICONEXCLAMATION); return false;
|
||
|
|
case ArduinoFloppyReader::ADFResult::adfrFirmwareTooOld: MessageBox(L"This requires firmware V1.8 or newer.", L"Firmware out of date", MB_OK | MB_ICONEXCLAMATION); return false;
|
||
|
|
case ArduinoFloppyReader::ADFResult::adfrCompletedWithErrors: m_partial = true; return true;
|
||
|
|
case ArduinoFloppyReader::ADFResult::adfrDriveError: {
|
||
|
|
std::string msg = "An error occured communicating with the Arduino interface:\r\n\r\n";
|
||
|
|
msg += writer.getLastError();
|
||
|
|
MessageBoxA(GetSafeHwnd(), msg.c_str(), "I/O Error", MB_OK | MB_ICONEXCLAMATION);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
// Main thread
|
||
|
|
bool CArduinoFloppyReaderWinDlg::runThreadWrite() {
|
||
|
|
ArduinoFloppyReader::ADFWriter writer;
|
||
|
|
|
||
|
|
const std::wstring comPort = getComPort();
|
||
|
|
|
||
|
|
// Try to open the com port and talk to the device
|
||
|
|
m_statusText.SetWindowText(L"Opening COM port and setting up device...");
|
||
|
|
if (!writer.openDevice(comPort)) {
|
||
|
|
std::string msg = "Unable to open COM port:\r\n\r\n";
|
||
|
|
msg += writer.getLastError();
|
||
|
|
MessageBoxA(GetSafeHwnd(), msg.c_str(), "Error", MB_OK | MB_ICONEXCLAMATION);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Analysis was complete and found some data. Run the reader
|
||
|
|
m_statusText.SetWindowText(L"Writing ADF file to disk...");
|
||
|
|
CString filename;
|
||
|
|
const unsigned int lastTrack = 80;
|
||
|
|
m_progressbar.SetRange(0, lastTrack * 2);
|
||
|
|
m_progressbar.SetPos(0);
|
||
|
|
|
||
|
|
m_inputADF.GetWindowText(filename);
|
||
|
|
|
||
|
|
ArduinoFloppyReader::ADFResult readerResult = writer.ADFToDisk(filename.GetBuffer(), m_verify.GetCheck() != 0, m_precomp.GetCheck() != 0,
|
||
|
|
[this](const int currentTrack, const ArduinoFloppyReader::DiskSurface currentSide, const bool isVerifyError) -> ArduinoFloppyReader::WriteResponse {
|
||
|
|
|
||
|
|
if (m_cancelButtonPressed) return ArduinoFloppyReader::WriteResponse::wrAbort;
|
||
|
|
|
||
|
|
CString str;
|
||
|
|
str.Format(L"%i", currentTrack);
|
||
|
|
m_statusTrack.SetWindowText(str);
|
||
|
|
m_statusSide.SetWindowText((currentSide == ArduinoFloppyReader::DiskSurface::dsUpper) ? L"Upper" : L"Lower");
|
||
|
|
|
||
|
|
m_progressbar.SetPos((currentTrack * 2) + ((currentSide == ArduinoFloppyReader::DiskSurface::dsUpper) ? 1 : 0));
|
||
|
|
|
||
|
|
if (isVerifyError) {
|
||
|
|
switch (MessageBox(L"Verify error writing track.", L"Disk Write Error", MB_ABORTRETRYIGNORE)) {
|
||
|
|
case IDABORT: return ArduinoFloppyReader::WriteResponse::wrAbort;
|
||
|
|
case IDRETRY: return ArduinoFloppyReader::WriteResponse::wrRetry;
|
||
|
|
case IDIGNORE: return ArduinoFloppyReader::WriteResponse::wrSkipBadChecksums;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Just continue
|
||
|
|
return ArduinoFloppyReader::WriteResponse::wrContinue;
|
||
|
|
});
|
||
|
|
|
||
|
|
|
||
|
|
switch (readerResult) {
|
||
|
|
case ArduinoFloppyReader::ADFResult::adfrComplete: m_partial = false; return true;
|
||
|
|
case ArduinoFloppyReader::ADFResult::adfrCompletedWithErrors: m_partial = true; return true;
|
||
|
|
case ArduinoFloppyReader::ADFResult::adfrAborted: return false;
|
||
|
|
case ArduinoFloppyReader::ADFResult::adfrFileError: MessageBox(L"Unable to open the specified file to read from it.", L"Input File Error", MB_OK | MB_ICONEXCLAMATION); return false;
|
||
|
|
case ArduinoFloppyReader::ADFResult::adfrDriveError: {
|
||
|
|
std::string msg = "An error occured communicating with the Arduino interface:\r\n\r\n";
|
||
|
|
msg += writer.getLastError();
|
||
|
|
MessageBoxA(GetSafeHwnd(), msg.c_str(), "I/O Error", MB_OK | MB_ICONEXCLAMATION);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
case ArduinoFloppyReader::ADFResult::adfrDiskWriteProtected: MessageBox(L"Unable to write to the disk. Disk is write protected.", L"Write Protection Error", MB_OK | MB_ICONEXCLAMATION); return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
afx_msg LRESULT CArduinoFloppyReaderWinDlg::OnUserMessage(WPARAM wparam, LPARAM lparam) {
|
||
|
|
if (m_iothread) {
|
||
|
|
if (m_iothread->joinable()) m_iothread->join();
|
||
|
|
delete m_iothread;
|
||
|
|
m_iothread = nullptr;
|
||
|
|
}
|
||
|
|
m_copyButton.SetWindowText(L"Copy Disk");
|
||
|
|
m_writeButton.SetWindowText(L"Write Disk");
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Enabel the selectable items on the dialog
|
||
|
|
void CArduinoFloppyReaderWinDlg::enableDialog(bool enable) {
|
||
|
|
m_browseButton.EnableWindow(enable);
|
||
|
|
m_browseButton2.EnableWindow(enable);
|
||
|
|
m_outputADF.EnableWindow(enable);
|
||
|
|
m_inputADF.EnableWindow(enable);
|
||
|
|
m_comport.EnableWindow(enable);
|
||
|
|
m_verify.EnableWindow(enable);
|
||
|
|
m_precomp.EnableWindow(enable);
|
||
|
|
m_diagnostics.EnableWindow(enable);
|
||
|
|
m_fileFormat.EnableWindow(enable);
|
||
|
|
m_precomp.EnableWindow(enable);
|
||
|
|
m_trk80.EnableWindow(enable);
|
||
|
|
m_trk82.EnableWindow(enable);
|
||
|
|
|
||
|
|
if (enable) {
|
||
|
|
m_statusText.SetWindowText(L"Ready");
|
||
|
|
m_progressbar.SetPos(0);
|
||
|
|
m_statusTrack.SetWindowText(L"0");
|
||
|
|
m_statusSide.SetWindowText(L"Upper");
|
||
|
|
m_statusGood.SetWindowText(L"0");
|
||
|
|
m_statusPartial.SetWindowText(L"0");
|
||
|
|
m_copyButton.EnableWindow(true);
|
||
|
|
m_writeButton.EnableWindow(true);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Called when the thread for reading ifinishes
|
||
|
|
void CArduinoFloppyReaderWinDlg::threadFinishedReading(bool successful) {
|
||
|
|
enableDialog(true);
|
||
|
|
|
||
|
|
if (successful) {
|
||
|
|
if (m_partial) {
|
||
|
|
CCompleteDialog dlg(m_sfx, true, false, false);
|
||
|
|
dlg.DoModal();
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
CCompleteDialog dlg(m_sfx, false, false, m_fileFormat.GetCurSel() == 1);
|
||
|
|
dlg.DoModal();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Free the thread
|
||
|
|
PostMessage(WM_USER, 0, 0);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
// Called when the thread for reading ifinishes
|
||
|
|
void CArduinoFloppyReaderWinDlg::threadFinishedWriting(bool successful) {
|
||
|
|
enableDialog(true);
|
||
|
|
|
||
|
|
if (successful) {
|
||
|
|
if (m_partial) {
|
||
|
|
CCompleteDialog dlg(m_sfx, true, true, false);
|
||
|
|
dlg.DoModal();
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
CCompleteDialog dlg(m_sfx, false, true, false);
|
||
|
|
dlg.DoModal();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Free the thread
|
||
|
|
PostMessage(WM_USER, 0, 0);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
// Save the comp port to the registry
|
||
|
|
void CArduinoFloppyReaderWinDlg::saveComPort() {
|
||
|
|
const std::wstring port = getComPort();
|
||
|
|
|
||
|
|
char buffer[20];
|
||
|
|
|
||
|
|
if (port.length() > 3) {
|
||
|
|
RegSetValueW(HKEY_CURRENT_USER, L"Software\\ArduinoFloppyReader\\ComPort", REG_SZ, (LPCWSTR)&port[3], port.length()-3);
|
||
|
|
}
|
||
|
|
|
||
|
|
_itoa_s(m_fileFormat.GetCurSel(), buffer, 10);
|
||
|
|
RegSetValueA(HKEY_CURRENT_USER, "Software\\ArduinoFloppyReader\\fileFormat", REG_SZ, buffer, strlen(buffer));
|
||
|
|
|
||
|
|
_itoa_s(m_trk82.GetCheck() ? 82 : 80, buffer, 10);
|
||
|
|
RegSetValueA(HKEY_CURRENT_USER, "Software\\ArduinoFloppyReader\\tracks", REG_SZ, buffer, strlen(buffer));
|
||
|
|
}
|
||
|
|
|
||
|
|
// Disk to ADF file start and stop button
|
||
|
|
void CArduinoFloppyReaderWinDlg::OnBnClickedStartstop()
|
||
|
|
{
|
||
|
|
if (m_iothread) {
|
||
|
|
m_cancelButtonPressed = true;
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
saveComPort();
|
||
|
|
|
||
|
|
CString filename;
|
||
|
|
m_outputADF.GetWindowText(filename);
|
||
|
|
if (filename.GetLength() < 1) {
|
||
|
|
MessageBox(L"You need to specify an ADF filename first", L"Error", MB_OK | MB_ICONEXCLAMATION);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
m_cancelButtonPressed = false;
|
||
|
|
enableDialog(false);
|
||
|
|
|
||
|
|
m_copyButton.SetWindowText(L"Abort!");
|
||
|
|
m_writeButton.EnableWindow(false);
|
||
|
|
|
||
|
|
m_iothread = new std::thread([this]() {
|
||
|
|
this->threadFinishedReading(this->runThreadRead());
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// ADF to Disk write button
|
||
|
|
void CArduinoFloppyReaderWinDlg::OnBnClickedStartstop2()
|
||
|
|
{
|
||
|
|
if (m_iothread) {
|
||
|
|
m_cancelButtonPressed = true;
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
saveComPort();
|
||
|
|
|
||
|
|
CString filename;
|
||
|
|
m_inputADF.GetWindowText(filename);
|
||
|
|
if (filename.GetLength() < 1) {
|
||
|
|
MessageBox(L"You need to specify an ADF filename first", L"Error", MB_OK | MB_ICONEXCLAMATION);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
m_cancelButtonPressed = false;
|
||
|
|
enableDialog(false);
|
||
|
|
|
||
|
|
m_copyButton.EnableWindow(false);
|
||
|
|
m_writeButton.SetWindowText(L"Abort!");
|
||
|
|
|
||
|
|
m_iothread = new std::thread([this]() {
|
||
|
|
this->threadFinishedWriting(this->runThreadWrite());
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
// Browse to select ADF file to save to
|
||
|
|
void CArduinoFloppyReaderWinDlg::OnBnClickedBrowse()
|
||
|
|
{
|
||
|
|
// szFilters is a text string that includes two file name filters:
|
||
|
|
TCHAR szFilters[] = _T("Amiga Disk Files (*.adf)|*.adf|All Files (*.*)|*.*||");
|
||
|
|
TCHAR szFiltersSCP[] = _T("Supercard Pro Files (*.scp)|*.scp|All Files (*.*)|*.*||");
|
||
|
|
|
||
|
|
bool isSCP = m_fileFormat.GetCurSel() == 1;
|
||
|
|
|
||
|
|
// Create an Save dialog
|
||
|
|
CString oldFileName;
|
||
|
|
m_outputADF.GetWindowText(oldFileName);
|
||
|
|
|
||
|
|
CFileDialog fileDlg(FALSE, isSCP ? _T("scp") : _T("adf"), oldFileName, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST | OFN_NODEREFERENCELINKS | OFN_ENABLESIZING | OFN_DONTADDTORECENT | OFN_EXPLORER, isSCP ? szFiltersSCP : szFilters, this);
|
||
|
|
if (isSCP) fileDlg.m_ofn.lpstrTitle = L"Save Disk to SCP File"; else fileDlg.m_ofn.lpstrTitle = L"Save Disk to ADF File";
|
||
|
|
|
||
|
|
// Display it
|
||
|
|
if (fileDlg.DoModal() == IDOK)
|
||
|
|
m_outputADF.SetWindowText(fileDlg.GetPathName());
|
||
|
|
}
|
||
|
|
|
||
|
|
// Browse to load and copy to disk
|
||
|
|
void CArduinoFloppyReaderWinDlg::OnBnClickedBrowse2()
|
||
|
|
{
|
||
|
|
// szFilters is a text string that includes two file name filters:
|
||
|
|
TCHAR szFilters[] = _T("Amiga Disk Files (*.adf)|*.adf|All Files (*.*)|*.*||");
|
||
|
|
|
||
|
|
// Create an Save dialog
|
||
|
|
CString oldFileName;
|
||
|
|
m_inputADF.GetWindowText(oldFileName);
|
||
|
|
|
||
|
|
CFileDialog fileDlg(TRUE, _T("adf"), oldFileName, OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_NODEREFERENCELINKS | OFN_ENABLESIZING | OFN_EXPLORER, szFilters, this);
|
||
|
|
fileDlg.m_ofn.lpstrTitle = L"Select an ADF File to Write To Disk";
|
||
|
|
|
||
|
|
// Display it
|
||
|
|
if (fileDlg.DoModal() == IDOK)
|
||
|
|
m_inputADF.SetWindowText(fileDlg.GetPathName());
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
#pragma endregion MAIN_DIALOG
|
||
|
|
|
||
|
|
// The Run Diagnostics button
|
||
|
|
void CArduinoFloppyReaderWinDlg::OnBnClickedStartstop3()
|
||
|
|
{
|
||
|
|
std::wstring comPort = getComPort();
|
||
|
|
saveComPort();
|
||
|
|
|
||
|
|
CDiagnosticsDialog dlgDiagnostics(comPort);
|
||
|
|
dlgDiagnostics.DoModal();
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
afx_msg LRESULT CArduinoFloppyReaderWinDlg::OnDevicechange(WPARAM wParam, LPARAM lParam)
|
||
|
|
{
|
||
|
|
std::wstring port = getComPort();
|
||
|
|
enumComPorts();
|
||
|
|
setComPort(port);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
void CArduinoFloppyReaderWinDlg::OnCbnSelchangeDiskformat()
|
||
|
|
{
|
||
|
|
// Correct the file extension
|
||
|
|
bool isSCP = m_fileFormat.GetCurSel() == 1;
|
||
|
|
CString filename;
|
||
|
|
|
||
|
|
m_outputADF.GetWindowText(filename);
|
||
|
|
|
||
|
|
int pos = filename.ReverseFind(_T('.'));
|
||
|
|
if (pos > 0) {
|
||
|
|
filename = filename.Left(pos);
|
||
|
|
|
||
|
|
if (isSCP) filename += _T(".scp"); else filename += _T(".adf");
|
||
|
|
|
||
|
|
m_outputADF.SetWindowText(filename);
|
||
|
|
|
||
|
|
}
|
||
|
|
}
|