#pragma once #include #include #include #include #include #include "data/file.hpp" #include "data/iterators.hpp" namespace devilution { /** * @brief A record reader that treats every error as fatal. */ class RecordReader { public: RecordReader(DataFileRecord &record, std::string_view filename) : it_(record.begin()) , end_(record.end()) , filename_(filename) { } template typename std::enable_if_t, void> readInt(std::string_view name, T &out) { DataFileField field = nextField(); failOnError(field.parseInt(out), name, field); } template typename std::enable_if_t, void> readOptionalInt(std::string_view name, T &out) { DataFileField field = nextField(); if (field.value().empty()) return; failOnError(field.parseInt(out), name, field); } template void readIntArray(std::string_view name, T (&out)[N]) { DataFileField field = nextField(); failOnError(field.parseIntArray(out), name, field); } template void readEnumArray(std::string_view name, std::optional fillMissing, T (&out)[N], F &&parseFn) { DataFileField field = nextField(); failOnError(field.parseEnumArray(out, fillMissing, parseFn), name, field, DataFileField::Error::InvalidValue); } template void readIntArray(std::string_view name, std::array &out) { DataFileField field = nextField(); failOnError(field.parseIntArray(out), name, field); } template typename std::enable_if_t, void> readFixed6(std::string_view name, T &out) { DataFileField field = nextField(); failOnError(field.parseFixed6(out), name, field); } void readBool(std::string_view name, bool &out) { DataFileField field = nextField(); failOnError(field.parseBool(out), name, field); } void readString(std::string_view name, std::string &out) { advance(); out = (*it_).value(); } template void read(std::string_view name, T &out, F &&parseFn) { DataFileField field = nextField(); tl::expected result = parseFn(field.value()); failOnError(result, name, field, DataFileField::Error::InvalidValue); out = *std::move(result); } template void readEnumList(std::string_view name, T &out, F &&parseFn) { DataFileField field = nextField(); failOnError(field.parseEnumList(out, std::forward(parseFn)), name, field, DataFileField::Error::InvalidValue); } std::string_view value() { advance(); needsIncrement_ = false; return (*it_).value(); } void advance(); DataFileField nextField() { advance(); return *it_; } private: template void failOnError(const tl::expected &result, std::string_view name, const DataFileField &field) { if (!result.has_value()) { DataFile::reportFatalFieldError(result.error(), filename_, name, field); } } template void failOnError(const tl::expected &result, std::string_view name, const DataFileField &field, DataFileField::Error error) { if (!result.has_value()) { DataFile::reportFatalFieldError(error, filename_, name, field, result.error()); } } FieldIterator it_; const FieldIterator end_; std::string_view filename_; bool needsIncrement_ = false; }; } // namespace devilution