1 // Copyright (C) 2014 Michael McMaster <michael@codesrc.com>
3 // This file is part of SCSI2SD.
5 // SCSI2SD is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
10 // SCSI2SD is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with SCSI2SD. If not, see <http://www.gnu.org/licenses/>.
19 // For compilers that support precompilation, includes "wx/wx.h".
20 #include <wx/wxprec.h>
26 #include <wx/filedlg.h>
27 #include <wx/filefn.h>
28 #include <wx/filename.h>
30 #include <wx/notebook.h>
31 #include <wx/progdlg.h>
33 #include <wx/wfstream.h>
34 #include <wx/windowptr.h>
35 #include <wx/stdpaths.h>
36 #include <wx/stream.h>
37 #include <wx/thread.h>
38 #include <wx/txtstrm.h>
42 #include "ConfigUtil.hh"
43 #include "BoardPanel.hh"
44 #include "TargetPanel.hh"
45 #include "SCSI2SD_HID.hh"
48 #include "terminalwx.h"
56 #if __cplusplus >= 201103L
59 using std::shared_ptr
;
63 using std::tr1::shared_ptr
;
66 using namespace SCSI2SD
;
71 void setProgressDialog(
72 const wxWindowPtr
<wxGenericProgressDialog
>& dlg
,
75 myProgressDialog
= dlg
;
80 void clearProgressDialog()
82 myProgressDialog
->Show(false);
83 myProgressDialog
.reset();
86 void update(unsigned char arrayId
, unsigned short rowNum
)
88 if (!myProgressDialog
) return;
93 ss
<< "Writing flash array " <<
94 static_cast<int>(arrayId
) << " row " <<
95 static_cast<int>(rowNum
);
96 wxLogMessage("%s", ss
.str());
97 myProgressDialog
->Update(myNumRows
, ss
.str());
101 wxWindowPtr
<wxGenericProgressDialog
> myProgressDialog
;
105 static ProgressWrapper TheProgressWrapper
;
108 void ProgressUpdate(unsigned char arrayId
, unsigned short rowNum
)
110 TheProgressWrapper
.update(arrayId
, rowNum
);
120 TimerLock(wxTimer
* timer
) :
122 myInterval(myTimer
->GetInterval())
129 if (myTimer
&& myInterval
> 0)
131 myTimer
->Start(myInterval
);
139 class AppFrame
: public wxFrame
143 wxFrame(NULL
, wxID_ANY
, "scsi2sd-util6", wxPoint(50, 50), wxSize(600, 700)),
144 myInitialConfig(false),
147 myConsoleProcess(NULL
)
149 wxMenu
*menuFile
= new wxMenu();
152 _("&Save to file..."),
153 _("Save settings to local file."));
157 _("Load settings from local file."));
158 menuFile
->AppendSeparator();
162 _("Load default configuration options."));
164 menuFile
->AppendSeparator();
165 myLoadButton
= menuFile
->Append(
167 _("Load from device"),
168 _("Load configuration from hardware device"));
169 mySaveButton
= menuFile
->Append(
172 _("Save configuration to hardware device"));
174 menuFile
->AppendSeparator();
177 _("&Upgrade Firmware..."),
178 _("Upgrade or inspect device firmware version."));
179 menuFile
->Append(wxID_EXIT
);
181 wxMenu
*menuWindow
= new wxMenu();
185 _("Show debug log window"));
187 wxMenu
*menuDebug
= new wxMenu();
188 mySCSILogChk
= menuDebug
->AppendCheckItem(
191 _("Log SCSI commands"));
193 mySelfTestChk
= menuDebug
->AppendCheckItem(
195 _("SCSI Standalone Self-Test"),
196 _("SCSI Standalone Self-Test"));
198 wxMenu
*menuHelp
= new wxMenu();
199 menuHelp
->Append(wxID_ABOUT
);
201 wxMenuBar
*menuBar
= new wxMenuBar();
202 menuBar
->Append( menuFile
, _("&File") );
203 menuBar
->Append( menuDebug
, _("&Debug") );
204 menuBar
->Append( menuWindow
, _("&Window") );
205 menuBar
->Append( menuHelp
, _("&Help") );
206 SetMenuBar( menuBar
);
211 wxPanel
* cfgPanel
= new wxPanel(this);
212 wxFlexGridSizer
*fgs
= new wxFlexGridSizer(3, 1, 15, 15);
213 cfgPanel
->SetSizer(fgs
);
215 // Empty space below menu bar.
216 fgs
->Add(5, 5, wxALL
);
218 wxNotebook
* tabs
= new wxNotebook(cfgPanel
, ID_Notebook
);
219 myBoardPanel
= new BoardPanel(tabs
, ConfigUtil::DefaultBoardConfig());
220 tabs
->AddPage(myBoardPanel
, _("General Settings"));
221 for (int i
= 0; i
< S2S_MAX_TARGETS
; ++i
)
223 TargetPanel
* target
=
224 new TargetPanel(tabs
, ConfigUtil::Default(i
));
225 myTargets
.push_back(target
);
226 std::stringstream ss
;
227 ss
<< "Device " << (i
+ 1);
228 tabs
->AddPage(target
, ss
.str());
238 Fit(); // Needed to reduce window size on Windows
240 FitInside(); // Needed on Linux to prevent status bar overlap
243 myLogWindow
= new wxLogWindow(this, _("scsi2sd-util6 debug log"), true);
244 myLogWindow
->PassMessages(false); // Prevent messagebox popups
246 myTimer
= new wxTimer(this, ID_Timer
);
247 myTimer
->Start(64); //ms, suitable for scsi debug logging
252 wxLogWindow
* myLogWindow
;
253 BoardPanel
* myBoardPanel
;
254 std::vector
<TargetPanel
*> myTargets
;
255 wxMenuItem
* mySCSILogChk
;
256 wxMenuItem
* mySelfTestChk
;
257 wxMenuItem
* myLoadButton
;
258 wxMenuItem
* mySaveButton
;
260 shared_ptr
<HID
> myHID
;
261 bool myInitialConfig
;
263 uint8_t myTickCounter
;
265 time_t myLastPollTime
;
267 wxWindowPtr
<TerminalWx
> myConsoleTerm
;
268 shared_ptr
<wxProcess
> myConsoleProcess
;
269 wxInputStream
* myConsoleStdout
;
270 wxInputStream
* myConsoleStderr
;
273 void mmLogStatus(const std::string
& msg
)
275 // We set PassMessages to false on our log window to prevent popups, but
276 // this also prevents wxLogStatus from updating the status bar.
278 wxLogMessage(this, "%s", msg
.c_str());
281 void onConfigChanged(wxCommandEvent
& event
)
290 // Check for duplicate SCSI IDs
291 std::set
<uint8_t> enabledID
;
293 // Check for overlapping SD sectors.
294 std::vector
<std::pair
<uint32_t, uint64_t> > sdSectors
;
296 bool isTargetEnabled
= false; // Need at least one enabled
297 uint32_t autoStartSector
= 0;
298 for (size_t i
= 0; i
< myTargets
.size(); ++i
)
300 myTargets
[i
]->setAutoStartSector(autoStartSector
);
301 valid
= myTargets
[i
]->evaluate() && valid
;
303 if (myTargets
[i
]->isEnabled())
305 isTargetEnabled
= true;
306 uint8_t scsiID
= myTargets
[i
]->getSCSIId();
307 if (enabledID
.find(scsiID
) != enabledID
.end())
309 myTargets
[i
]->setDuplicateID(true);
314 enabledID
.insert(scsiID
);
315 myTargets
[i
]->setDuplicateID(false);
318 auto sdSectorRange
= myTargets
[i
]->getSDSectorRange();
319 for (auto it(sdSectors
.begin()); it
!= sdSectors
.end(); ++it
)
321 if (sdSectorRange
.first
< it
->second
&&
322 sdSectorRange
.second
> it
->first
)
325 myTargets
[i
]->setSDSectorOverlap(true);
329 myTargets
[i
]->setSDSectorOverlap(false);
332 sdSectors
.push_back(sdSectorRange
);
333 autoStartSector
= sdSectorRange
.second
;
337 myTargets
[i
]->setDuplicateID(false);
338 myTargets
[i
]->setSDSectorOverlap(false);
342 valid
= valid
&& isTargetEnabled
; // Need at least one.
344 mySaveButton
->Enable(valid
&& myHID
);
346 myLoadButton
->Enable(static_cast<bool>(myHID
));
352 ID_ConfigDefaults
= wxID_HIGHEST
+ 1,
366 void OnID_ConfigDefaults(wxCommandEvent
& event
)
368 myBoardPanel
->setConfig(ConfigUtil::DefaultBoardConfig());
369 for (size_t i
= 0; i
< myTargets
.size(); ++i
)
371 myTargets
[i
]->setConfig(ConfigUtil::Default(i
));
375 void OnID_SaveFile(wxCommandEvent
& event
)
377 TimerLock
lock(myTimer
);
383 "Save config settings",
386 "XML files (*.xml)|*.xml",
387 wxFD_SAVE
| wxFD_OVERWRITE_PROMPT
);
388 if (dlg
.ShowModal() == wxID_CANCEL
) return;
390 wxFileOutputStream
file(dlg
.GetPath());
393 wxLogError("Cannot save settings to file '%s'.", dlg
.GetPath());
397 wxTextOutputStream
s(file
);
401 s
<< ConfigUtil::toXML(myBoardPanel
->getConfig());
402 for (size_t i
= 0; i
< myTargets
.size(); ++i
)
404 s
<< ConfigUtil::toXML(myTargets
[i
]->getConfig());
410 void OnID_OpenFile(wxCommandEvent
& event
)
412 TimerLock
lock(myTimer
);
416 "Load config settings",
419 "XML files (*.xml)|*.xml",
420 wxFD_OPEN
| wxFD_FILE_MUST_EXIST
);
421 if (dlg
.ShowModal() == wxID_CANCEL
) return;
425 std::pair
<S2S_BoardCfg
, std::vector
<S2S_TargetCfg
>> configs(
426 ConfigUtil::fromXML(std::string(dlg
.GetPath())));
428 myBoardPanel
->setConfig(configs
.first
);
431 for (i
= 0; i
< configs
.second
.size() && i
< myTargets
.size(); ++i
)
433 myTargets
[i
]->setConfig(configs
.second
[i
]);
436 for (; i
< myTargets
.size(); ++i
)
438 myTargets
[i
]->setConfig(ConfigUtil::Default(i
));
441 catch (std::exception
& e
)
444 "Cannot load settings from file '%s'.\n%s",
451 wxOK
| wxICON_ERROR
);
455 void OnID_Firmware(wxCommandEvent
& event
)
457 TimerLock
lock(myTimer
);
461 void OnID_LogWindow(wxCommandEvent
& event
)
466 void doFirmwareUpdate()
470 "Load firmware file",
473 "SCSI2SD Firmware files (*.dfu)|*.dfu",
474 wxFD_OPEN
| wxFD_FILE_MUST_EXIST
);
475 if (dlg
.ShowModal() == wxID_CANCEL
) return;
477 std::string
filename(dlg
.GetPath());
478 wxLogMessage("Attempting firmware update from file %s", filename
);
480 wxWindowPtr
<wxGenericProgressDialog
> progress(
481 new wxGenericProgressDialog(
482 "Searching for bootloader",
483 "Searching for bootloader",
486 wxPD_AUTO_HIDE
| wxPD_CAN_ABORT
)
488 mmLogStatus("Searching for bootloader");
489 bool versionChecked
= false;
494 if (!myHID
) myHID
.reset(HID::Open());
497 if (!myHID
->isCorrectFirmware(filename
))
500 "Firmware does not match device hardware",
502 wxOK
| wxICON_ERROR
);
505 versionChecked
= true;
507 mmLogStatus("Resetting SCSI2SD into bootloader");
509 myHID
->enterBootloader();
514 if (myDfu
.hasDevice() && !versionChecked
)
516 mmLogStatus("STM DFU Bootloader found, checking compatibility");
518 if (!checkVersionMarker(filename
))
521 "Firmware does not match device hardware",
523 wxOK
| wxICON_ERROR
);
526 versionChecked
= true;
529 if (myDfu
.hasDevice())
531 mmLogStatus("STM DFU Bootloader found");
533 doDFUUpdate(filename
);
537 catch (std::exception
& e
)
539 mmLogStatus(e
.what());
543 if (!progress
->Pulse())
545 return; // user cancelled.
550 bool checkVersionMarker(const std::string
& firmware
)
552 std::stringstream ss
;
556 if (wxExecute("which dfu-util", wxEXEC_SYNC
) == 0)
560 wxFileName
exePath(wxStandardPaths::Get().GetExecutablePath());
561 ss
<< '"' << exePath
.GetPathWithSep() << "dfu-util\" ";
565 std::string tmpFile
=
566 wxFileName::CreateTempFileName(
567 _("SCSI2SD_MARKER"), static_cast<wxFile
*>(NULL
));
568 wxRemoveFile(tmpFile
); // dfu-util won't overwrite.
570 ss
<< "--alt 2 -s 0x1FFF7800:4 -U \"" << tmpFile
<< "\"";
572 wxLogMessage("Running: %s", ss
.str());
574 std::string cmd
= ss
.str();
575 long result
= wxExecute(
581 wxLogMessage("OTP Version check failed.");
585 // Ok, we now have a file with 8 bytes in it.
586 wxFile
file(tmpFile
);
587 if (file
.Length() != 4)
589 wxLogMessage("OTP Version check file isn't 4 bytes.");
594 if (file
.Read(data
, sizeof(data
)) != sizeof(data
))
597 "Couldn't read file",
598 "Couldn't read file",
599 wxOK
| wxICON_ERROR
);
602 wxRemoveFile(tmpFile
);
605 (((uint32_t)(data
[0]))) |
606 (((uint32_t)(data
[1])) << 8) |
607 (((uint32_t)(data
[2])) << 16) |
608 (((uint32_t)(data
[3])) << 24);
609 if (value
== 0xFFFFFFFF)
612 wxLogMessage("OTP Hardware version not set. Ignoring.");
615 else if (value
== 0x06002020)
617 wxLogMessage("Found V6 2020 hardware marker");
618 return firmware
.rfind("firmware.V6.2020.dfu") != std::string::npos
;
620 else if (value
== 0x06002019)
622 wxLogMessage("Found V6 revF hardware marker");
623 return firmware
.rfind("firmware.V6.revF.dfu") != std::string::npos
||
624 firmware
.rfind("firmware.dfu") != std::string::npos
;
628 wxLogMessage("Found unknown hardware marker: %u", value
);
629 return false; // Some unknown version.
634 void doDFUUpdate(const std::string
& filename
)
636 if (filename
.find(".dfu") == std::string::npos
)
640 "SCSI2SD V6 requires a .dfu file",
641 wxOK
| wxICON_ERROR
);
645 std::stringstream ss
;
647 ss
<< "dfu-util --download \""
648 << filename
.c_str() << "\" --alt 0 --reset";
650 if (wxExecute("which dfu-util", wxEXEC_SYNC
) == 0)
654 wxFileName
exePath(wxStandardPaths::Get().GetExecutablePath());
655 ss
<< '"' << exePath
.GetPathWithSep() << "dfu-util\" ";
657 ss
<< "--download \"" << filename
.c_str() << "\" --alt 0 --reset";
660 wxLogMessage("Running: %s", ss
.str());
662 myConsoleProcess
.reset(new wxProcess(this));
663 myConsoleProcess
->Redirect();
664 std::string cmd
= ss
.str();
665 long result
= wxExecute(
668 myConsoleProcess
.get()
674 "Firmware update failed (dfu-util not found ?) Command = " + cmd
,
675 wxOK
| wxICON_ERROR
);
676 mmLogStatus("Firmware update failed");
677 myConsoleProcess
.reset();
679 myConsoleStdout
= myConsoleProcess
->GetInputStream();
680 myConsoleStderr
= myConsoleProcess
->GetErrorStream();
681 wxFrame
* frame(new wxFrame(this, wxID_ANY
, "dfu-util"));
682 myConsoleTerm
.reset(new TerminalWx(frame
, wxID_ANY
, wxDefaultPosition
));
688 void redirectDfuOutput()
690 if (myConsoleProcess
)
692 std::stringstream ss
;
693 while (myConsoleStderr
&& !myConsoleStderr
->Eof() && myConsoleStderr
->CanRead())
695 int c
= myConsoleStderr
->GetC();
705 while (myConsoleStdout
&& !myConsoleStdout
->Eof() && myConsoleStdout
->CanRead())
707 int c
= myConsoleStdout
->GetC();
717 myConsoleTerm
->DisplayCharsUnsafe(ss
.str());
721 void doFinishDfu(wxProcessEvent
& event
)
725 if (event
.GetExitCode() == 0)
729 "Firmware update complete. Please reconnect USB cable.",
730 wxOK
| wxICON_ERROR
);
731 mmLogStatus("Firmware update succeeded");
735 "Firmware update failed.",
736 wxOK
| wxICON_ERROR
);
737 mmLogStatus("Firmware update failed");
740 myConsoleStdout
= myConsoleStderr
= NULL
;
741 myConsoleProcess
.reset();
742 myConsoleTerm
->GetParent()->Close();
743 myConsoleTerm
->Close();
744 myConsoleTerm
.reset();
748 void dumpSCSICommand(std::vector
<uint8_t> buf
)
750 std::stringstream msg
;
752 for (size_t i
= 0; i
< 32 && i
< buf
.size(); ++i
)
754 msg
<< std::setfill('0') << std::setw(2) <<
755 static_cast<int>(buf
[i
]) << ' ';
757 wxLogMessage(this, msg
.str().c_str());
762 if (!mySCSILogChk
->IsChecked() ||
769 std::vector
<uint8_t> info
;
770 if (myHID
->readSCSIDebugInfo(info
))
772 dumpSCSICommand(info
);
775 catch (std::exception
& e
)
777 wxLogWarning(this, e
.what());
782 void OnID_Timer(wxTimerEvent
& event
)
787 time_t now
= time(NULL
);
788 if (now
== myLastPollTime
) return;
789 myLastPollTime
= now
;
791 // Check if we are connected to the HID device.
794 if (myHID
&& !myHID
->ping())
796 // Verify the USB HID connection is valid
802 myHID
.reset(HID::Open());
805 std::stringstream msg
;
806 msg
<< "SCSI2SD Ready, firmware version " <<
807 myHID
->getFirmwareVersionStr();
808 mmLogStatus(msg
.str());
810 std::stringstream devInfo
;
811 devInfo
<< "Hardware version: " <<
812 myHID
->getHardwareVersion() << std::endl
<<
814 myHID
->getSerialNumber();
815 wxLogMessage(this, "%s", devInfo
.str());
817 std::vector
<uint8_t> csd(myHID
->getSD_CSD());
818 std::vector
<uint8_t> cid(myHID
->getSD_CID());
819 std::stringstream sdinfo
;
820 sdinfo
<< "SD Capacity (512-byte sectors): " <<
821 myHID
->getSDCapacity() << std::endl
;
823 sdinfo
<< "SD CSD Register: ";
824 for (size_t i
= 0; i
< csd
.size(); ++i
)
827 std::hex
<< std::setfill('0') << std::setw(2) <<
828 static_cast<int>(csd
[i
]);
831 sdinfo
<< "SD CID Register: ";
832 for (size_t i
= 0; i
< cid
.size(); ++i
)
835 std::hex
<< std::setfill('0') << std::setw(2) <<
836 static_cast<int>(cid
[i
]);
839 wxLogMessage(this, "%s", sdinfo
.str());
841 if (mySelfTestChk
->IsChecked())
843 std::stringstream scsiInfo
;
845 scsiInfo
<< "SCSI Self-Test: ";
846 if (myHID
->scsiSelfTest(errcode
))
848 scsiInfo
<< "Passed";
852 scsiInfo
<< "FAIL (" << errcode
<< ")";
854 wxLogMessage(this, "%s", scsiInfo
.str());
857 if (!myInitialConfig
)
859 /* This doesn't work properly, and causes crashes.
860 wxCommandEvent loadEvent(wxEVT_NULL, ID_BtnLoad);
861 GetEventHandler()->AddPendingEvent(loadEvent);
868 char ticks
[] = {'/', '-', '\\', '|'};
869 std::stringstream ss
;
870 ss
<< "Searching for SCSI2SD device " << ticks
[myTickCounter
% sizeof(ticks
)];
872 SetStatusText(ss
.str());
876 catch (std::runtime_error
& e
)
878 std::cerr
<< e
.what() << std::endl
;
879 mmLogStatus(e
.what());
885 void doLoad(wxCommandEvent
& event
)
887 TimerLock
lock(myTimer
);
890 mmLogStatus("Loading configuration");
892 wxWindowPtr
<wxGenericProgressDialog
> progress(
893 new wxGenericProgressDialog(
894 "Load config settings",
895 "Loading config settings",
898 wxPD_CAN_ABORT
| wxPD_REMAINING_TIME
)
901 int currentProgress
= 0;
902 int totalProgress
= 2;
904 std::vector
<uint8_t> cfgData(S2S_CFG_SIZE
);
905 uint32_t sector
= myHID
->getSDCapacity() - 2;
906 for (size_t i
= 0; i
< 2; ++i
)
908 std::stringstream ss
;
909 ss
<< "Reading sector " << sector
;
910 mmLogStatus(ss
.str());
911 currentProgress
+= 1;
912 if (currentProgress
== totalProgress
)
914 ss
.str("Load Complete.");
915 mmLogStatus("Load Complete.");
918 if (!progress
->Update(
919 (100 * currentProgress
) / totalProgress
,
927 std::vector
<uint8_t> sdData
;
931 myHID
->readSector(sector
++, sdData
);
933 catch (std::runtime_error
& e
)
935 mmLogStatus(e
.what());
945 myBoardPanel
->setConfig(ConfigUtil::boardConfigFromBytes(&cfgData
[0]));
946 for (int i
= 0; i
< S2S_MAX_TARGETS
; ++i
)
948 myTargets
[i
]->setConfig(
949 ConfigUtil::fromBytes(
950 &cfgData
[sizeof(S2S_BoardCfg
) + i
* sizeof(S2S_TargetCfg
)]
955 myInitialConfig
= true;
959 mmLogStatus("Load failed");
960 progress
->Update(100, "Load failed");
964 mmLogStatus("Load Aborted");
971 void doSave(wxCommandEvent
& event
)
973 TimerLock
lock(myTimer
);
976 mmLogStatus("Saving configuration");
978 wxWindowPtr
<wxGenericProgressDialog
> progress(
979 new wxGenericProgressDialog(
980 "Save config settings",
981 "Saving config settings",
984 wxPD_CAN_ABORT
| wxPD_REMAINING_TIME
)
988 int currentProgress
= 0;
989 int totalProgress
= 2;
991 std::vector
<uint8_t> cfgData(
992 ConfigUtil::boardConfigToBytes(myBoardPanel
->getConfig())
994 for (int i
= 0; i
< S2S_MAX_TARGETS
; ++i
)
996 std::vector
<uint8_t> raw(
997 ConfigUtil::toBytes(myTargets
[i
]->getConfig())
999 cfgData
.insert(cfgData
.end(), raw
.begin(), raw
.end());
1002 uint32_t sector
= myHID
->getSDCapacity() - 2;
1004 for (size_t i
= 0; i
< 2; ++i
)
1006 std::stringstream ss
;
1007 ss
<< "Writing SD sector " << sector
;
1008 mmLogStatus(ss
.str());
1009 currentProgress
+= 1;
1011 if (currentProgress
== totalProgress
)
1013 ss
.str("Save Complete.");
1014 mmLogStatus("Save Complete.");
1016 if (!progress
->Update(
1017 (100 * currentProgress
) / totalProgress
,
1027 std::vector
<uint8_t> buf
;
1028 buf
.insert(buf
.end(), &cfgData
[i
* 512], &cfgData
[(i
+1) * 512]);
1029 myHID
->writeSector(sector
++, buf
);
1031 catch (std::runtime_error
& e
)
1033 mmLogStatus(e
.what());
1043 mmLogStatus("Save failed");
1044 progress
->Update(100, "Save failed");
1048 mmLogStatus("Save Aborted");
1055 // Note: Don't confuse this with the wxApp::OnExit virtual method
1056 void OnExitEvt(wxCommandEvent
& event
);
1058 void OnCloseEvt(wxCloseEvent
& event
);
1060 void OnAbout(wxCommandEvent
& event
)
1063 "SCSI2SD (scsi2sd-util6)\n"
1064 "Copyright (C) 2014-2017 Michael McMaster <michael@codesrc.com>\n"
1066 "This program is free software: you can redistribute it and/or modify\n"
1067 "it under the terms of the GNU General Public License as published by\n"
1068 "the Free Software Foundation, either version 3 of the License, or\n"
1069 "(at your option) any later version.\n"
1071 "This program is distributed in the hope that it will be useful,\n"
1072 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
1073 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
1074 "GNU General Public License for more details.\n"
1076 "You should have received a copy of the GNU General Public License\n"
1077 "along with this program. If not, see <http://www.gnu.org/licenses/>.\n",
1079 "About scsi2sd-util6", wxOK
| wxICON_INFORMATION
);
1082 wxDECLARE_EVENT_TABLE();
1085 wxBEGIN_EVENT_TABLE(AppFrame
, wxFrame
)
1086 EVT_MENU(AppFrame::ID_ConfigDefaults
, AppFrame::OnID_ConfigDefaults
)
1087 EVT_MENU(AppFrame::ID_Firmware
, AppFrame::OnID_Firmware
)
1088 EVT_MENU(AppFrame::ID_LogWindow
, AppFrame::OnID_LogWindow
)
1089 EVT_MENU(AppFrame::ID_SaveFile
, AppFrame::OnID_SaveFile
)
1090 EVT_MENU(AppFrame::ID_OpenFile
, AppFrame::OnID_OpenFile
)
1091 EVT_MENU(wxID_EXIT
, AppFrame::OnExitEvt
)
1092 EVT_MENU(wxID_ABOUT
, AppFrame::OnAbout
)
1094 EVT_TIMER(AppFrame::ID_Timer
, AppFrame::OnID_Timer
)
1096 EVT_COMMAND(wxID_ANY
, ConfigChangedEvent
, AppFrame::onConfigChanged
)
1098 EVT_MENU(ID_BtnSave
, AppFrame::doSave
)
1099 EVT_MENU(ID_BtnLoad
, AppFrame::doLoad
)
1101 EVT_CLOSE(AppFrame::OnCloseEvt
)
1103 EVT_END_PROCESS(wxID_ANY
, AppFrame::doFinishDfu
)
1109 class App
: public wxApp
1112 virtual bool OnInit()
1114 AppFrame
* frame
= new AppFrame();
1116 SetTopWindow(frame
);
1123 wxIMPLEMENT_APP(App
);
1126 AppFrame::OnExitEvt(wxCommandEvent
& event
)
1128 wxGetApp().ExitMainLoop();
1132 AppFrame::OnCloseEvt(wxCloseEvent
& event
)
1134 wxGetApp().ExitMainLoop();