3030#include " mainwindow.h"
3131#include " utils.h"
3232
33+ #include < cstdint>
3334#include < deque>
3435#include < memory>
3536#include < optional>
4041
4142#include < QSize>
4243#include < QString>
44+ #include < QXmlStreamReader>
4345#include < QtWidgets>
4446
4547enum class NODISCARD CancelDispositionEnum : uint8_t { Forbid, Allow };
@@ -48,6 +50,19 @@ enum class NODISCARD PollResultEnum : uint8_t { Timeout, Finished };
4850namespace { // anonymous
4951namespace mwa_detail {
5052
53+ NODISCARD bool detectMm2Binary (const QString &fileName)
54+ {
55+ QFile f{fileName};
56+ if (!f.open (QIODevice::ReadOnly)) {
57+ return false ;
58+ }
59+
60+ static constexpr auto MMAPPER_MAGIC = static_cast <int32_t >(0xFFB2AF01u );
61+ int32_t magic = 0 ;
62+ QDataStream (&f) >> magic;
63+ return magic == MMAPPER_MAGIC ;
64+ }
65+
5166// MMapper2 XML map (as opposed to Pandora XML map)
5267NODISCARD bool detectMm2Xml (const QString &fileName)
5368{
@@ -65,6 +80,94 @@ NODISCARD bool detectMm2Xml(const QString &fileName)
6580 return line2.contains (" mmapper2xml" );
6681}
6782
83+ // Pandora XML map
84+ NODISCARD bool detectPandora (const QString &fileName)
85+ {
86+ QFile f{fileName};
87+ if (!f.open (QIODevice::ReadOnly)) {
88+ return false ;
89+ }
90+ QXmlStreamReader xml{&f};
91+ if (xml.readNextStartElement () && xml.error () != QXmlStreamReader::NoError) {
92+ return false ;
93+ } else if (xml.name () != " map" ) {
94+ return false ;
95+ } else if (xml.attributes ().isEmpty () || !xml.attributes ().hasAttribute (" rooms" )) {
96+ return false ;
97+ }
98+ return true ;
99+ }
100+
101+ template <typename T>
102+ NODISCARD std::unique_ptr<AbstractMapStorage> make (const AbstractMapStorage::Data &data,
103+ MainWindow *const mw)
104+ {
105+ return std::make_unique<T>(data, mw);
106+ }
107+
108+ class NODISCARD FileFormatHelper final
109+ {
110+ public:
111+ using DetectFn = bool (*)(const QString &);
112+ using MakeFn = std::unique_ptr<AbstractMapStorage> (*)(const AbstractMapStorage::Data &data,
113+ MainWindow *mw);
114+
115+ private:
116+ DetectFn m_detect = nullptr ;
117+ MakeFn m_make = nullptr ;
118+
119+ public:
120+ explicit FileFormatHelper (const DetectFn d, const MakeFn m)
121+ : m_detect{d}
122+ , m_make{m}
123+ {
124+ assert (m_detect != nullptr );
125+ assert (m_make != nullptr );
126+ if (m_detect == nullptr || m_make == nullptr ) {
127+ std::abort ();
128+ }
129+ }
130+
131+ private:
132+ static void logException (const mm::source_location loc)
133+ {
134+ try {
135+ std::rethrow_exception (std::current_exception ());
136+ } catch (const std::exception &ex) {
137+ mm::WarningOstream{loc} << ex.what ();
138+ } catch (...) {
139+ mm::WarningOstream{loc} << " Unknown exception." ;
140+ }
141+ }
142+
143+ public:
144+ NODISCARD bool detect (const QString &fileName) const
145+ {
146+ try {
147+ return m_detect (fileName);
148+ } catch (...) {
149+ logException (MM_SOURCE_LOCATION ());
150+ return false ;
151+ }
152+ }
153+ NODISCARD std::unique_ptr<AbstractMapStorage> make (const AbstractMapStorage::Data &data,
154+ MainWindow *mw) const
155+ {
156+ try {
157+ return m_make (data, mw);
158+ } catch (...) {
159+ logException (MM_SOURCE_LOCATION ());
160+ throw ;
161+ }
162+ }
163+ };
164+
165+ const std::array<FileFormatHelper, 3 > formats{
166+ FileFormatHelper{&detectMm2Binary, &make<MapStorage>},
167+ FileFormatHelper{&detectMm2Xml, &make<XmlMapStorage>},
168+ FileFormatHelper{&detectPandora, &make<PandoraMapStorage>},
169+ };
170+
68171NODISCARD bool hasRooms (const RawMapLoadData &data)
69172{
70173 return !data.rooms .empty ();
@@ -630,24 +733,13 @@ std::unique_ptr<AbstractMapStorage> MainWindow::getLoadOrMergeMapStorage(
630733{
631734 auto tmp = [this , pc, &fileName, pFile]() -> std::unique_ptr<AbstractMapStorage> {
632735 auto &file = deref (pFile);
633- const auto fileNameLower = fileName.toLower ();
634-
635736 const AbstractMapStorage::Data data{pc, fileName, file};
636- if (fileNameLower.endsWith (" .xml" )) {
637- if (mwa_detail::detectMm2Xml (fileName)) {
638- // MMapper2 XML map
639- return std::make_unique<XmlMapStorage>(data, this );
640- } else {
641- // Pandora map
642- return std::make_unique<PandoraMapStorage>(data, this );
737+ for (const auto &fmt : mwa_detail::formats) {
738+ if (fmt.detect (fileName)) {
739+ return fmt.make (data, this );
643740 }
644- } else if (fileNameLower.endsWith (" .mm2xml" )) {
645- // MMapper2 XML map
646- return std::make_unique<XmlMapStorage>(data, this );
647- } else {
648- // MMapper2 binary map
649- return std::make_unique<MapStorage>(data, this );
650741 }
742+ throw std::runtime_error (" unrecognized map file data format" );
651743 }();
652744
653745 AbstractMapStorage *const pStorage = tmp.get ();
0 commit comments