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