dml: Add more robust error reporting

This commit is contained in:
Joshua Scott 2018-04-01 16:23:36 +01:00
parent bba2aae43b
commit 9034c893a7
15 changed files with 172 additions and 31 deletions

View File

@ -1,7 +1,9 @@
#pragma once #pragma once
#include "FieldBase.h" #include "FieldBase.h"
#include "types.h" #include "types.h"
#include "exception.h"
#include <sstream> #include <sstream>
#include <stdexcept>
namespace ki namespace ki
{ {
@ -69,7 +71,7 @@ namespace dml
* Example: <FieldName TYPE="STR">Value</FieldName> * Example: <FieldName TYPE="STR">Value</FieldName>
* *
* If the field in the XML data does not have the same type * 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 void from_xml(const rapidxml::xml_node<> *node) final
{ {
@ -82,12 +84,15 @@ namespace dml
attr; attr = attr->next_attribute()) attr; attr = attr->next_attribute())
{ {
const std::string name = attr->name(); const std::string name = attr->name();
if (name != "TYPE") if (name == "TYPE")
{ {
const std::string value = attr->value(); const std::string value = attr->value();
if (value != get_type_name()) 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") else if (name == "NOXFER")
@ -97,7 +102,9 @@ namespace dml
} }
else 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 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());
} }
} }

View File

@ -0,0 +1,26 @@
#pragma once
#include <stdexcept>
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) { }
};
}
}

View File

@ -33,8 +33,9 @@ namespace dml
auto *type_attr = node->first_attribute("TYPE"); auto *type_attr = node->first_attribute("TYPE");
if (!type_attr) if (!type_attr)
{ {
// TODO: Exceptions std::ostringstream oss;
return nullptr; oss << "XML Field node is missing required TYPE attribute (" << node->name() << ").";
throw value_error(oss.str());
} }
const std::string type = type_attr->value(); const std::string type = type_attr->value();
@ -63,8 +64,9 @@ namespace dml
field = new GidField("", record); field = new GidField("", record);
else else
{ {
// TODO: Exceptions std::ostringstream oss;
return nullptr; oss << "Unknown DML type \"" << type << "\" in XML Field node: " << node->name() << ".";
throw value_error(oss.str());
} }
field->from_xml(node); field->from_xml(node);

View File

@ -94,8 +94,9 @@ namespace dml
const std::string node_name = node->name(); const std::string node_name = node->name();
if (node_name != "RECORD") if (node_name != "RECORD")
{ {
// TODO: Exceptions std::ostringstream oss;
return; oss << "Expected <RECORD> node but got <" << node->name() << ">.";
throw value_error(oss.str());
} }
// Every child node inside a <RECORD> element is a Field. // Every child node inside a <RECORD> element is a Field.

View File

@ -18,6 +18,12 @@ namespace dml
{ {
ValueBytes<BYT> data; ValueBytes<BYT> data;
istream.read(data.buff, sizeof(BYT)); 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; m_value = data.value;
} }

View File

@ -9,24 +9,34 @@ namespace dml
template <> template <>
void DblField::write_to(std::ostream &ostream) const void DblField::write_to(std::ostream &ostream) const
{ {
ValueBytes<USHRT> endianness_check;
endianness_check.value = 0x0102;
ValueBytes<DBL> data; ValueBytes<DBL> data;
data.value = m_value; data.value = m_value;
ValueBytes<USHRT> endianness_check;
endianness_check.value = 0x0102;
if (endianness_check.buff[0] == 0x01) if (endianness_check.buff[0] == 0x01)
std::reverse(&data.buff[0], &data.buff[8]); std::reverse(&data.buff[0], &data.buff[8]);
ostream.write(data.buff, sizeof(DBL)); ostream.write(data.buff, sizeof(DBL));
} }
template <> template <>
void DblField::read_from(std::istream &istream) void DblField::read_from(std::istream &istream)
{ {
ValueBytes<USHRT> endianness_check;
endianness_check.value = 0x0102;
ValueBytes<DBL> data; ValueBytes<DBL> data;
istream.read(data.buff, sizeof(DBL)); 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<USHRT> endianness_check;
endianness_check.value = 0x0102;
if (endianness_check.buff[0] == 0x01) if (endianness_check.buff[0] == 0x01)
std::reverse(&data.buff[0], &data.buff[8]); std::reverse(&data.buff[0], &data.buff[8]);
m_value = data.value; m_value = data.value;
} }

View File

@ -9,22 +9,31 @@ namespace dml
template <> template <>
void FltField::write_to(std::ostream &ostream) const void FltField::write_to(std::ostream &ostream) const
{ {
ValueBytes<USHRT> endianness_check;
endianness_check.value = 0x0102;
ValueBytes<FLT> data; ValueBytes<FLT> data;
data.value = m_value; data.value = m_value;
ValueBytes<USHRT> endianness_check;
endianness_check.value = 0x0102;
if (endianness_check.buff[0] == 0x01) if (endianness_check.buff[0] == 0x01)
std::reverse(&data.buff[0], &data.buff[4]); std::reverse(&data.buff[0], &data.buff[4]);
ostream.write(data.buff, sizeof(FLT)); ostream.write(data.buff, sizeof(FLT));
} }
template <> template <>
void FltField::read_from(std::istream &istream) void FltField::read_from(std::istream &istream)
{ {
ValueBytes<USHRT> endianness_check;
endianness_check.value = 0x0102;
ValueBytes<FLT> data; ValueBytes<FLT> data;
istream.read(data.buff, sizeof(FLT)); 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<USHRT> endianness_check;
endianness_check.value = 0x0102;
if (endianness_check.buff[0] == 0x01) if (endianness_check.buff[0] == 0x01)
std::reverse(&data.buff[0], &data.buff[4]); std::reverse(&data.buff[0], &data.buff[4]);
m_value = data.value; m_value = data.value;

View File

@ -19,12 +19,20 @@ namespace dml
template <> template <>
void GidField::read_from(std::istream &istream) void GidField::read_from(std::istream &istream)
{ {
ValueBytes<USHRT> endianness_check;
endianness_check.value = 0x0102;
ValueBytes<GID> data; ValueBytes<GID> data;
istream.read(data.buff, sizeof(GID)); 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<USHRT> endianness_check;
endianness_check.value = 0x0102;
if (endianness_check.buff[0] == 0x01) if (endianness_check.buff[0] == 0x01)
std::reverse(&data.buff[0], &data.buff[8]); std::reverse(&data.buff[0], &data.buff[8]);
m_value = data.value; m_value = data.value;
} }

View File

@ -19,12 +19,20 @@ namespace dml
template <> template <>
void IntField::read_from(std::istream &istream) void IntField::read_from(std::istream &istream)
{ {
ValueBytes<USHRT> endianness_check;
endianness_check.value = 0x0102;
ValueBytes<INT> data; ValueBytes<INT> data;
istream.read(data.buff, sizeof(INT)); 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<USHRT> endianness_check;
endianness_check.value = 0x0102;
if (endianness_check.buff[0] == 0x01) if (endianness_check.buff[0] == 0x01)
std::reverse(&data.buff[0], &data.buff[4]); std::reverse(&data.buff[0], &data.buff[4]);
m_value = data.value; m_value = data.value;
} }

View File

@ -19,12 +19,20 @@ namespace dml
template <> template <>
void ShrtField::read_from(std::istream &istream) void ShrtField::read_from(std::istream &istream)
{ {
ValueBytes<SHRT> endianness_check;
endianness_check.value = 0x0102;
ValueBytes<SHRT> data; ValueBytes<SHRT> data;
istream.read(data.buff, sizeof(SHRT)); 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<SHRT> endianness_check;
endianness_check.value = 0x0102;
if (endianness_check.buff[0] == 0x01) if (endianness_check.buff[0] == 0x01)
std::reverse(&data.buff[0], &data.buff[2]); std::reverse(&data.buff[0], &data.buff[2]);
m_value = data.value; m_value = data.value;
} }

View File

@ -21,16 +21,30 @@ namespace dml
void StrField::read_from(std::istream &istream) void StrField::read_from(std::istream &istream)
{ {
// Get the length // Get the length
ValueBytes<USHRT> endianness_check;
endianness_check.value = 0x0102;
ValueBytes<USHRT> length_data; ValueBytes<USHRT> length_data;
istream.read(length_data.buff, sizeof(USHRT)); 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<USHRT> endianness_check;
endianness_check.value = 0x0102;
if (endianness_check.buff[0] == 0x01) if (endianness_check.buff[0] == 0x01)
std::reverse(&length_data.buff[0], &length_data.buff[2]); std::reverse(&length_data.buff[0], &length_data.buff[2]);
// Read the data into a buffer // Read the data into a buffer
char *data = new char[length_data.value + 1] { 0 }; char *data = new char[length_data.value + 1] { 0 };
istream.read(data, length_data.value); 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); m_value = STR(data);
delete[] data; delete[] data;
} }

View File

@ -18,6 +18,12 @@ namespace dml
{ {
ValueBytes<UBYT> data; ValueBytes<UBYT> data;
istream.read(data.buff, sizeof(UBYT)); 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; m_value = data.value;
} }

View File

@ -19,12 +19,20 @@ namespace dml
template <> template <>
void UIntField::read_from(std::istream &istream) void UIntField::read_from(std::istream &istream)
{ {
ValueBytes<USHRT> endianness_check;
endianness_check.value = 0x0102;
ValueBytes<UINT> data; ValueBytes<UINT> data;
istream.read(data.buff, sizeof(UINT)); 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<USHRT> endianness_check;
endianness_check.value = 0x0102;
if (endianness_check.buff[0] == 0x01) if (endianness_check.buff[0] == 0x01)
std::reverse(&data.buff[0], &data.buff[4]); std::reverse(&data.buff[0], &data.buff[4]);
m_value = data.value; m_value = data.value;
} }

View File

@ -23,6 +23,13 @@ namespace dml
endianness_check.value = 0x0102; endianness_check.value = 0x0102;
ValueBytes<USHRT> data; ValueBytes<USHRT> data;
istream.read(data.buff, sizeof(USHRT)); 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) if (endianness_check.buff[0] == 0x01)
std::reverse(&data.buff[0], &data.buff[2]); std::reverse(&data.buff[0], &data.buff[2]);
m_value = data.value; m_value = data.value;

View File

@ -23,10 +23,17 @@ namespace dml
void WStrField::read_from(std::istream &istream) void WStrField::read_from(std::istream &istream)
{ {
// Get the length // Get the length
ValueBytes<USHRT> endianness_check;
endianness_check.value = 0x0102;
ValueBytes<USHRT> length_data; ValueBytes<USHRT> length_data;
istream.read(length_data.buff, sizeof(USHRT)); 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<USHRT> endianness_check;
endianness_check.value = 0x0102;
if (endianness_check.buff[0] == 0x01) if (endianness_check.buff[0] == 0x01)
std::reverse(&length_data.buff[0], &length_data.buff[2]); 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); size_t length = length_data.value * sizeof(char16_t);
char *data = new char[length + sizeof(char16_t)]{ 0 }; char *data = new char[length + sizeof(char16_t)]{ 0 };
istream.read(data, length); 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) for (int i = 0; i < length; i += 2)
{ {
if (endianness_check.buff[0] == 0x01) if (endianness_check.buff[0] == 0x01)
std::reverse(&data[i], &data[i + 2]); std::reverse(&data[i], &data[i + 2]);
} }
m_value = WSTR((char16_t *)data); m_value = WSTR((char16_t *)data);
delete[] data; delete[] data;
} }