Skip to content

Commit 60ba7b3

Browse files
nschimmeNils Schimmelmann
authored andcommitted
refactor map file data format detection
1 parent 79798cc commit 60ba7b3

1 file changed

Lines changed: 107 additions & 15 deletions

File tree

src/mainwindow/mainwindow-async.cpp

Lines changed: 107 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include "mainwindow.h"
3131
#include "utils.h"
3232

33+
#include <cstdint>
3334
#include <deque>
3435
#include <memory>
3536
#include <optional>
@@ -40,6 +41,7 @@
4041

4142
#include <QSize>
4243
#include <QString>
44+
#include <QXmlStreamReader>
4345
#include <QtWidgets>
4446

4547
enum class NODISCARD CancelDispositionEnum : uint8_t { Forbid, Allow };
@@ -48,6 +50,19 @@ enum class NODISCARD PollResultEnum : uint8_t { Timeout, Finished };
4850
namespace { // anonymous
4951
namespace 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)
5267
NODISCARD 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+
68171
NODISCARD 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

Comments
 (0)