Skip to content

Commit

Permalink
Simplify txtdata parsing with a helper class
Browse files Browse the repository at this point in the history
  • Loading branch information
glebm committed Nov 14, 2023
1 parent f566359 commit cfdade5
Show file tree
Hide file tree
Showing 10 changed files with 270 additions and 645 deletions.
2 changes: 1 addition & 1 deletion Packaging/resources/assets/txtdata/items/item_prefixes.tsv
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name power.type power.param1 power.param2 minLevel itemTypes alignment doubleChance useful minVal maxVal multVal
name power power.value1 power.value2 minLevel itemTypes alignment doubleChance useful minVal maxVal multVal
Tin TOHIT_CURSE 6 10 3 Weapon,Bow,Misc Any true false 0 0 -3
Brass TOHIT_CURSE 1 5 1 Weapon,Bow,Misc Any true false 0 0 -2
Bronze TOHIT 1 5 1 Weapon,Bow,Misc Any true true 100 500 2
Expand Down
2 changes: 1 addition & 1 deletion Packaging/resources/assets/txtdata/items/item_suffixes.tsv
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name power.type power.param1 power.param2 minLevel itemTypes alignment doubleChance useful minVal maxVal multVal
name power power.value1 power.value2 minLevel itemTypes alignment doubleChance useful minVal maxVal multVal
quality DAMMOD 1 2 2 Weapon,Staff,Bow Any false true 100 200 2
maiming DAMMOD 3 5 7 Weapon,Staff,Bow Any false true 1300 1500 3
slaying DAMMOD 6 8 15 Weapon Any false true 2600 3000 5
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name uniqueBaseItem minLevel value powers[0].type powers[0].param1 powers[0].param2 powers[1].type powers[1].param1 powers[1].param2 powers[2].type powers[2].param1 powers[2].param2 powers[3].type powers[3].param1 powers[3].param2 powers[4].type powers[4].param1 powers[4].param2 powers[5].type powers[5].param1 powers[5].param2
name uniqueBaseItem minLevel value power0 power0.value1 power0.value2 power1 power1.value1 power1.value2 power2 power2.value1 power2.value2 power3 power3.value1 power3.value2 power4 power4.value1 power4.value2 power5 power5.value1 power5.value2
The Butcher's Cleaver CLEAVER 1 3650 STR 10 10 SETDAM 4 24 SETDUR 10 10
The Undead Crown SKCROWN 1 16650 RNDSTEALLIFE SETAC 8 8 INVCURS 77
Empyrean Band INFRARING 1 8000 ATTRIBS 2 2 LIGHT 2 2 FASTRECOVER 1 1 ABSHALFTRAP
Expand Down
3 changes: 2 additions & 1 deletion Source/.clang-tidy
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ Checks: >
-modernize-avoid-c-arrays,
-modernize-use-trailing-return-type,
-modernize-concat-nested-namespaces,
-modernize-avoid-bind
-modernize-avoid-bind,
-modernize-use-constraints
HeaderFilterRegex: "^(Source|test)\\.h$"

Expand Down
1 change: 1 addition & 0 deletions Source/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ set(libdevilutionx_SRCS

data/file.cpp
data/parser.cpp
data/record_reader.cpp

DiabloUI/button.cpp
DiabloUI/credits.cpp
Expand Down
4 changes: 2 additions & 2 deletions Source/data/iterators.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ class DataFileField {
}

template <typename T, typename ParseFn>
[[nodiscard]] tl::expected<void, Error> parseEnumList(T &destination, ParseFn &&parseFn)
[[nodiscard]] tl::expected<void, std::string> parseEnumList(T &destination, ParseFn &&parseFn)
{
destination = {};
const std::string_view str = value();
Expand All @@ -155,7 +155,7 @@ class DataFileField {
for (const std::string_view part : SplitByChar(str, ',')) {
auto result = parseFn(part);
if (!result.has_value())
return tl::make_unexpected(Error::InvalidValue);
return tl::make_unexpected(std::move(result).error());
destination |= result.value();
}
return {};
Expand Down
17 changes: 17 additions & 0 deletions Source/data/record_reader.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#include "data/record_reader.hpp"

namespace devilution {

void RecordReader::advance()
{
if (needsIncrement_) {
++it_;
} else {
needsIncrement_ = true;
}
if (it_ == end_) {
DataFile::reportFatalError(DataFile::Error::NotEnoughColumns, filename_);
}
}

} // namespace devilution
123 changes: 123 additions & 0 deletions Source/data/record_reader.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
#pragma once

#include <string_view>
#include <type_traits>

#include <expected.hpp>
#include <function_ref.hpp>

#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 T>
typename std::enable_if_t<std::is_integral_v<T>, void>
readInt(std::string_view name, T &out)
{
advance();
DataFileField field = *it_;
if (tl::expected<void, DataFileField::Error> result = field.parseInt(out); !result.has_value()) {
DataFile::reportFatalFieldError(result.error(), filename_, name, field);
}
}

template <typename T>
typename std::enable_if_t<std::is_integral_v<T>, void>
readOptionalInt(std::string_view name, T &out)
{
advance();
DataFileField field = *it_;
if (field.value().empty()) return;
if (tl::expected<void, DataFileField::Error> result = field.parseInt(out); !result.has_value()) {
DataFile::reportFatalFieldError(result.error(), filename_, name, field);
}
}

template <typename T, size_t N>
void readIntArray(std::string_view name, T (&out)[N])
{
advance();
DataFileField field = *it_;
if (tl::expected<void, DataFileField::Error> result = field.parseIntArray(out); !result.has_value()) {
DataFile::reportFatalFieldError(result.error(), filename_, name, field);
}
}

template <typename T>
typename std::enable_if_t<std::is_integral_v<T>, void>
readFixed6(std::string_view name, T &out)
{
advance();
DataFileField field = *it_;
if (tl::expected<void, DataFileField::Error> result = field.parseFixed6(out); !result.has_value()) {
DataFile::reportFatalFieldError(result.error(), filename_, name, field);
}
}

void readBool(std::string_view name, bool &out)
{
advance();
DataFileField field = *it_;
if (tl::expected<void, DataFileField::Error> result = field.parseBool(out); !result.has_value()) {
DataFile::reportFatalFieldError(result.error(), filename_, name, field);
}
}

void readString(std::string_view name, std::string &out)
{
advance();
out = (*it_).value();
}

template <typename T, typename F>
void read(std::string_view name, T &out, F &&parseFn)
{
advance();
DataFileField field = *it_;
if (tl::expected<T, std::string> result = parseFn(field.value()); result.has_value()) {
out = *std::move(result);
} else {
DataFile::reportFatalFieldError(DataFileField::Error::InvalidValue, filename_, name, field, result.error());
}
}

template <typename T, typename F>
void readEnumList(std::string_view name, T &out, F &&parseFn)
{
advance();
DataFileField field = *it_;
if (tl::expected<void, std::string> result = field.parseEnumList(out, std::forward<F>(parseFn)); !result.has_value()) {
DataFile::reportFatalFieldError(DataFileField::Error::InvalidValue, filename_, name, field, result.error());
}
}

std::string_view value()
{
advance();
needsIncrement_ = false;
return (*it_).value();
}

void advance();

private:
FieldIterator it_;
const FieldIterator end_;
std::string_view filename_;
bool needsIncrement_ = false;
};

} // namespace devilution
Loading

0 comments on commit cfdade5

Please sign in to comment.