From 9034c893a7738de0fa593aa2bdabea99213c4355 Mon Sep 17 00:00:00 2001 From: Joshua Scott Date: Sun, 1 Apr 2018 16:23:36 +0100 Subject: [PATCH] dml: Add more robust error reporting --- include/ki/dml/Field.h | 21 ++++++++++++++++----- include/ki/dml/exception.h | 26 ++++++++++++++++++++++++++ src/dml/FieldBase.cpp | 10 ++++++---- src/dml/Record.cpp | 5 +++-- src/dml/types/BytField.cpp | 6 ++++++ src/dml/types/DblField.cpp | 18 ++++++++++++++---- src/dml/types/FltField.cpp | 17 +++++++++++++---- src/dml/types/GidField.cpp | 12 ++++++++++-- src/dml/types/IntField.cpp | 12 ++++++++++-- src/dml/types/ShrtField.cpp | 12 ++++++++++-- src/dml/types/StrField.cpp | 18 ++++++++++++++++-- src/dml/types/UBytField.cpp | 6 ++++++ src/dml/types/UIntField.cpp | 12 ++++++++++-- src/dml/types/UShrtField.cpp | 7 +++++++ src/dml/types/WStrField.cpp | 21 +++++++++++++++++++-- 15 files changed, 172 insertions(+), 31 deletions(-) create mode 100644 include/ki/dml/exception.h diff --git a/include/ki/dml/Field.h b/include/ki/dml/Field.h index 9d704b3..4c7f435 100644 --- a/include/ki/dml/Field.h +++ b/include/ki/dml/Field.h @@ -1,7 +1,9 @@ #pragma once #include "FieldBase.h" #include "types.h" +#include "exception.h" #include +#include namespace ki { @@ -69,7 +71,7 @@ namespace dml * Example: Value * * If the field in the XML data does not have the same type - * as this field, then an exception is thrown. + * as this field, then a value_error is thrown. */ void from_xml(const rapidxml::xml_node<> *node) final { @@ -82,12 +84,15 @@ namespace dml attr; attr = attr->next_attribute()) { const std::string name = attr->name(); - if (name != "TYPE") + if (name == "TYPE") { const std::string value = attr->value(); if (value != get_type_name()) { - // TODO: Exceptions + std::ostringstream oss; + oss << "XML Field node has incorrect TYPE attribute value. "; + oss << "(value=\"" << value << "\", expected=\"" << get_type_name() << "\". "; + throw value_error(oss.str()); } } else if (name == "NOXFER") @@ -97,7 +102,9 @@ namespace dml } else { - // TODO: Exceptions + std::ostringstream oss; + oss << "XML Field node has unknown attribute \"" << name << "\"."; + throw value_error(oss.str()); } } @@ -140,7 +147,11 @@ namespace dml } else { - // TODO: Exceptions + std::ostringstream oss; + oss << "Tried to copy value from " << + other->get_type_name() << " field to " << + get_type_name() << " field."; + throw value_error(oss.str()); } } diff --git a/include/ki/dml/exception.h b/include/ki/dml/exception.h new file mode 100644 index 0000000..8999370 --- /dev/null +++ b/include/ki/dml/exception.h @@ -0,0 +1,26 @@ +#pragma once +#include + +namespace ki +{ +namespace dml +{ + class runtime_error : public std::runtime_error + { + public: + runtime_error(std::string message) : std::runtime_error(message) { } + }; + + class parse_error : public runtime_error + { + public: + parse_error(std::string message) : runtime_error(message) { } + }; + + class value_error : public runtime_error + { + public: + value_error(std::string message) : runtime_error(message) { } + }; +} +} \ No newline at end of file diff --git a/src/dml/FieldBase.cpp b/src/dml/FieldBase.cpp index 3155fd1..d1c9f0b 100644 --- a/src/dml/FieldBase.cpp +++ b/src/dml/FieldBase.cpp @@ -33,8 +33,9 @@ namespace dml auto *type_attr = node->first_attribute("TYPE"); if (!type_attr) { - // TODO: Exceptions - return nullptr; + std::ostringstream oss; + oss << "XML Field node is missing required TYPE attribute (" << node->name() << ")."; + throw value_error(oss.str()); } const std::string type = type_attr->value(); @@ -63,8 +64,9 @@ namespace dml field = new GidField("", record); else { - // TODO: Exceptions - return nullptr; + std::ostringstream oss; + oss << "Unknown DML type \"" << type << "\" in XML Field node: " << node->name() << "."; + throw value_error(oss.str()); } field->from_xml(node); diff --git a/src/dml/Record.cpp b/src/dml/Record.cpp index 9629dba..489e86f 100644 --- a/src/dml/Record.cpp +++ b/src/dml/Record.cpp @@ -94,8 +94,9 @@ namespace dml const std::string node_name = node->name(); if (node_name != "RECORD") { - // TODO: Exceptions - return; + std::ostringstream oss; + oss << "Expected node but got <" << node->name() << ">."; + throw value_error(oss.str()); } // Every child node inside a element is a Field. diff --git a/src/dml/types/BytField.cpp b/src/dml/types/BytField.cpp index 6c73577..a2b088d 100644 --- a/src/dml/types/BytField.cpp +++ b/src/dml/types/BytField.cpp @@ -18,6 +18,12 @@ namespace dml { ValueBytes data; istream.read(data.buff, sizeof(BYT)); + if (istream.fail()) + { + std::ostringstream oss; + oss << "Not enough data was available to read BYT value (" << m_name << ")."; + throw parse_error(oss.str()); + } m_value = data.value; } diff --git a/src/dml/types/DblField.cpp b/src/dml/types/DblField.cpp index 3b181ce..e9d2c35 100644 --- a/src/dml/types/DblField.cpp +++ b/src/dml/types/DblField.cpp @@ -9,24 +9,34 @@ namespace dml template <> void DblField::write_to(std::ostream &ostream) const { - ValueBytes endianness_check; - endianness_check.value = 0x0102; ValueBytes data; data.value = m_value; + + ValueBytes endianness_check; + endianness_check.value = 0x0102; if (endianness_check.buff[0] == 0x01) std::reverse(&data.buff[0], &data.buff[8]); + ostream.write(data.buff, sizeof(DBL)); } template <> void DblField::read_from(std::istream &istream) { - ValueBytes endianness_check; - endianness_check.value = 0x0102; ValueBytes data; istream.read(data.buff, sizeof(DBL)); + if (istream.fail()) + { + std::ostringstream oss; + oss << "Not enough data was available to read DBL value (" << m_name << ")."; + throw parse_error(oss.str()); + } + + ValueBytes endianness_check; + endianness_check.value = 0x0102; if (endianness_check.buff[0] == 0x01) std::reverse(&data.buff[0], &data.buff[8]); + m_value = data.value; } diff --git a/src/dml/types/FltField.cpp b/src/dml/types/FltField.cpp index 5a304d9..5c13551 100644 --- a/src/dml/types/FltField.cpp +++ b/src/dml/types/FltField.cpp @@ -9,22 +9,31 @@ namespace dml template <> void FltField::write_to(std::ostream &ostream) const { - ValueBytes endianness_check; - endianness_check.value = 0x0102; ValueBytes data; data.value = m_value; + + ValueBytes endianness_check; + endianness_check.value = 0x0102; if (endianness_check.buff[0] == 0x01) std::reverse(&data.buff[0], &data.buff[4]); + ostream.write(data.buff, sizeof(FLT)); } template <> void FltField::read_from(std::istream &istream) { - ValueBytes endianness_check; - endianness_check.value = 0x0102; ValueBytes data; istream.read(data.buff, sizeof(FLT)); + if (istream.fail()) + { + std::ostringstream oss; + oss << "Not enough data was available to read FLT value (" << m_name << ")."; + throw parse_error(oss.str()); + } + + ValueBytes endianness_check; + endianness_check.value = 0x0102; if (endianness_check.buff[0] == 0x01) std::reverse(&data.buff[0], &data.buff[4]); m_value = data.value; diff --git a/src/dml/types/GidField.cpp b/src/dml/types/GidField.cpp index 600f9c8..261d1b7 100644 --- a/src/dml/types/GidField.cpp +++ b/src/dml/types/GidField.cpp @@ -19,12 +19,20 @@ namespace dml template <> void GidField::read_from(std::istream &istream) { - ValueBytes endianness_check; - endianness_check.value = 0x0102; ValueBytes data; istream.read(data.buff, sizeof(GID)); + if (istream.fail()) + { + std::ostringstream oss; + oss << "Not enough data was available to read GID value (" << m_name << ")."; + throw parse_error(oss.str()); + } + + ValueBytes endianness_check; + endianness_check.value = 0x0102; if (endianness_check.buff[0] == 0x01) std::reverse(&data.buff[0], &data.buff[8]); + m_value = data.value; } diff --git a/src/dml/types/IntField.cpp b/src/dml/types/IntField.cpp index f664ed0..56b0019 100644 --- a/src/dml/types/IntField.cpp +++ b/src/dml/types/IntField.cpp @@ -19,12 +19,20 @@ namespace dml template <> void IntField::read_from(std::istream &istream) { - ValueBytes endianness_check; - endianness_check.value = 0x0102; ValueBytes data; istream.read(data.buff, sizeof(INT)); + if (istream.fail()) + { + std::ostringstream oss; + oss << "Not enough data was available to read INT value (" << m_name << ")."; + throw parse_error(oss.str()); + } + + ValueBytes endianness_check; + endianness_check.value = 0x0102; if (endianness_check.buff[0] == 0x01) std::reverse(&data.buff[0], &data.buff[4]); + m_value = data.value; } diff --git a/src/dml/types/ShrtField.cpp b/src/dml/types/ShrtField.cpp index b8c0054..f234bdc 100644 --- a/src/dml/types/ShrtField.cpp +++ b/src/dml/types/ShrtField.cpp @@ -19,12 +19,20 @@ namespace dml template <> void ShrtField::read_from(std::istream &istream) { - ValueBytes endianness_check; - endianness_check.value = 0x0102; ValueBytes data; istream.read(data.buff, sizeof(SHRT)); + if (istream.fail()) + { + std::ostringstream oss; + oss << "Not enough data was available to read SHRT value (" << m_name << ")."; + throw parse_error(oss.str()); + } + + ValueBytes endianness_check; + endianness_check.value = 0x0102; if (endianness_check.buff[0] == 0x01) std::reverse(&data.buff[0], &data.buff[2]); + m_value = data.value; } diff --git a/src/dml/types/StrField.cpp b/src/dml/types/StrField.cpp index 01e249c..028b108 100644 --- a/src/dml/types/StrField.cpp +++ b/src/dml/types/StrField.cpp @@ -21,16 +21,30 @@ namespace dml void StrField::read_from(std::istream &istream) { // Get the length - ValueBytes endianness_check; - endianness_check.value = 0x0102; ValueBytes length_data; istream.read(length_data.buff, sizeof(USHRT)); + if (istream.fail()) + { + std::ostringstream oss; + oss << "Not enough data was available to read STR value (" << m_name << ")."; + throw parse_error(oss.str()); + } + + ValueBytes endianness_check; + endianness_check.value = 0x0102; if (endianness_check.buff[0] == 0x01) std::reverse(&length_data.buff[0], &length_data.buff[2]); // Read the data into a buffer char *data = new char[length_data.value + 1] { 0 }; istream.read(data, length_data.value); + if (istream.fail()) + { + std::ostringstream oss; + oss << "Not enough data was available to read STR value (" << m_name << ")."; + throw parse_error(oss.str()); + } + m_value = STR(data); delete[] data; } diff --git a/src/dml/types/UBytField.cpp b/src/dml/types/UBytField.cpp index 72fb537..e470230 100644 --- a/src/dml/types/UBytField.cpp +++ b/src/dml/types/UBytField.cpp @@ -18,6 +18,12 @@ namespace dml { ValueBytes data; istream.read(data.buff, sizeof(UBYT)); + if (istream.fail()) + { + std::ostringstream oss; + oss << "Not enough data was available to read UBYT value (" << m_name << ")."; + throw parse_error(oss.str()); + } m_value = data.value; } diff --git a/src/dml/types/UIntField.cpp b/src/dml/types/UIntField.cpp index ad5f05c..5faf627 100644 --- a/src/dml/types/UIntField.cpp +++ b/src/dml/types/UIntField.cpp @@ -19,12 +19,20 @@ namespace dml template <> void UIntField::read_from(std::istream &istream) { - ValueBytes endianness_check; - endianness_check.value = 0x0102; ValueBytes data; istream.read(data.buff, sizeof(UINT)); + if (istream.fail()) + { + std::ostringstream oss; + oss << "Not enough data was available to read UINT value (" << m_name << ")."; + throw parse_error(oss.str()); + } + + ValueBytes endianness_check; + endianness_check.value = 0x0102; if (endianness_check.buff[0] == 0x01) std::reverse(&data.buff[0], &data.buff[4]); + m_value = data.value; } diff --git a/src/dml/types/UShrtField.cpp b/src/dml/types/UShrtField.cpp index f0fb73d..02eb0f9 100644 --- a/src/dml/types/UShrtField.cpp +++ b/src/dml/types/UShrtField.cpp @@ -23,6 +23,13 @@ namespace dml endianness_check.value = 0x0102; ValueBytes data; istream.read(data.buff, sizeof(USHRT)); + if (istream.fail()) + { + std::ostringstream oss; + oss << "Not enough data was available to read USHRT value (" << m_name << ")."; + throw parse_error(oss.str()); + } + if (endianness_check.buff[0] == 0x01) std::reverse(&data.buff[0], &data.buff[2]); m_value = data.value; diff --git a/src/dml/types/WStrField.cpp b/src/dml/types/WStrField.cpp index 0d7d7e9..c1c140a 100644 --- a/src/dml/types/WStrField.cpp +++ b/src/dml/types/WStrField.cpp @@ -23,10 +23,17 @@ namespace dml void WStrField::read_from(std::istream &istream) { // Get the length - ValueBytes endianness_check; - endianness_check.value = 0x0102; ValueBytes length_data; istream.read(length_data.buff, sizeof(USHRT)); + if (istream.fail()) + { + std::ostringstream oss; + oss << "Not enough data was available to read WSTR value (" << m_name << ")."; + throw parse_error(oss.str()); + } + + ValueBytes endianness_check; + endianness_check.value = 0x0102; if (endianness_check.buff[0] == 0x01) std::reverse(&length_data.buff[0], &length_data.buff[2]); @@ -34,11 +41,21 @@ namespace dml size_t length = length_data.value * sizeof(char16_t); char *data = new char[length + sizeof(char16_t)]{ 0 }; istream.read(data, length); + if (istream.fail()) + { + std::ostringstream oss; + oss << "Not enough data was available to read WSTR value (" << m_name << ")."; + throw parse_error(oss.str()); + } + + // Reverse each character from little endian to big endian + // if memory is supposed to be in big endian on this PC. for (int i = 0; i < length; i += 2) { if (endianness_check.buff[0] == 0x01) std::reverse(&data[i], &data[i + 2]); } + m_value = WSTR((char16_t *)data); delete[] data; }