Fix input of size fieds and CDROM sector length in scsi2sd-util
[SCSI2SD.git] / software / scsi2sd-util / TargetPanel.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 // For compilers that support precompilation, includes "wx/wx.h".
19 #include <wx/wxprec.h>
20 #ifndef WX_PRECOMP
21 #include <wx/wx.h>
22 #endif
23
24 #include <wx/wrapsizer.h>
25
26 #include "ConfigUtil.hh"
27 #include "TargetPanel.hh"
28
29 #include <limits>
30 #include <sstream>
31
32 #include <math.h>
33 #include <string.h>
34
35 using namespace SCSI2SD;
36
37 wxDEFINE_EVENT(SCSI2SD::ConfigChangedEvent, wxCommandEvent);
38
39 namespace
40 {
41         template<typename IntType, class WXCTRL> std::pair<IntType, bool>
42         CtrlGetValue(WXCTRL* ctrl)
43         {
44                 IntType value;
45                 std::stringstream conv;
46                 conv << ctrl->GetValue();
47                 conv >> value;
48                 return std::make_pair(value, static_cast<bool>(conv));
49         }
50
51         void CtrlGetFixedString(wxTextEntry* ctrl, char* dest, size_t len)
52         {
53                 memset(dest, ' ', len);
54                 std::string str(ctrl->GetValue().ToAscii());
55                 // Don't use strncpy - we need to avoid NULL's
56                 memcpy(dest, str.c_str(), std::min(len, str.size()));
57         }
58
59         bool CtrlIsAscii(wxTextEntry* ctrl)
60         {
61                 return ctrl->GetValue().IsAscii();
62         }
63
64 }
65
66 TargetPanel::TargetPanel(wxWindow* parent, const TargetConfig& initialConfig) :
67         wxPanel(parent),
68         myParent(parent),
69         myAutoStartSector(0),
70         myStartSDSectorValidator(new wxIntegerValidator<uint32_t>),
71         mySectorSizeValidator(new wxIntegerValidator<uint16_t>),
72         myNumSectorValidator(new wxIntegerValidator<uint32_t>),
73         mySizeValidator(new wxFloatingPointValidator<float>(2))
74 {
75         wxFlexGridSizer *fgs = new wxFlexGridSizer(11, 3, 9, 25);
76
77         fgs->Add(new wxStaticText(this, wxID_ANY, wxT("")));
78         myEnableCtrl =
79                 new wxCheckBox(
80                         this,
81                         ID_enableCtrl,
82                         _("Enable SCSI Target"));
83         fgs->Add(myEnableCtrl);
84         // Set a non-visible string to leave room in the column for future messages
85         fgs->Add(new wxStaticText(this, wxID_ANY, wxT("                                        ")));
86         Bind(wxEVT_CHECKBOX, &TargetPanel::onInput<wxCommandEvent>, this, ID_enableCtrl);
87
88
89         fgs->Add(new wxStaticText(this, wxID_ANY, _("SCSI ID")));
90         myScsiIdCtrl =
91                 new wxSpinCtrl
92                         (this,
93                         ID_scsiIdCtrl,
94                         wxEmptyString,
95                         wxDefaultPosition,
96                         wxDefaultSize,
97                         wxSP_WRAP | wxSP_ARROW_KEYS,
98                         0,
99                         7,
100                         0);
101         fgs->Add(myScsiIdCtrl);
102         myScsiIdMsg = new wxStaticText(this, wxID_ANY, wxT(""));
103         fgs->Add(myScsiIdMsg);
104         Bind(wxEVT_SPINCTRL, &TargetPanel::onInput<wxSpinEvent>, this, ID_scsiIdCtrl);
105
106         fgs->Add(new wxStaticText(this, wxID_ANY, _("Device Type")));
107         wxString deviceTypes[] =
108         {
109                 _("Hard Drive"),
110                 _("Removable"),
111                 _("CDROM"),
112                 _("3.5\" Floppy"),
113                 _("Magneto optical")
114         };
115         myDeviceTypeCtrl =
116                 new wxChoice(
117                         this,
118                         ID_deviceTypeCtrl,
119                         wxDefaultPosition,
120                         wxDefaultSize,
121                         sizeof(deviceTypes) / sizeof(wxString),
122                         deviceTypes
123                         );
124         myDeviceTypeCtrl->SetSelection(0);
125         fgs->Add(myDeviceTypeCtrl);
126         fgs->Add(new wxStaticText(this, wxID_ANY, wxT("")));
127         Bind(wxEVT_CHOICE, &TargetPanel::onInput<wxCommandEvent>, this, ID_deviceTypeCtrl);
128
129         fgs->Add(new wxStaticText(this, wxID_ANY, wxT("SD card start sector")));
130         wxWrapSizer* startContainer = new wxWrapSizer();
131         myStartSDSectorCtrl =
132                 new wxTextCtrl(
133                         this,
134                         ID_startSDSectorCtrl,
135                         "0",
136                         wxDefaultPosition,
137                         wxDefaultSize,
138                         0,
139                         *myStartSDSectorValidator);
140         myStartSDSectorCtrl->SetToolTip(wxT("Supports multiple SCSI targets "
141                 "on a single memory card. In units of 512-byte sectors."));
142         startContainer->Add(myStartSDSectorCtrl);
143         myAutoStartSectorCtrl =
144                 new wxCheckBox(
145                         this,
146                         ID_autoStartSectorCtrl,
147                         wxT("Auto"));
148         startContainer->Add(myAutoStartSectorCtrl);
149         Bind(wxEVT_CHECKBOX, &TargetPanel::onInput<wxCommandEvent>, this, ID_autoStartSectorCtrl);
150         fgs->Add(startContainer);
151         myStartSDSectorMsg = new wxStaticText(this, wxID_ANY, wxT(""));
152         fgs->Add(myStartSDSectorMsg);
153         Bind(wxEVT_TEXT, &TargetPanel::onInput<wxCommandEvent>, this, ID_startSDSectorCtrl);
154
155
156         fgs->Add(new wxStaticText(this, wxID_ANY, wxT("Sector size (bytes)")));
157         mySectorSizeCtrl =
158                 new wxTextCtrl(
159                         this,
160                         ID_sectorSizeCtrl,
161                         "512",
162                         wxDefaultPosition,
163                         wxDefaultSize,
164                         0,
165                         *mySectorSizeValidator);
166         mySectorSizeCtrl->SetToolTip(wxT("Between 64 and 8192. Default of 512 is suitable in most cases."));
167         fgs->Add(mySectorSizeCtrl);
168         mySectorSizeMsg = new wxStaticText(this, wxID_ANY, wxT(""));
169         fgs->Add(mySectorSizeMsg);
170         Bind(wxEVT_TEXT, &TargetPanel::onSizeInput, this, ID_sectorSizeCtrl);
171
172
173         fgs->Add(new wxStaticText(this, wxID_ANY, wxT("Sector count")));
174         myNumSectorCtrl =
175                 new wxTextCtrl(
176                         this,
177                         ID_numSectorCtrl,
178                         "",
179                         wxDefaultPosition,
180                         wxDefaultSize,
181                         0,
182                         *myNumSectorValidator);
183         myNumSectorCtrl->SetToolTip(wxT("Number of sectors (device size)"));
184         fgs->Add(myNumSectorCtrl);
185         myNumSectorMsg = new wxStaticText(this, wxID_ANY, wxT(""));
186         fgs->Add(myNumSectorMsg);
187         Bind(wxEVT_TEXT, &TargetPanel::onSizeInput, this, ID_numSectorCtrl);
188
189
190         fgs->Add(new wxStaticText(this, wxID_ANY, wxT("Device size")));
191         wxWrapSizer* sizeContainer = new wxWrapSizer();
192         mySizeCtrl =
193                 new wxTextCtrl(
194                         this,
195                         ID_sizeCtrl,
196                         "",
197                         wxDefaultPosition,
198                         wxDefaultSize,
199                         0,
200                         *mySizeValidator);
201         mySizeCtrl->SetToolTip(wxT("Device size"));
202         sizeContainer->Add(mySizeCtrl);
203         wxString units[] = {wxT("KB"), wxT("MB"), wxT("GB")};
204         mySizeUnitCtrl =
205                 new wxChoice(
206                         this,
207                         ID_sizeUnitCtrl,
208                         wxDefaultPosition,
209                         wxDefaultSize,
210                         sizeof(units) / sizeof(wxString),
211                         units
212                         );
213         sizeContainer->Add(mySizeUnitCtrl);
214         fgs->Add(sizeContainer);
215         fgs->Add(new wxStaticText(this, wxID_ANY, wxT("")));
216         Bind(wxEVT_TEXT, &TargetPanel::onSizeInput, this, ID_sizeCtrl);
217         Bind(wxEVT_CHOICE, &TargetPanel::onSizeInput, this, ID_sizeUnitCtrl);
218
219         fgs->Add(new wxStaticText(this, wxID_ANY, wxT("Vendor")));
220         myVendorCtrl =
221                 new wxTextCtrl(
222                         this,
223                         ID_vendorCtrl,
224                         wxEmptyString,
225                         wxDefaultPosition,
226                         wxSize(GetCharWidth() * 10, -1));
227         myVendorCtrl->SetMaxLength(8);
228         myVendorCtrl->SetToolTip(wxT("SCSI Vendor string. eg. ' codesrc'"));
229         fgs->Add(myVendorCtrl);
230         myVendorMsg = new wxStaticText(this, wxID_ANY, wxT(""));
231         fgs->Add(myVendorMsg);
232         Bind(wxEVT_TEXT, &TargetPanel::onInput<wxCommandEvent>, this, ID_vendorCtrl);
233
234         fgs->Add(new wxStaticText(this, wxID_ANY, wxT("Product ID")));
235         myProductCtrl =
236                 new wxTextCtrl(
237                         this,
238                         ID_productCtrl,
239                         wxEmptyString,
240                         wxDefaultPosition,
241                         wxSize(GetCharWidth() * 17, -1));
242         myProductCtrl->SetMaxLength(18);
243         myProductCtrl->SetToolTip(wxT("SCSI Product ID string. eg. 'SCSI2SD'"));
244         fgs->Add(myProductCtrl);
245         myProductMsg = new wxStaticText(this, wxID_ANY, wxT(""));
246         fgs->Add(myProductMsg);
247         Bind(wxEVT_TEXT, &TargetPanel::onInput<wxCommandEvent>, this, ID_productCtrl);
248
249         fgs->Add(new wxStaticText(this, wxID_ANY, wxT("Revision")));
250         myRevisionCtrl =
251                 new wxTextCtrl(
252                         this,
253                         ID_revisionCtrl,
254                         wxEmptyString,
255                         wxDefaultPosition,
256                         wxSize(GetCharWidth() * 6, -1));
257         myRevisionCtrl->SetMaxLength(4);
258         myRevisionCtrl->SetToolTip(wxT("SCSI device revision string. eg. '3.5a'"));
259         fgs->Add(myRevisionCtrl);
260         myRevisionMsg = new wxStaticText(this, wxID_ANY, wxT(""));
261         fgs->Add(myRevisionMsg);
262         Bind(wxEVT_TEXT, &TargetPanel::onInput<wxCommandEvent>, this, ID_revisionCtrl);
263
264         fgs->Add(new wxStaticText(this, wxID_ANY, wxT("Serial number")));
265         mySerialCtrl =
266                 new wxTextCtrl(
267                         this,
268                         ID_serialCtrl,
269                         wxEmptyString,
270                         wxDefaultPosition,
271                         wxSize(GetCharWidth() * 18, -1));
272         mySerialCtrl->SetMaxLength(16);
273         mySerialCtrl->SetToolTip(wxT("SCSI serial number. eg. '13eab5632a'"));
274         fgs->Add(mySerialCtrl);
275         mySerialMsg = new wxStaticText(this, wxID_ANY, wxT(""));
276         fgs->Add(mySerialMsg);
277         Bind(wxEVT_TEXT, &TargetPanel::onInput<wxCommandEvent>, this, ID_serialCtrl);
278
279         wxBoxSizer* hbox = new wxBoxSizer(wxHORIZONTAL);
280         hbox->Add(fgs, 1, wxALL | wxEXPAND, 15);
281         this->SetSizer(hbox);
282         Centre();
283
284
285         setConfig(initialConfig);
286         evaluate();
287 }
288
289 bool
290 TargetPanel::evaluate()
291 {
292         bool valid = true;
293         std::stringstream conv;
294
295         bool enabled = myEnableCtrl->IsChecked();
296         {
297                 myScsiIdCtrl->Enable(enabled);
298                 myDeviceTypeCtrl->Enable(enabled);
299                 myStartSDSectorCtrl->Enable(enabled && !myAutoStartSectorCtrl->IsChecked());
300                 myAutoStartSectorCtrl->Enable(enabled);
301                 mySectorSizeCtrl->Enable(enabled);
302                 myNumSectorCtrl->Enable(enabled);
303                 mySizeCtrl->Enable(enabled);
304                 mySizeUnitCtrl->Enable(enabled);
305                 myVendorCtrl->Enable(enabled);
306                 myProductCtrl->Enable(enabled);
307                 myRevisionCtrl->Enable(enabled);
308                 mySerialCtrl->Enable(enabled);
309         }
310
311         switch (myDeviceTypeCtrl->GetSelection())
312         {
313         case CONFIG_FLOPPY_14MB:
314                 mySectorSizeCtrl->ChangeValue("512");
315                 mySectorSizeCtrl->Enable(false);
316                 myNumSectorCtrl->ChangeValue("2880");
317                 myNumSectorCtrl->Enable(false);
318                 mySizeUnitCtrl->Enable(false);
319                 mySizeCtrl->Enable(false);
320                 evaluateSize();
321                 break;
322         };
323
324         if (myAutoStartSectorCtrl->IsChecked())
325         {
326                 std::stringstream ss; ss << myAutoStartSector;
327                 myStartSDSectorCtrl->ChangeValue(ss.str());
328         }
329
330         uint32_t startSDsector;
331         {
332                 conv << myStartSDSectorCtrl->GetValue();
333                 conv >> startSDsector;
334         }
335
336         if (!conv)
337                 // TODO check if it is beyond the current SD card.
338         {
339                 myStartSDSectorMsg->SetLabelMarkup(wxT("<span foreground='red' weight='bold'>Number &gt;= 0</span>"));
340                 valid = false;
341         }
342         else
343         {
344                 myStartSDSectorMsg->SetLabelMarkup("");
345         }
346         conv.str(std::string()); conv.clear();
347
348         uint16_t sectorSize(CtrlGetValue<uint16_t>(mySectorSizeCtrl).first);
349         if (sectorSize < 64 || sectorSize > 8192)
350         {
351                 mySectorSizeMsg->SetLabelMarkup(wxT("<span foreground='red' weight='bold'>Must be between 64 and 8192</span>"));
352                 valid = false;
353         }
354         else
355         {
356                 mySectorSizeMsg->SetLabelMarkup("");
357         }
358         conv.str(std::string()); conv.clear();
359
360         std::pair<uint32_t, bool> numSectors(CtrlGetValue<uint32_t>(myNumSectorCtrl));
361         if (!numSectors.second ||
362                 numSectors.first == 0 ||
363                 !convertUnitsToSectors().second)
364         {
365                 myNumSectorMsg->SetLabelMarkup(wxT("<span foreground='red' weight='bold'>Invalid size</span>"));
366                 valid = false;
367         }
368         else
369         {
370                 myNumSectorMsg->SetLabelMarkup("");
371         }
372
373         if (!CtrlIsAscii(myVendorCtrl))
374         {
375                 myVendorMsg->SetLabelMarkup(wxT("<span foreground='red' weight='bold'>Invalid characters</span>"));
376                 valid = false;
377         }
378         else
379         {
380                 myVendorMsg->SetLabelMarkup("");
381         }
382
383         if (!CtrlIsAscii(myProductCtrl))
384         {
385                 myProductMsg->SetLabelMarkup(wxT("<span foreground='red' weight='bold'>Invalid characters</span>"));
386                 valid = false;
387         }
388         else
389         {
390                 myProductMsg->SetLabelMarkup("");
391         }
392
393         if (!CtrlIsAscii(myRevisionCtrl))
394         {
395                 myRevisionMsg->SetLabelMarkup(wxT("<span foreground='red' weight='bold'>Invalid characters</span>"));
396                 valid = false;
397         }
398         else
399         {
400                 myRevisionMsg->SetLabelMarkup("");
401         }
402
403         if (!CtrlIsAscii(mySerialCtrl))
404         {
405                 mySerialMsg->SetLabelMarkup(wxT("<span foreground='red' weight='bold'>Invalid characters</span>"));
406                 valid = false;
407         }
408         else
409         {
410                 mySerialMsg->SetLabelMarkup("");
411         }
412
413         return valid || !enabled;
414 }
415
416 template<typename EvtType> void
417 TargetPanel::onInput(EvtType& event)
418 {
419         if (event.GetId() == ID_deviceTypeCtrl)
420         {
421                 switch (myDeviceTypeCtrl->GetSelection())
422                 {
423                 case CONFIG_OPTICAL:
424                         mySectorSizeCtrl->ChangeValue("2048");
425                         evaluateSize();
426                         break;
427                 }
428         }
429         wxCommandEvent changeEvent(ConfigChangedEvent);
430         wxPostEvent(myParent, changeEvent);
431 }
432
433 void
434 TargetPanel::onSizeInput(wxCommandEvent& event)
435 {
436         if (event.GetId() != ID_numSectorCtrl)
437         {
438                 std::pair<uint32_t, bool> sec = convertUnitsToSectors();
439                 if (sec.second)
440                 {
441                         std::stringstream ss;
442                         ss << sec.first;
443                         myNumSectorCtrl->ChangeValue(ss.str());
444                 }
445         }
446         if (event.GetId() != ID_sizeCtrl)
447         {
448                 evaluateSize();
449         }
450         onInput(event); // propagate
451 }
452
453 void
454 TargetPanel::evaluateSize()
455 {
456         uint32_t numSectors;
457         std::stringstream conv;
458         conv << myNumSectorCtrl->GetValue();
459         conv >> numSectors;
460
461         conv.str(""); conv.clear();
462
463         if (conv)
464         {
465                 uint64_t bytes =
466                         uint64_t(numSectors) *
467                                 CtrlGetValue<uint16_t>(mySectorSizeCtrl).first;
468
469                 if (bytes >= 1024 * 1024 * 1024)
470                 {
471                         conv << (bytes / (1024.0 * 1024 * 1024));
472                         mySizeUnitCtrl->SetSelection(UNIT_GB);
473                 }
474                 else if (bytes >= 1024 * 1024)
475                 {
476                         conv << (bytes / (1024.0 * 1024));
477                         mySizeUnitCtrl->SetSelection(UNIT_MB);
478                 }
479                 else
480                 {
481                         conv << (bytes / 1024.0);
482                         mySizeUnitCtrl->SetSelection(UNIT_KB);
483                 }
484                 mySizeCtrl->ChangeValue(conv.str());
485         }
486 }
487
488 std::pair<uint32_t, bool>
489 TargetPanel::convertUnitsToSectors() const
490 {
491         bool valid = true;
492
493         uint64_t multiplier(0);
494         switch (mySizeUnitCtrl->GetSelection())
495         {
496                 case UNIT_KB: multiplier = 1024; break;
497                 case UNIT_MB: multiplier = 1024 * 1024; break;
498                 case UNIT_GB: multiplier = 1024 * 1024 * 1024; break;
499                 default: valid = false;
500         }
501
502         double size;
503         std::stringstream conv;
504         conv << mySizeCtrl->GetValue();
505         conv >> size;
506         valid = valid && conv;
507
508         uint16_t sectorSize = CtrlGetValue<uint16_t>(mySectorSizeCtrl).first;
509         uint64_t sectors = ceil(multiplier * size / sectorSize);
510
511         if (sectors > std::numeric_limits<uint32_t>::max())
512         {
513                 sectors = std::numeric_limits<uint32_t>::max();
514                 valid = false;
515         }
516
517         return std::make_pair(static_cast<uint32_t>(sectors), valid);
518 }
519
520
521 TargetConfig
522 TargetPanel::getConfig() const
523 {
524         TargetConfig config;
525
526         // Try and keep unknown/unused fields as-is to support newer firmware
527         // versions.
528         memcpy(&config, &myConfig, sizeof(config));
529
530         bool valid = true;
531
532         auto scsiId = CtrlGetValue<uint8_t>(myScsiIdCtrl);
533         config.scsiId = scsiId.first & CONFIG_TARGET_ID_BITS;
534         valid = valid && scsiId.second;
535         if (myEnableCtrl->IsChecked())
536         {
537                 config.scsiId = config.scsiId | CONFIG_TARGET_ENABLED;
538         }
539
540         config.deviceType = myDeviceTypeCtrl->GetSelection();
541
542         auto startSDSector = CtrlGetValue<uint32_t>(myStartSDSectorCtrl);
543         config.sdSectorStart = startSDSector.first;
544         valid = valid && startSDSector.second;
545
546         auto numSectors = CtrlGetValue<uint32_t>(myNumSectorCtrl);
547         config.scsiSectors = numSectors.first;
548         valid = valid && numSectors.second;
549
550         auto sectorSize = CtrlGetValue<uint16_t>(mySectorSizeCtrl);
551         config.bytesPerSector = sectorSize.first;
552         valid = valid && sectorSize.second;
553
554         CtrlGetFixedString(myVendorCtrl, config.vendor, sizeof(config.vendor));
555         CtrlGetFixedString(myProductCtrl, config.prodId, sizeof(config.prodId));
556         CtrlGetFixedString(myRevisionCtrl, config.revision, sizeof(config.revision));
557         CtrlGetFixedString(mySerialCtrl, config.serial, sizeof(config.serial));
558
559         return config;
560 }
561
562 void
563 TargetPanel::setConfig(const TargetConfig& config)
564 {
565         memcpy(&myConfig, &config, sizeof(config));
566
567         myScsiIdCtrl->SetValue(config.scsiId & CONFIG_TARGET_ID_BITS);
568         myEnableCtrl->SetValue(config.scsiId & CONFIG_TARGET_ENABLED);
569
570         myDeviceTypeCtrl->SetSelection(config.deviceType);
571
572         {
573                 std::stringstream ss; ss << config.sdSectorStart;
574                 myStartSDSectorCtrl->ChangeValue(ss.str());
575                 myAutoStartSectorCtrl->SetValue(0);
576         }
577
578         {
579                 std::stringstream ss; ss << config.scsiSectors;
580                 myNumSectorCtrl->ChangeValue(ss.str());
581         }
582
583         {
584                 std::stringstream ss; ss << config.bytesPerSector;
585                 mySectorSizeCtrl->ChangeValue(ss.str());
586         }
587
588         myVendorCtrl->ChangeValue(std::string(config.vendor, sizeof(config.vendor)));
589         myProductCtrl->ChangeValue(std::string(config.prodId, sizeof(config.prodId)));
590         myRevisionCtrl->ChangeValue(std::string(config.revision, sizeof(config.revision)));
591         mySerialCtrl->ChangeValue(std::string(config.serial, sizeof(config.serial)));
592
593         // Set the size fields based on sector size, and evaluate inputs.
594         wxCommandEvent fakeEvent(wxEVT_NULL, ID_numSectorCtrl);
595         onSizeInput(fakeEvent);
596 }
597
598 bool
599 TargetPanel::isEnabled() const
600 {
601         return myEnableCtrl->IsChecked();
602 }
603
604 uint8_t
605 TargetPanel::getSCSIId() const
606 {
607         return CtrlGetValue<uint8_t>(myScsiIdCtrl).first & CONFIG_TARGET_ID_BITS;
608 }
609
610 std::pair<uint32_t, uint64_t>
611 TargetPanel::getSDSectorRange() const
612 {
613         std::pair<uint32_t, uint64_t> result;
614         result.first = CtrlGetValue<uint32_t>(myStartSDSectorCtrl).first;
615
616         uint32_t numSCSISectors = CtrlGetValue<uint32_t>(myNumSectorCtrl).first;
617         uint16_t scsiSectorSize = CtrlGetValue<uint16_t>(mySectorSizeCtrl).first;
618
619         const int sdSector = 512; // Always 512 for SDHC/SDXC
620         result.second = result.first +
621                 (
622                         ((uint64_t(numSCSISectors) * scsiSectorSize) + (sdSector - 1))
623                                 / sdSector
624                 );
625         return result;
626 }
627
628 void
629 TargetPanel::setDuplicateID(bool duplicate)
630 {
631         if (duplicate)
632         {
633                 myScsiIdMsg->SetLabelMarkup(wxT("<span foreground='red' weight='bold'>Duplicate ID</span>"));
634         }
635         else
636         {
637                 myScsiIdMsg->SetLabelMarkup("");
638         }
639 }
640
641 void
642 TargetPanel::setSDSectorOverlap(bool overlap)
643 {
644         if (overlap)
645         {
646                 myStartSDSectorMsg->SetLabelMarkup(wxT("<span foreground='red' weight='bold'>Overlapping data</span>"));
647         }
648         else
649         {
650                 myStartSDSectorMsg->SetLabelMarkup("");
651         }
652 }
653
654 void
655 TargetPanel::setAutoStartSector(uint32_t start)
656 {
657         myAutoStartSector = start;
658 }
659