V4.7 release fixes
[SCSI2SD.git] / software / scsi2sd-util / ConfigUtil.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 #include "ConfigUtil.hh"
19
20 #include <limits>
21 #include <sstream>
22 #include <stdexcept>
23
24 #include <string.h>
25
26 #include <wx/wxprec.h>
27 #ifndef WX_PRECOMP
28 #include <wx/wx.h>
29 #endif
30 #include <wx/base64.h>
31 #include <wx/buffer.h>
32 #include <wx/xml/xml.h>
33
34
35 using namespace SCSI2SD;
36
37 namespace
38 {
39         // Endian conversion routines.
40         // The Cortex-M3 inside the Cypress PSoC 5LP is a
41         // little-endian device.
42
43         bool isHostLE()
44         {
45                 union
46                 {
47                         int i;
48                         char c[sizeof(int)];
49                 } x;
50                 x.i = 1;
51                 return (x.c[0] == 1);
52         }
53
54         uint16_t toLE16(uint16_t in)
55         {
56                 if (isHostLE())
57                 {
58                         return in;
59                 }
60                 else
61                 {
62                         return (in >> 8) | (in << 8);
63                 }
64         }
65         uint16_t fromLE16(uint16_t in)
66         {
67                 return toLE16(in);
68         }
69
70         uint32_t toLE32(uint32_t in)
71         {
72                 if (isHostLE())
73                 {
74                         return in;
75                 }
76                 else
77                 {
78                         return (in >> 24) |
79                                 ((in >> 8) & 0xff00) |
80                                 ((in << 8) & 0xff0000) |
81                                 (in << 24);
82                 }
83         }
84         uint32_t fromLE32(uint32_t in)
85         {
86                 return toLE32(in);
87         }
88
89         std::vector<uint8_t> getModePages(const TargetConfig& cfg)
90         {
91                 std::vector<uint8_t> result;
92                 int i = 0;
93                 while (i < sizeof(cfg.modePages) - 2)
94                 {
95                         int pageLen = cfg.modePages[i+1];
96                         if (pageLen == 0) break;
97                         std::copy(
98                                 &cfg.modePages[i],
99                                 &cfg.modePages[i+pageLen+2],
100                                 std::back_inserter(result));
101                         i += pageLen + 2;
102                 }
103                 return result;
104         }
105
106         std::vector<uint8_t> getVPDPages(const TargetConfig& cfg)
107         {
108                 std::vector<uint8_t> result;
109                 int i = 0;
110                 while (i < sizeof(cfg.vpd) - 4)
111                 {
112                         int pageLen = cfg.vpd[i+3];
113                         if (pageLen == 0) break;
114                         std::copy(
115                                 &cfg.vpd[i],
116                                 &cfg.vpd[i+pageLen+4],
117                                 std::back_inserter(result));
118                         i += pageLen + 4;
119                 }
120                 return result;
121         }
122 }
123
124 BoardConfig
125 ConfigUtil::DefaultBoardConfig()
126 {
127         BoardConfig config;
128         memset(&config, 0, sizeof(config));
129
130         memcpy(config.magic, "BCFG", 4);
131
132
133         // Default to maximum fail-safe options.
134         config.flags = 0;
135         config.selectionDelay = 255; // auto
136
137         return config;
138 }
139
140 TargetConfig
141 ConfigUtil::Default(size_t targetIdx)
142 {
143         TargetConfig config;
144         memset(&config, 0, sizeof(config));
145
146         config.scsiId = targetIdx;
147         if (targetIdx == 0)
148         {
149                 config.scsiId = config.scsiId | CONFIG_TARGET_ENABLED;
150         }
151         config.deviceType = CONFIG_FIXED;
152
153         // Default to maximum fail-safe options.
154         config.flagsDEPRECATED = 0;
155         config.deviceTypeModifier = 0;
156         config.sdSectorStart = 0;
157
158         // Default to 2GB. Many systems have trouble with > 2GB disks, and
159         // a few start to complain at 1GB.
160         config.scsiSectors = 4194303; // 2GB - 1 sector
161         config.bytesPerSector = 512;
162         config.sectorsPerTrack = 63;
163         config.headsPerCylinder = 255;
164         memcpy(config.vendor, " codesrc", 8);
165         memcpy(config.prodId, "         SCSI2SD", 16);
166         memcpy(config.revision, " 4.2", 4);
167         memcpy(config.serial, "1234567812345678", 16);
168
169         // Reserved fields, already set to 0
170         // config.reserved
171
172         // not supported yet.
173         // config.vpd
174
175         return config;
176 }
177
178
179 TargetConfig
180 ConfigUtil::fromBytes(const uint8_t* data)
181 {
182         TargetConfig result;
183         memcpy(&result, data, sizeof(TargetConfig));
184         result.sdSectorStart = toLE32(result.sdSectorStart);
185         result.scsiSectors = toLE32(result.scsiSectors);
186         result.bytesPerSector = toLE16(result.bytesPerSector);
187         result.sectorsPerTrack = toLE16(result.sectorsPerTrack);
188         result.headsPerCylinder = toLE16(result.headsPerCylinder);
189         return result;
190 }
191
192
193 std::vector<uint8_t>
194 ConfigUtil::toBytes(const TargetConfig& _config)
195 {
196         TargetConfig config(_config);
197         config.sdSectorStart = fromLE32(config.sdSectorStart);
198         config.scsiSectors = fromLE32(config.scsiSectors);
199         config.bytesPerSector = fromLE16(config.bytesPerSector);
200         config.sectorsPerTrack = fromLE16(config.sectorsPerTrack);
201         config.headsPerCylinder = fromLE16(config.headsPerCylinder);
202
203         const uint8_t* begin = reinterpret_cast<const uint8_t*>(&config);
204         return std::vector<uint8_t>(begin, begin + sizeof(config));
205 }
206
207 BoardConfig
208 ConfigUtil::boardConfigFromBytes(const uint8_t* data)
209 {
210         BoardConfig result;
211         memcpy(&result, data, sizeof(BoardConfig));
212
213         if (memcmp("BCFG", result.magic, 4))
214         {
215                 return DefaultBoardConfig();
216         }
217
218         return result;
219 }
220
221
222 std::vector<uint8_t>
223 ConfigUtil::boardConfigToBytes(const BoardConfig& _config)
224 {
225         BoardConfig config(_config);
226
227         memcpy(config.magic, "BCFG", 4);
228         const uint8_t* begin = reinterpret_cast<const uint8_t*>(&config);
229         return std::vector<uint8_t>(begin, begin + sizeof(config));
230 }
231
232 std::string
233 ConfigUtil::toXML(const TargetConfig& config)
234 {
235         std::stringstream s;
236         std::vector<uint8_t> modePages(getModePages(config));
237         std::vector<uint8_t> vpd(getVPDPages(config));
238
239         s <<
240                 "<SCSITarget id=\"" <<
241                         static_cast<int>(config.scsiId & CONFIG_TARGET_ID_BITS) << "\">\n" <<
242
243                 "       <enabled>" <<
244                         (config.scsiId & CONFIG_TARGET_ENABLED ? "true" : "false") <<
245                         "</enabled>\n" <<
246
247                 "\n" <<
248                 "       <!-- ********************************************************\n" <<
249                 "       Space separated list. Available options:\n" <<
250                 "       apple\t\tReturns Apple-specific mode pages\n" <<
251                 "       omti\t\tOMTI host non-standard link control\n" <<
252                 "       ********************************************************* -->\n" <<
253                 "       <quirks>";
254         if (config.quirks == CONFIG_QUIRKS_APPLE)
255         {
256                 s << "apple";
257         }
258         else if (config.quirks == CONFIG_QUIRKS_OMTI)
259         {
260                 s << "omti";
261         }
262
263         s <<
264                         "</quirks>\n" <<
265
266                 "\n\n" <<
267                 "       <!-- ********************************************************\n" <<
268                 "       0x0    Fixed hard drive.\n" <<
269                 "       0x1    Removable drive.\n" <<
270                 "       0x2    Optical drive  (ie. CD drive).\n" <<
271                 "       0x3    1.44MB Floppy Drive.\n" <<
272                 "       ********************************************************* -->\n" <<
273                 "       <deviceType>0x" <<
274                                 std::hex << static_cast<int>(config.deviceType) <<
275                         "</deviceType>\n" <<
276
277                 "\n\n" <<
278                 "       <!-- ********************************************************\n" <<
279                 "       Device type modifier is usually 0x00. Only change this if your\n" <<
280                 "       OS requires some special value.\n" <<
281                 "\n" <<
282                 "       0x4C    Data General Micropolis disk\n" <<
283                 "       ********************************************************* -->\n" <<
284                 "       <deviceTypeModifier>0x" <<
285                                 std::hex << static_cast<int>(config.deviceTypeModifier) <<
286                         "</deviceTypeModifier>\n" <<
287
288                 "\n\n" <<
289                 "       <!-- ********************************************************\n" <<
290                 "       SD card offset, as a sector number (always 512 bytes).\n" <<
291                 "       ********************************************************* -->\n" <<
292                 "       <sdSectorStart>" << std::dec << config.sdSectorStart << "</sdSectorStart>\n" <<
293                 "\n\n" <<
294                 "       <!-- ********************************************************\n" <<
295                 "       Drive geometry settings.\n" <<
296                 "       ********************************************************* -->\n" <<
297                 "\n"
298                 "       <scsiSectors>" << std::dec << config.scsiSectors << "</scsiSectors>\n" <<
299                 "       <bytesPerSector>" << std::dec << config.bytesPerSector << "</bytesPerSector>\n" <<
300                 "       <sectorsPerTrack>" << std::dec << config.sectorsPerTrack<< "</sectorsPerTrack>\n" <<
301                 "       <headsPerCylinder>" << std::dec << config.headsPerCylinder << "</headsPerCylinder>\n" <<
302                 "\n\n" <<
303                 "       <!-- ********************************************************\n" <<
304                 "       Drive identification information. The SCSI2SD doesn't\n" <<
305                 "       care what these are set to. Use these strings to trick a OS\n" <<
306                 "       thinking a specific hard drive model is attached.\n" <<
307                 "       ********************************************************* -->\n" <<
308                 "\n"
309                 "       <!-- 8 character vendor string -->\n" <<
310                 "       <!-- For Apple HD SC Setup/Drive Setup, use ' SEAGATE' -->\n" <<
311                 "       <vendor>" << std::string(config.vendor, 8) << "</vendor>\n" <<
312                 "\n" <<
313                 "       <!-- 16 character produce identifier -->\n" <<
314                 "       <!-- For Apple HD SC Setup/Drive Setup, use '          ST225N' -->\n" <<
315                 "       <prodId>" << std::string(config.prodId, 16) << "</prodId>\n" <<
316                 "\n" <<
317                 "       <!-- 4 character product revision number -->\n" <<
318                 "       <!-- For Apple HD SC Setup/Drive Setup, use '1.0 ' -->\n" <<
319                 "       <revision>" << std::string(config.revision, 4) << "</revision>\n" <<
320                 "\n" <<
321                 "       <!-- 16 character serial number -->\n" <<
322                 "       <serial>" << std::string(config.serial, 16) << "</serial>\n" <<
323                 "\n" <<
324                 "       <!-- Custom mode pages, base64 encoded, up to 1024 bytes.-->\n" <<
325                 "       <modePages>\n" <<
326                         (modePages.size() == 0 ? "" :
327                                 wxBase64Encode(&modePages[0], modePages.size())) <<
328                                 "\n" <<
329                 "       </modePages>\n" <<
330                 "\n" <<
331                 "       <!-- Custom inquiry VPD pages, base64 encoded, up to 1024 bytes.-->\n" <<
332                 "       <vpd>\n" <<
333                         (vpd.size() == 0 ? "" :
334                                 wxBase64Encode(&vpd[0], vpd.size())) <<
335                                 "\n" <<
336                 "       </vpd>\n" <<
337                 "</SCSITarget>\n";
338
339         return s.str();
340 }
341
342 std::string
343 ConfigUtil::toXML(const BoardConfig& config)
344 {
345         std::stringstream s;
346
347         s << "<BoardConfig>\n" <<
348
349                 "       <unitAttention>" <<
350                         (config.flags & CONFIG_ENABLE_UNIT_ATTENTION ? "true" : "false") <<
351                         "</unitAttention>\n" <<
352
353                 "       <parity>" <<
354                         (config.flags & CONFIG_ENABLE_PARITY ? "true" : "false") <<
355                         "</parity>\n" <<
356
357                 "       <!-- ********************************************************\n" <<
358                 "       Only set to true when using with a fast SCSI2 host\n " <<
359                 "       controller. This can cause problems with older/slower\n" <<
360                 "       hardware.\n" <<
361                 "       ********************************************************* -->\n" <<
362                 "       <enableScsi2>" <<
363                         (config.flags & CONFIG_ENABLE_SCSI2 ? "true" : "false") <<
364                         "</enableScsi2>\n" <<
365
366                 "       <!-- ********************************************************\n" <<
367                 "       Setting to 'true' will result in increased performance at the\n" <<
368                 "       cost of lower noise immunity.\n" <<
369                 "       Only set to true when using short cables with only 1 or two\n" <<
370                 "       devices. This should remain off when using external SCSI1 DB25\n" <<
371                 "       cables.\n" <<
372                 "       ********************************************************* -->\n" <<
373                 "       <disableGlitchFilter>" <<
374                         (config.flags & CONFIG_DISABLE_GLITCH ? "true" : "false") <<
375                         "</disableGlitchFilter>\n" <<
376
377                 "       <enableCache>" <<
378                         (config.flags & CONFIG_ENABLE_CACHE ? "true" : "false") <<
379                         "</enableCache>\n" <<
380
381                 "       <enableDisconnect>" <<
382                         (config.flags & CONFIG_ENABLE_DISCONNECT ? "true" : "false") <<
383                         "</enableDisconnect>\n" <<
384
385                 "       <!-- ********************************************************\n" <<
386                 "       Respond to very short duration selection attempts. This supports\n" <<
387                 "       non-standard hardware, but is generally safe to enable.\n" <<
388                 "       Required for Philips P2000C.\n" <<
389                 "       ********************************************************* -->\n" <<
390                 "       <selLatch>" <<
391                         (config.flags & CONFIG_ENABLE_SEL_LATCH? "true" : "false") <<
392                         "</selLatch>\n" <<
393
394
395                 "       <!-- ********************************************************\n" <<
396                 "       Convert luns to IDs. The unit must already be configured to respond\n" <<
397                 "       on the ID. Allows dual drives to be accessed from a \n" <<
398                 "       XEBEC S1410 SASI bridge.\n" <<
399                 "       eg. Configured for dual drives as IDs 0 and 1, but the XEBEC will\n" <<
400                 "       access the second disk as ID0, lun 1.\n" <<
401                 "       See ttp://bitsavers.trailing-edge.com/pdf/xebec/104524C_S1410Man_Aug83.pdf\n" <<
402                 "       ********************************************************* -->\n" <<
403                 "       <mapLunsToIds>" <<
404                         (config.flags & CONFIG_MAP_LUNS_TO_IDS ? "true" : "false") <<
405                         "</mapLunsToIds>\n" <<
406
407                 "       <!-- ********************************************************\n" <<
408                 "       Delay (in milliseconds) before responding to a SCSI selection.\n" <<
409                 "       255 (auto) sets it to 0 for SCSI2 hosts and 1ms otherwise.\n" <<
410                 "       Some samplers need this set to 1 manually.\n" <<
411                 "       ********************************************************* -->\n" <<
412                 "       <selectionDelay>" << static_cast<int>(config.selectionDelay) << "</selectionDelay>\n" <<
413
414                 "       <!-- ********************************************************\n" <<
415                 "       Startup delay (in seconds) before responding to the SCSI bus \n" <<
416                 "       after power on. Default = 0.\n" <<
417                 "       ********************************************************* -->\n" <<
418                 "       <startupDelay>" << static_cast<int>(config.startupDelay) << "</startupDelay>\n" <<
419                 "</BoardConfig>\n";
420
421         return s.str();
422 }
423
424
425 static uint64_t parseInt(wxXmlNode* node, uint64_t limit)
426 {
427         std::string str(node->GetNodeContent().mb_str());
428         if (str.empty())
429         {
430                 throw std::runtime_error("Empty " + node->GetName());
431         }
432
433         std::stringstream s;
434         if (str.find("0x") == 0)
435         {
436                 s << std::hex << str.substr(2);
437         }
438         else
439         {
440                 s << str;
441         }
442
443         uint64_t result;
444         s >> result;
445         if (!s)
446         {
447                 throw std::runtime_error("Invalid value for " + node->GetName());
448         }
449
450         if (result > limit)
451         {
452                 std::stringstream msg;
453                 msg << "Invalid value for " << node->GetName() <<
454                         " (max=" << limit << ")";
455                 throw std::runtime_error(msg.str());
456         }
457         return result;
458 }
459
460 static TargetConfig
461 parseTarget(wxXmlNode* node)
462 {
463         int id;
464         {
465                 std::stringstream s;
466                 s << node->GetAttribute("id", "7");
467                 s >> id;
468                 if (!s) throw std::runtime_error("Could not parse SCSITarget id attr");
469         }
470         TargetConfig result = ConfigUtil::Default(id & 0x7);
471
472         wxXmlNode *child = node->GetChildren();
473         while (child)
474         {
475                 if (child->GetName() == "enabled")
476                 {
477                         std::string s(child->GetNodeContent().mb_str());
478                         if (s == "true")
479                         {
480                                 result.scsiId |= CONFIG_TARGET_ENABLED;
481                         }
482                         else
483                         {
484                                 result.scsiId = result.scsiId & ~CONFIG_TARGET_ENABLED;
485                         }
486                 }
487                 else if (child->GetName() == "quirks")
488                 {
489                         std::stringstream s(std::string(child->GetNodeContent().mb_str()));
490                         std::string quirk;
491                         while (s >> quirk)
492                         {
493                                 if (quirk == "apple")
494                                 {
495                                         result.quirks |= CONFIG_QUIRKS_APPLE;
496                                 }
497                                 else if (quirk == "omti")
498                                 {
499                                         result.quirks |= CONFIG_QUIRKS_OMTI;
500                                 }
501                         }
502                 }
503                 else if (child->GetName() == "deviceType")
504                 {
505                         result.deviceType = parseInt(child, 0xFF);
506                 }
507                 else if (child->GetName() == "deviceTypeModifier")
508                 {
509                         result.deviceTypeModifier = parseInt(child, 0xFF);
510                 }
511                 else if (child->GetName() == "sdSectorStart")
512                 {
513                         result.sdSectorStart = parseInt(child, 0xFFFFFFFF);
514                 }
515                 else if (child->GetName() == "scsiSectors")
516                 {
517                         result.scsiSectors = parseInt(child, 0xFFFFFFFF);
518                 }
519                 else if (child->GetName() == "bytesPerSector")
520                 {
521                         result.bytesPerSector = parseInt(child, 8192);
522                 }
523                 else if (child->GetName() == "sectorsPerTrack")
524                 {
525                         result.sectorsPerTrack = parseInt(child, 255);
526                 }
527                 else if (child->GetName() == "headsPerCylinder")
528                 {
529                         result.headsPerCylinder = parseInt(child, 255);
530                 }
531                 else if (child->GetName() == "vendor")
532                 {
533                         std::string s(child->GetNodeContent().mb_str());
534                         s = s.substr(0, sizeof(result.vendor));
535                         memset(result.vendor, ' ', sizeof(result.vendor));
536                         memcpy(result.vendor, s.c_str(), s.size());
537                 }
538                 else if (child->GetName() == "prodId")
539                 {
540                         std::string s(child->GetNodeContent().mb_str());
541                         s = s.substr(0, sizeof(result.prodId));
542                         memset(result.prodId, ' ', sizeof(result.prodId));
543                         memcpy(result.prodId, s.c_str(), s.size());
544                 }
545                 else if (child->GetName() == "revision")
546                 {
547                         std::string s(child->GetNodeContent().mb_str());
548                         s = s.substr(0, sizeof(result.revision));
549                         memset(result.revision, ' ', sizeof(result.revision));
550                         memcpy(result.revision, s.c_str(), s.size());
551                 }
552                 else if (child->GetName() == "serial")
553                 {
554                         std::string s(child->GetNodeContent().mb_str());
555                         s = s.substr(0, sizeof(result.serial));
556                         memset(result.serial, ' ', sizeof(result.serial));
557                         memcpy(result.serial, s.c_str(), s.size());
558                 }
559                 else if (child->GetName() == "modePages")
560                 {
561                         wxMemoryBuffer buf =
562                                 wxBase64Decode(child->GetNodeContent(), wxBase64DecodeMode_SkipWS);
563                         size_t len = std::min(buf.GetDataLen(), sizeof(result.modePages));
564                         memcpy(result.modePages, buf.GetData(), len);
565                 }
566                 else if (child->GetName() == "vpd")
567                 {
568                         wxMemoryBuffer buf =
569                                 wxBase64Decode(child->GetNodeContent(), wxBase64DecodeMode_SkipWS);
570                         size_t len = std::min(buf.GetDataLen(), sizeof(result.vpd));
571                         memcpy(result.vpd, buf.GetData(), len);
572                 }
573
574
575
576                 child = child->GetNext();
577         }
578         return result;
579 }
580
581 static BoardConfig
582 parseBoardConfig(wxXmlNode* node)
583 {
584         BoardConfig result = ConfigUtil::DefaultBoardConfig();
585
586         wxXmlNode *child = node->GetChildren();
587         while (child)
588         {
589                 if (child->GetName() == "selectionDelay")
590                 {
591                         result.selectionDelay = parseInt(child, 255);
592                 }
593                 else if (child->GetName() == "startupDelay")
594                 {
595                         result.startupDelay = parseInt(child, 255);
596                 }
597                 else if (child->GetName() == "unitAttention")
598                 {
599                         std::string s(child->GetNodeContent().mb_str());
600                         if (s == "true")
601                         {
602                                 result.flags |= CONFIG_ENABLE_UNIT_ATTENTION;
603                         }
604                         else
605                         {
606                                 result.flags = result.flags & ~CONFIG_ENABLE_UNIT_ATTENTION;
607                         }
608                 }
609                 else if (child->GetName() == "parity")
610                 {
611                         std::string s(child->GetNodeContent().mb_str());
612                         if (s == "true")
613                         {
614                                 result.flags |= CONFIG_ENABLE_PARITY;
615                         }
616                         else
617                         {
618                                 result.flags = result.flags & ~CONFIG_ENABLE_PARITY;
619                         }
620                 }
621                 else if (child->GetName() == "enableScsi2")
622                 {
623                         std::string s(child->GetNodeContent().mb_str());
624                         if (s == "true")
625                         {
626                                 result.flags |= CONFIG_ENABLE_SCSI2;
627                         }
628                         else
629                         {
630                                 result.flags = result.flags & ~CONFIG_ENABLE_SCSI2;
631                         }
632                 }
633                 else if (child->GetName() == "disableGlitchFilter")
634                 {
635                         std::string s(child->GetNodeContent().mb_str());
636                         if (s == "true")
637                         {
638                                 result.flags |= CONFIG_DISABLE_GLITCH;
639                         }
640                         else
641                         {
642                                 result.flags = result.flags & ~CONFIG_DISABLE_GLITCH;
643                         }
644                 }
645                 else if (child->GetName() == "enableCache")
646                 {
647                         std::string s(child->GetNodeContent().mb_str());
648                         if (s == "true")
649                         {
650                                 result.flags |= CONFIG_ENABLE_CACHE;
651                         }
652                         else
653                         {
654                                 result.flags = result.flags & ~CONFIG_ENABLE_CACHE;
655                         }
656                 }
657                 else if (child->GetName() == "enableDisconnect")
658                 {
659                         std::string s(child->GetNodeContent().mb_str());
660                         if (s == "true")
661                         {
662                                 result.flags |= CONFIG_ENABLE_DISCONNECT;
663                         }
664                         else
665                         {
666                                 result.flags = result.flags & ~CONFIG_ENABLE_DISCONNECT;
667                         }
668                 }
669                 else if (child->GetName() == "selLatch")
670                 {
671                         std::string s(child->GetNodeContent().mb_str());
672                         if (s == "true")
673                         {
674                                 result.flags |= CONFIG_ENABLE_SEL_LATCH;
675                         }
676                         else
677                         {
678                                 result.flags = result.flags & ~CONFIG_ENABLE_SEL_LATCH;
679                         }
680                 }
681                 else if (child->GetName() == "mapLunsToIds")
682                 {
683                         std::string s(child->GetNodeContent().mb_str());
684                         if (s == "true")
685                         {
686                                 result.flags |= CONFIG_MAP_LUNS_TO_IDS;
687                         }
688                         else
689                         {
690                                 result.flags = result.flags & ~CONFIG_MAP_LUNS_TO_IDS;
691                         }
692                 }
693                 child = child->GetNext();
694         }
695         return result;
696 }
697
698
699 std::pair<BoardConfig, std::vector<TargetConfig>>
700 ConfigUtil::fromXML(const std::string& filename)
701 {
702         wxXmlDocument doc;
703         if (!doc.Load(filename))
704         {
705                 throw std::runtime_error("Could not load XML file");
706         }
707
708         // start processing the XML file
709         if (doc.GetRoot()->GetName() != "SCSI2SD")
710         {
711                 throw std::runtime_error("Invalid root node, expected <SCSI2SD>");
712         }
713
714         BoardConfig boardConfig = DefaultBoardConfig();
715         int boardConfigFound = 0;
716
717         std::vector<TargetConfig> targets;
718         wxXmlNode *child = doc.GetRoot()->GetChildren();
719         while (child)
720         {
721                 if (child->GetName() == "SCSITarget")
722                 {
723                         targets.push_back(parseTarget(child));
724                 }
725                 else if (child->GetName() == "BoardConfig")
726                 {
727                         boardConfig = parseBoardConfig(child);
728                         boardConfigFound = 1;
729                 }
730                 child = child->GetNext();
731         }
732
733         if (!boardConfigFound && targets.size() > 0)
734         {
735                 boardConfig.flags = targets[0].flagsDEPRECATED;
736         }
737         return make_pair(boardConfig, targets);
738 }
739