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