Lots of bug fixing, and 4.7 merges
[SCSI2SD-V6.git] / src / scsi2sd-util6 / scsi2sd-util.cc
1 //      Copyright (C) 2014 Michael McMaster <michael@codesrc.com>
2 //
3 //      This file is part of SCSI2SD.
4 //
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.
9 //
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.
14 //
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/>.
17
18
19 // For compilers that support precompilation, includes "wx/wx.h".
20 #include <wx/wxprec.h>
21 #ifndef WX_PRECOMP
22 #include <wx/wx.h>
23 #endif
24
25 #include <wx/app.h>
26 #include <wx/filedlg.h>
27 #include <wx/filefn.h>
28 #include <wx/filename.h>
29 #include <wx/log.h>
30 #include <wx/notebook.h>
31 #include <wx/progdlg.h>
32 #include <wx/utils.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>
39
40 #include <zipper.hh>
41
42 #include "ConfigUtil.hh"
43 #include "BoardPanel.hh"
44 #include "TargetPanel.hh"
45 #include "SCSI2SD_HID.hh"
46 #include "Dfu.hh"
47
48 #include "terminalwx.h"
49
50 #include <algorithm>
51 #include <iomanip>
52 #include <vector>
53 #include <set>
54 #include <sstream>
55
56 #if __cplusplus >= 201103L
57 #include <cstdint>
58 #include <memory>
59 using std::shared_ptr;
60 #else
61 #include <stdint.h>
62 #include <tr1/memory>
63 using std::tr1::shared_ptr;
64 #endif
65
66 using namespace SCSI2SD;
67
68 class ProgressWrapper
69 {
70 public:
71         void setProgressDialog(
72                 const wxWindowPtr<wxGenericProgressDialog>& dlg,
73                 size_t maxRows)
74         {
75                 myProgressDialog = dlg;
76                 myMaxRows = maxRows;
77                 myNumRows = 0;
78         }
79
80         void clearProgressDialog()
81         {
82                 myProgressDialog->Show(false);
83                 myProgressDialog.reset();
84         }
85
86         void update(unsigned char arrayId, unsigned short rowNum)
87         {
88                 if (!myProgressDialog) return;
89
90                 myNumRows++;
91
92                 std::stringstream ss;
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());
98         }
99
100 private:
101         wxWindowPtr<wxGenericProgressDialog> myProgressDialog;
102         size_t myMaxRows;
103         size_t myNumRows;
104 };
105 static ProgressWrapper TheProgressWrapper;
106
107 extern "C"
108 void ProgressUpdate(unsigned char arrayId, unsigned short rowNum)
109 {
110         TheProgressWrapper.update(arrayId, rowNum);
111 }
112
113 namespace
114 {
115
116
117 class TimerLock
118 {
119 public:
120         TimerLock(wxTimer* timer) :
121                 myTimer(timer),
122                 myInterval(myTimer->GetInterval())
123         {
124                 myTimer->Stop();
125         };
126
127         virtual ~TimerLock()
128         {
129                 if (myTimer && myInterval > 0)
130                 {
131                         myTimer->Start(myInterval);
132                 }
133         }
134 private:
135         wxTimer* myTimer;
136         int myInterval;
137 };
138
139 class AppFrame : public wxFrame
140 {
141 public:
142         AppFrame() :
143                 wxFrame(NULL, wxID_ANY, "scsi2sd-util6", wxPoint(50, 50), wxSize(600, 700)),
144                 myInitialConfig(false),
145                 myTickCounter(0),
146                 myLastPollTime(0),
147                 myConsoleProcess(NULL)
148         {
149                 wxMenu *menuFile = new wxMenu();
150                 menuFile->Append(
151                         ID_SaveFile,
152                         _("&Save to file..."),
153                         _("Save settings to local file."));
154                 menuFile->Append(
155                         ID_OpenFile,
156                         _("&Open file..."),
157                         _("Load settings from local file."));
158                 menuFile->AppendSeparator();
159                 menuFile->Append(
160                         ID_ConfigDefaults,
161                         _("Load &Defaults"),
162                         _("Load default configuration options."));
163
164                 menuFile->AppendSeparator();
165                 myLoadButton = menuFile->Append(
166                         ID_BtnLoad,
167                         _("Load from device"),
168                         _("Load configuration from hardware device"));
169                 mySaveButton = menuFile->Append(
170                         ID_BtnSave,
171                         _("Save to device"),
172                         _("Save configuration to hardware device"));
173
174                 menuFile->AppendSeparator();
175                 menuFile->Append(
176                         ID_Firmware,
177                         _("&Upgrade Firmware..."),
178                         _("Upgrade or inspect device firmware version."));
179                 menuFile->Append(wxID_EXIT);
180
181                 wxMenu *menuWindow= new wxMenu();
182                 menuWindow->Append(
183                         ID_LogWindow,
184                         _("Show &Log"),
185                         _("Show debug log window"));
186
187                 wxMenu *menuDebug = new wxMenu();
188                 mySCSILogChk = menuDebug->AppendCheckItem(
189                         ID_SCSILog,
190                         _("Log SCSI data"),
191                         _("Log SCSI commands"));
192
193                 mySelfTestChk = menuDebug->AppendCheckItem(
194                         ID_SelfTest,
195                         _("SCSI Standalone Self-Test"),
196                         _("SCSI Standalone Self-Test"));
197
198                 wxMenu *menuHelp = new wxMenu();
199                 menuHelp->Append(wxID_ABOUT);
200
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 );
207
208                 CreateStatusBar();
209
210                 {
211                         wxPanel* cfgPanel = new wxPanel(this);
212                         wxFlexGridSizer *fgs = new wxFlexGridSizer(3, 1, 15, 15);
213                         cfgPanel->SetSizer(fgs);
214
215                         // Empty space below menu bar.
216                         fgs->Add(5, 5, wxALL);
217
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)
222                         {
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());
229                                 target->Fit();
230                         }
231                         tabs->Fit();
232                         fgs->Add(tabs);
233
234                         cfgPanel->Fit();
235                 }
236
237 #ifdef __WINDOWS__
238                 Fit(); // Needed to reduce window size on Windows
239 #else
240                 FitInside(); // Needed on Linux to prevent status bar overlap
241 #endif
242
243                 myLogWindow = new wxLogWindow(this, _("scsi2sd-util6 debug log"), true);
244                 myLogWindow->PassMessages(false); // Prevent messagebox popups
245
246                 myTimer = new wxTimer(this, ID_Timer);
247                 myTimer->Start(64); //ms, suitable for scsi debug logging
248         }
249
250 private:
251         Dfu myDfu;
252         wxLogWindow* myLogWindow;
253         BoardPanel* myBoardPanel;
254         std::vector<TargetPanel*> myTargets;
255         wxMenuItem* mySCSILogChk;
256         wxMenuItem* mySelfTestChk;
257         wxMenuItem* myLoadButton;
258         wxMenuItem* mySaveButton;
259         wxTimer* myTimer;
260         shared_ptr<HID> myHID;
261         bool myInitialConfig;
262
263         uint8_t myTickCounter;
264
265         time_t myLastPollTime;
266
267         wxWindowPtr<TerminalWx> myConsoleTerm;
268         shared_ptr<wxProcess> myConsoleProcess;
269         wxInputStream* myConsoleStdout;
270         wxInputStream* myConsoleStderr;
271         
272
273         void mmLogStatus(const std::string& msg)
274         {
275                 // We set PassMessages to false on our log window to prevent popups, but
276                 // this also prevents wxLogStatus from updating the status bar.
277                 SetStatusText(msg);
278                 wxLogMessage(this, "%s", msg.c_str());
279         }
280
281         void onConfigChanged(wxCommandEvent& event)
282         {
283                 evaluate();
284         }
285
286         void evaluate()
287         {
288                 bool valid = true;
289
290                 // Check for duplicate SCSI IDs
291                 std::set<uint8_t> enabledID;
292
293                 // Check for overlapping SD sectors.
294                 std::vector<std::pair<uint32_t, uint64_t> > sdSectors;
295
296                 bool isTargetEnabled = false; // Need at least one enabled
297                 uint32_t autoStartSector = 0;
298                 for (size_t i = 0; i < myTargets.size(); ++i)
299                 {
300                         myTargets[i]->setAutoStartSector(autoStartSector);
301                         valid = myTargets[i]->evaluate() && valid;
302
303                         if (myTargets[i]->isEnabled())
304                         {
305                                 isTargetEnabled = true;
306                                 uint8_t scsiID = myTargets[i]->getSCSIId();
307                                 if (enabledID.find(scsiID) != enabledID.end())
308                                 {
309                                         myTargets[i]->setDuplicateID(true);
310                                         valid = false;
311                                 }
312                                 else
313                                 {
314                                         enabledID.insert(scsiID);
315                                         myTargets[i]->setDuplicateID(false);
316                                 }
317
318                                 auto sdSectorRange = myTargets[i]->getSDSectorRange();
319                                 for (auto it(sdSectors.begin()); it != sdSectors.end(); ++it)
320                                 {
321                                         if (sdSectorRange.first < it->second &&
322                                                 sdSectorRange.second > it->first)
323                                         {
324                                                 valid = false;
325                                                 myTargets[i]->setSDSectorOverlap(true);
326                                         }
327                                         else
328                                         {
329                                                 myTargets[i]->setSDSectorOverlap(false);
330                                         }
331                                 }
332                                 sdSectors.push_back(sdSectorRange);
333                                 autoStartSector = sdSectorRange.second;
334                         }
335                         else
336                         {
337                                 myTargets[i]->setDuplicateID(false);
338                                 myTargets[i]->setSDSectorOverlap(false);
339                         }
340                 }
341
342                 valid = valid && isTargetEnabled; // Need at least one.
343
344                 mySaveButton->Enable(valid && myHID);
345
346                 myLoadButton->Enable(static_cast<bool>(myHID));
347         }
348
349
350         enum
351         {
352                 ID_ConfigDefaults = wxID_HIGHEST + 1,
353                 ID_Firmware,
354                 ID_Timer,
355                 ID_Notebook,
356                 ID_BtnLoad,
357                 ID_BtnSave,
358                 ID_LogWindow,
359                 ID_SCSILog,
360                 ID_SelfTest,
361                 ID_SaveFile,
362                 ID_OpenFile,
363                 ID_ConsoleTerm
364         };
365
366         void OnID_ConfigDefaults(wxCommandEvent& event)
367         {
368                 myBoardPanel->setConfig(ConfigUtil::DefaultBoardConfig());
369                 for (size_t i = 0; i < myTargets.size(); ++i)
370                 {
371                         myTargets[i]->setConfig(ConfigUtil::Default(i));
372                 }
373         }
374
375         void OnID_SaveFile(wxCommandEvent& event)
376         {
377                 TimerLock lock(myTimer);
378
379
380
381                 wxFileDialog dlg(
382                         this,
383                         "Save config settings",
384                         "",
385                         "",
386                         "XML files (*.xml)|*.xml",
387                         wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
388                 if (dlg.ShowModal() == wxID_CANCEL) return;
389
390                 wxFileOutputStream file(dlg.GetPath());
391                 if (!file.IsOk())
392                 {
393                         wxLogError("Cannot save settings to file '%s'.", dlg.GetPath());
394                         return;
395                 }
396
397                 wxTextOutputStream s(file);
398
399                 s << "<SCSI2SD>\n";
400
401                 s << ConfigUtil::toXML(myBoardPanel->getConfig());
402                 for (size_t i = 0; i < myTargets.size(); ++i)
403                 {
404                         s << ConfigUtil::toXML(myTargets[i]->getConfig());
405                 }
406
407                 s << "</SCSI2SD>\n";
408         }
409
410         void OnID_OpenFile(wxCommandEvent& event)
411         {
412                 TimerLock lock(myTimer);
413
414                 wxFileDialog dlg(
415                         this,
416                         "Load config settings",
417                         "",
418                         "",
419                         "XML files (*.xml)|*.xml",
420                         wxFD_OPEN | wxFD_FILE_MUST_EXIST);
421                 if (dlg.ShowModal() == wxID_CANCEL) return;
422
423                 try
424                 {
425                         std::pair<S2S_BoardCfg, std::vector<S2S_TargetCfg>> configs(
426                                 ConfigUtil::fromXML(std::string(dlg.GetPath())));
427
428                         myBoardPanel->setConfig(configs.first);
429
430                         size_t i;
431                         for (i = 0; i < configs.second.size() && i < myTargets.size(); ++i)
432                         {
433                                 myTargets[i]->setConfig(configs.second[i]);
434                         }
435
436                         for (; i < myTargets.size(); ++i)
437                         {
438                                 myTargets[i]->setConfig(ConfigUtil::Default(i));
439                         }
440                 }
441                 catch (std::exception& e)
442                 {
443                         wxLogError(
444                                 "Cannot load settings from file '%s'.\n%s",
445                                 dlg.GetPath(),
446                                 e.what());
447
448                         wxMessageBox(
449                                 e.what(),
450                                 "Load error",
451                                 wxOK | wxICON_ERROR);
452                 }
453         }
454
455         void OnID_Firmware(wxCommandEvent& event)
456         {
457                 TimerLock lock(myTimer);
458                 doFirmwareUpdate();
459         }
460
461         void OnID_LogWindow(wxCommandEvent& event)
462         {
463                 myLogWindow->Show();
464         }
465
466         void doFirmwareUpdate()
467         {
468                 wxFileDialog dlg(
469                         this,
470                         "Load firmware file",
471                         "",
472                         "",
473                         "SCSI2SD Firmware files (*.dfu)|*.dfu",
474                         wxFD_OPEN | wxFD_FILE_MUST_EXIST);
475                 if (dlg.ShowModal() == wxID_CANCEL) return;
476
477                 std::string filename(dlg.GetPath());
478
479                 wxWindowPtr<wxGenericProgressDialog> progress(
480                         new wxGenericProgressDialog(
481                                 "Searching for bootloader",
482                                 "Searching for bootloader",
483                                 100,
484                                 this,
485                                 wxPD_AUTO_HIDE | wxPD_CAN_ABORT)
486                                 );
487                 mmLogStatus("Searching for bootloader");
488                 while (true)
489                 {
490                         try
491                         {
492                                 if (!myHID) myHID.reset(HID::Open());
493                                 if (myHID)
494                                 {
495                                         mmLogStatus("Resetting SCSI2SD into bootloader");
496
497                                         myHID->enterBootloader();
498                                         myHID.reset();
499                                 }
500
501
502                                 if (myDfu.hasDevice())
503                                 {
504                                         mmLogStatus("STM DFU Bootloader found");
505                                         progress->Show(0);
506                                         doDFUUpdate(filename);
507                                         return;
508                                 }
509                         }
510                         catch (std::exception& e)
511                         {
512                                 mmLogStatus(e.what());
513                                 myHID.reset();
514                         }
515                         wxMilliSleep(100);
516                         if (!progress->Pulse())
517                         {
518                                 return; // user cancelled.
519                         }
520                 }
521         }
522
523         void doDFUUpdate(const std::string& filename)
524         {
525                 if (filename.find(".dfu") == std::string::npos)
526                 {
527                         wxMessageBox(
528                                 "Wrong filename",
529                                 "SCSI2SD V6 requires a .dfu file",
530                                 wxOK | wxICON_ERROR);
531                         return;
532                 }
533
534
535                 std::stringstream ss;
536 #ifdef __WINDOWS__
537                 ss << "dfu-util --download \""
538                         << filename.c_str() << "\" --alt 0 --reset";
539 #else
540                 if (wxExecute("which dfu-util", wxEXEC_SYNC) == 0)
541                 {
542                         ss << "dfu-util ";
543                 } else {
544                         wxFileName exePath(wxStandardPaths::Get().GetExecutablePath());
545                         ss << '"' << exePath.GetPathWithSep() << "dfu-util\" ";
546                 }
547                 ss << "--download \"" << filename.c_str() << "\" --alt 0 --reset";
548 #endif
549
550                 wxLogMessage("Running: %s", ss.str());
551
552                 myConsoleProcess.reset(new wxProcess(this));
553                 myConsoleProcess->Redirect();
554                 std::string cmd = ss.str();
555                 long result = wxExecute(
556                         cmd.c_str(),
557                         wxEXEC_ASYNC,
558                         myConsoleProcess.get()
559                         );
560                 if (!result)
561                 {
562                         wxMessageBox(
563                                 "Update failed",
564                                 "Firmware update failed (dfu-util not found ?) Command = " + cmd,
565                                 wxOK | wxICON_ERROR);
566                         mmLogStatus("Firmware update failed");
567                         myConsoleProcess.reset();
568                 } else {
569                         myConsoleStdout = myConsoleProcess->GetInputStream();
570                         myConsoleStderr = myConsoleProcess->GetErrorStream();
571                         wxFrame* frame(new wxFrame(this, wxID_ANY, "dfu-util"));
572                         myConsoleTerm.reset(new TerminalWx(frame, wxID_ANY, wxDefaultPosition));
573                         frame->Fit();
574                         frame->Show();
575                 }
576         }
577
578         void redirectDfuOutput()
579         {
580                 if (myConsoleProcess)
581                 {
582                         std::stringstream ss;
583                         while (myConsoleStderr && !myConsoleStderr->Eof() && myConsoleStderr->CanRead())
584                         {
585                                 int c = myConsoleStderr->GetC();
586                                 if (c == '\n')
587                                 {
588                                         ss << "\r\n";
589                                 }
590                                 else if (c >= 0)
591                                 {
592                                         ss << (char) c;
593                                 }
594                         }
595                         while (myConsoleStdout && !myConsoleStdout->Eof() && myConsoleStdout->CanRead())
596                         {
597                                 int c = myConsoleStdout->GetC();
598                                 if (c == '\n')
599                                 {
600                                         ss << "\r\n";
601                                 }
602                                 else if (c >= 0)
603                                 {
604                                         ss << (char) c;
605                                 }
606                         }
607                         myConsoleTerm->DisplayCharsUnsafe(ss.str());
608                 }
609         }
610
611         void doFinishDfu(wxProcessEvent& event)
612         {
613                 redirectDfuOutput();
614
615                 if (event.GetExitCode() == 0)
616                 {
617                         wxMessageBox(
618                                 "Update complete",
619                                 "Firmware update complete. Please reconnect USB cable.",
620                                 wxOK | wxICON_ERROR);
621                         mmLogStatus("Firmware update succeeded");
622                 } else {
623                         wxMessageBox(
624                                 "Update failed",
625                                 "Firmware update failed.",
626                                 wxOK | wxICON_ERROR);
627                         mmLogStatus("Firmware update failed");
628                 }
629
630                 myConsoleStdout = myConsoleStderr = NULL;
631                 myConsoleProcess.reset();
632                 myConsoleTerm->GetParent()->Close();
633                 myConsoleTerm->Close();
634                 myConsoleTerm.reset();
635
636         }
637
638         void dumpSCSICommand(std::vector<uint8_t> buf)
639         {
640                 std::stringstream msg;
641                 msg << std::hex;
642                 for (size_t i = 0; i < 32 && i < buf.size(); ++i)
643                 {
644                         msg << std::setfill('0') << std::setw(2) <<
645                         static_cast<int>(buf[i]) << ' ';
646                 }
647                 wxLogMessage(this, msg.str().c_str());
648         }
649
650         void logSCSI()
651         {
652                 if (!mySCSILogChk->IsChecked() ||
653                         !myHID)
654                 {
655                         return;
656                 }
657                 try
658                 {
659                         std::vector<uint8_t> info;
660                         if (myHID->readSCSIDebugInfo(info))
661                         {
662                                 dumpSCSICommand(info);
663                         }
664                 }
665                 catch (std::exception& e)
666                 {
667                         wxLogWarning(this, e.what());
668                         myHID.reset();
669                 }
670         }
671
672         void OnID_Timer(wxTimerEvent& event)
673         {
674                 redirectDfuOutput();
675
676                 logSCSI();
677                 time_t now = time(NULL);
678                 if (now == myLastPollTime) return;
679                 myLastPollTime = now;
680
681                 // Check if we are connected to the HID device.
682                 try
683                 {
684                         if (myHID && !myHID->ping())
685                         {
686                                 // Verify the USB HID connection is valid
687                                 myHID.reset();
688                         }
689
690                         if (!myHID)
691                         {
692                                 myHID.reset(HID::Open());
693                                 if (myHID)
694                                 {
695                                         std::stringstream msg;
696                                         msg << "SCSI2SD Ready, firmware version " <<
697                                                 myHID->getFirmwareVersionStr();
698                                         mmLogStatus(msg.str());
699
700                                         std::vector<uint8_t> csd(myHID->getSD_CSD());
701                                         std::vector<uint8_t> cid(myHID->getSD_CID());
702                                         std::stringstream sdinfo;
703                                         sdinfo << "SD Capacity (512-byte sectors): " <<
704                                                 myHID->getSDCapacity() << std::endl;
705
706                                         sdinfo << "SD CSD Register: ";
707                                         for (size_t i = 0; i < csd.size(); ++i)
708                                         {
709                                                 sdinfo <<
710                                                         std::hex << std::setfill('0') << std::setw(2) <<
711                                                         static_cast<int>(csd[i]);
712                                         }
713                                         sdinfo << std::endl;
714                                         sdinfo << "SD CID Register: ";
715                                         for (size_t i = 0; i < cid.size(); ++i)
716                                         {
717                                                 sdinfo <<
718                                                         std::hex << std::setfill('0') << std::setw(2) <<
719                                                         static_cast<int>(cid[i]);
720                                         }
721
722                                         wxLogMessage(this, "%s", sdinfo.str());
723
724                                         if (mySelfTestChk->IsChecked())
725                                         {
726                                                 std::stringstream scsiInfo;
727                                                 int errcode;
728                                                 scsiInfo << "SCSI Self-Test: ";
729                                                 if (myHID->scsiSelfTest(errcode))
730                                                 {
731                                                         scsiInfo << "Passed";
732                                                 }
733                                                 else
734                                                 {
735                                                         scsiInfo << "FAIL (" << errcode << ")";
736                                                 }
737                                                 wxLogMessage(this, "%s", scsiInfo.str());
738                                         }
739
740                                         if (!myInitialConfig)
741                                         {
742 /* This doesn't work properly, and causes crashes.
743                                                 wxCommandEvent loadEvent(wxEVT_NULL, ID_BtnLoad);
744                                                 GetEventHandler()->AddPendingEvent(loadEvent);
745 */
746                                         }
747
748                                 }
749                                 else
750                                 {
751                                         char ticks[] = {'/', '-', '\\', '|'};
752                                         std::stringstream ss;
753                                         ss << "Searching for SCSI2SD device " << ticks[myTickCounter % sizeof(ticks)];
754                                         myTickCounter++;
755                                         SetStatusText(ss.str());
756                                 }
757                         }
758                 }
759                 catch (std::runtime_error& e)
760                 {
761                         std::cerr << e.what() << std::endl;
762                         mmLogStatus(e.what());
763                 }
764
765                 evaluate();
766         }
767
768         void doLoad(wxCommandEvent& event)
769         {
770                 TimerLock lock(myTimer);
771                 if (!myHID) return;
772
773                 mmLogStatus("Loading configuration");
774
775                 wxWindowPtr<wxGenericProgressDialog> progress(
776                         new wxGenericProgressDialog(
777                                 "Load config settings",
778                                 "Loading config settings",
779                                 100,
780                                 this,
781                                 wxPD_CAN_ABORT | wxPD_REMAINING_TIME)
782                                 );
783
784                 int currentProgress = 0;
785                 int totalProgress = 2;
786
787                 std::vector<uint8_t> cfgData(S2S_CFG_SIZE);
788                 uint32_t sector = myHID->getSDCapacity() - 2;
789                 for (size_t i = 0; i < 2; ++i)
790                 {
791                         std::stringstream ss;
792                         ss << "Reading sector " << sector;
793                         mmLogStatus(ss.str());
794                         currentProgress += 1;
795                         if (currentProgress == totalProgress)
796                         {
797                                 ss.str("Load Complete.");
798                                 mmLogStatus("Load Complete.");
799                         }
800
801                         if (!progress->Update(
802                                         (100 * currentProgress) / totalProgress,
803                                         ss.str()
804                                         )
805                                 )
806                         {
807                                 goto abort;
808                         }
809
810                         std::vector<uint8_t> sdData;
811
812                         try
813                         {
814                                 myHID->readSector(sector++, sdData);
815                         }
816                         catch (std::runtime_error& e)
817                         {
818                                 mmLogStatus(e.what());
819                                 goto err;
820                         }
821
822                         std::copy(
823                                 sdData.begin(),
824                                 sdData.end(),
825                                 &cfgData[i * 512]);
826                 }
827
828                 myBoardPanel->setConfig(ConfigUtil::boardConfigFromBytes(&cfgData[0]));
829                 for (int i = 0; i < S2S_MAX_TARGETS; ++i)
830                 {
831                         myTargets[i]->setConfig(
832                                 ConfigUtil::fromBytes(
833                                         &cfgData[sizeof(S2S_BoardCfg) + i * sizeof(S2S_TargetCfg)]
834                                         )
835                                 );
836                 }
837
838                 myInitialConfig = true;
839                 goto out;
840
841         err:
842                 mmLogStatus("Load failed");
843                 progress->Update(100, "Load failed");
844                 goto out;
845
846         abort:
847                 mmLogStatus("Load Aborted");
848
849         out:
850                 return;
851
852         }
853
854         void doSave(wxCommandEvent& event)
855         {
856                 TimerLock lock(myTimer);
857                 if (!myHID) return;
858
859                 mmLogStatus("Saving configuration");
860
861                 wxWindowPtr<wxGenericProgressDialog> progress(
862                         new wxGenericProgressDialog(
863                                 "Save config settings",
864                                 "Saving config settings",
865                                 100,
866                                 this,
867                                 wxPD_CAN_ABORT | wxPD_REMAINING_TIME)
868                                 );
869
870
871                 int currentProgress = 0;
872                 int totalProgress = 2;
873
874                 std::vector<uint8_t> cfgData(
875                         ConfigUtil::boardConfigToBytes(myBoardPanel->getConfig())
876                         );
877                 for (int i = 0; i < S2S_MAX_TARGETS; ++i)
878                 {
879                         std::vector<uint8_t> raw(
880                                 ConfigUtil::toBytes(myTargets[i]->getConfig())
881                                 );
882                         cfgData.insert(cfgData.end(), raw.begin(), raw.end());
883                 }
884
885                 uint32_t sector = myHID->getSDCapacity() - 2;
886
887                 for (size_t i = 0; i < 2; ++i)
888                 {
889                         std::stringstream ss;
890                         ss << "Writing SD sector " << sector;
891                         mmLogStatus(ss.str());
892                         currentProgress += 1;
893
894                         if (currentProgress == totalProgress)
895                         {
896                                 ss.str("Save Complete.");
897                                 mmLogStatus("Save Complete.");
898                         }
899                         if (!progress->Update(
900                                         (100 * currentProgress) / totalProgress,
901                                         ss.str()
902                                         )
903                                 )
904                         {
905                                 goto abort;
906                         }
907
908                         try
909                         {
910                                 std::vector<uint8_t> buf;
911                                 buf.insert(buf.end(), &cfgData[i * 512], &cfgData[(i+1) * 512]);
912                                 myHID->writeSector(sector++, buf);
913                         }
914                         catch (std::runtime_error& e)
915                         {
916                                 mmLogStatus(e.what());
917                                 goto err;
918                         }
919                 }
920
921                 myHID.reset();
922
923                 goto out;
924
925         err:
926                 mmLogStatus("Save failed");
927                 progress->Update(100, "Save failed");
928                 goto out;
929
930         abort:
931                 mmLogStatus("Save Aborted");
932
933         out:
934                 return;
935
936         }
937
938         // Note: Don't confuse this with the wxApp::OnExit virtual method
939         void OnExitEvt(wxCommandEvent& event);
940
941         void OnCloseEvt(wxCloseEvent& event);
942
943         void OnAbout(wxCommandEvent& event)
944         {
945                 wxMessageBox(
946                         "SCSI2SD (scsi2sd-util6)\n"
947                         "Copyright (C) 2014-2017 Michael McMaster <michael@codesrc.com>\n"
948                         "\n"
949 "This program is free software: you can redistribute it and/or modify\n"
950 "it under the terms of the GNU General Public License as published by\n"
951 "the Free Software Foundation, either version 3 of the License, or\n"
952 "(at your option) any later version.\n"
953 "\n"
954 "This program is distributed in the hope that it will be useful,\n"
955 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
956 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
957 "GNU General Public License for more details.\n"
958 "\n"
959 "You should have received a copy of the GNU General Public License\n"
960 "along with this program.  If not, see <http://www.gnu.org/licenses/>.\n",
961
962                         "About scsi2sd-util6", wxOK | wxICON_INFORMATION );
963         }
964
965         wxDECLARE_EVENT_TABLE();
966 };
967
968 wxBEGIN_EVENT_TABLE(AppFrame, wxFrame)
969         EVT_MENU(AppFrame::ID_ConfigDefaults, AppFrame::OnID_ConfigDefaults)
970         EVT_MENU(AppFrame::ID_Firmware, AppFrame::OnID_Firmware)
971         EVT_MENU(AppFrame::ID_LogWindow, AppFrame::OnID_LogWindow)
972         EVT_MENU(AppFrame::ID_SaveFile, AppFrame::OnID_SaveFile)
973         EVT_MENU(AppFrame::ID_OpenFile, AppFrame::OnID_OpenFile)
974         EVT_MENU(wxID_EXIT, AppFrame::OnExitEvt)
975         EVT_MENU(wxID_ABOUT, AppFrame::OnAbout)
976
977         EVT_TIMER(AppFrame::ID_Timer, AppFrame::OnID_Timer)
978
979         EVT_COMMAND(wxID_ANY, ConfigChangedEvent, AppFrame::onConfigChanged)
980
981         EVT_MENU(ID_BtnSave, AppFrame::doSave)
982         EVT_MENU(ID_BtnLoad, AppFrame::doLoad)
983
984         EVT_CLOSE(AppFrame::OnCloseEvt)
985
986         EVT_END_PROCESS(wxID_ANY, AppFrame::doFinishDfu)
987
988 wxEND_EVENT_TABLE()
989
990
991
992 class App : public wxApp
993 {
994 public:
995         virtual bool OnInit()
996         {
997                 AppFrame* frame = new AppFrame();
998                 frame->Show(true);
999                 SetTopWindow(frame);
1000                 return true;
1001         }
1002 };
1003 } // namespace
1004
1005 // Main Method
1006 wxIMPLEMENT_APP(App);
1007
1008 void
1009 AppFrame::OnExitEvt(wxCommandEvent& event)
1010 {
1011         wxGetApp().ExitMainLoop();
1012 }
1013
1014 void
1015 AppFrame::OnCloseEvt(wxCloseEvent& event)
1016 {
1017         wxGetApp().ExitMainLoop();
1018 }
1019