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