You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
openmw/components/esm3/variant.cpp

278 lines
6.0 KiB
C++

#include "variant.hpp"
#include <cassert>
#include <stdexcept>
#include "esmreader.hpp"
#include "variantimp.hpp"
#include "components/esm/defs.hpp"
namespace ESM
{
namespace
{
constexpr uint32_t STRV = fourCC("STRV");
constexpr uint32_t INTV = fourCC("INTV");
constexpr uint32_t FLTV = fourCC("FLTV");
constexpr uint32_t STTV = fourCC("STTV");
template <typename T, bool orDefault = false>
struct GetValue
{
constexpr T operator()(int value) const { return static_cast<T>(value); }
constexpr T operator()(float value) const { return static_cast<T>(value); }
template <typename V>
constexpr T operator()(const V&) const
{
if constexpr (orDefault)
return T {};
else
throw std::runtime_error("cannot convert variant");
}
};
template <typename T>
struct SetValue
{
T mValue;
explicit SetValue(T value) : mValue(value) {}
void operator()(int& value) const { value = static_cast<int>(mValue); }
void operator()(float& value) const { value = static_cast<float>(mValue); }
template <typename V>
void operator()(V&) const { throw std::runtime_error("cannot convert variant"); }
};
}
const std::string& Variant::getString() const
{
return std::get<std::string>(mData);
}
int Variant::getInteger() const
{
return std::visit(GetValue<int>{}, mData);
}
float Variant::getFloat() const
{
return std::visit(GetValue<float>{}, mData);
}
void Variant::read (ESMReader& esm, Format format)
{
// type
VarType type = VT_Unknown;
if (format==Format_Global)
{
std::string typeId = esm.getHNString ("FNAM");
if (typeId == "s")
type = VT_Short;
else if (typeId == "l")
type = VT_Long;
else if (typeId == "f")
type = VT_Float;
else
esm.fail ("illegal global variable type " + typeId);
}
else if (format==Format_Gmst)
{
if (!esm.hasMoreSubs())
{
type = VT_None;
}
else
{
esm.getSubName();
NAME name = esm.retSubName();
if (name==STRV)
{
type = VT_String;
}
else if (name==INTV)
{
type = VT_Int;
}
else if (name==FLTV)
{
type = VT_Float;
}
else
esm.fail ("invalid subrecord: " + name.toString());
}
}
else if (format == Format_Info)
{
esm.getSubName();
NAME name = esm.retSubName();
if (name==INTV)
{
type = VT_Int;
}
else if (name==FLTV)
{
type = VT_Float;
}
else
esm.fail ("invalid subrecord: " + name.toString());
}
else if (format == Format_Local)
{
esm.getSubName();
NAME name = esm.retSubName();
if (name==INTV)
{
type = VT_Int;
}
else if (name==FLTV)
{
type = VT_Float;
}
else if (name==STTV)
{
type = VT_Short;
}
else
esm.fail ("invalid subrecord: " + name.toString());
}
setType (type);
std::visit(ReadESMVariantValue {esm, format, mType}, mData);
}
void Variant::write (ESMWriter& esm, Format format) const
{
if (mType==VT_Unknown)
{
throw std::runtime_error ("can not serialise variant of unknown type");
}
else if (mType==VT_None)
{
if (format==Format_Global)
throw std::runtime_error ("can not serialise variant of type none to global format");
if (format==Format_Info)
throw std::runtime_error ("can not serialise variant of type none to info format");
if (format==Format_Local)
throw std::runtime_error ("can not serialise variant of type none to local format");
// nothing to do here for GMST format
}
else
std::visit(WriteESMVariantValue {esm, format, mType}, mData);
}
void Variant::write (std::ostream& stream) const
{
switch (mType)
{
case VT_Unknown:
stream << "variant unknown";
break;
case VT_None:
stream << "variant none";
break;
case VT_Short:
stream << "variant short: " << std::get<int>(mData);
break;
case VT_Int:
stream << "variant int: " << std::get<int>(mData);
break;
case VT_Long:
stream << "variant long: " << std::get<int>(mData);
break;
case VT_Float:
stream << "variant float: " << std::get<float>(mData);
break;
case VT_String:
stream << "variant string: \"" << std::get<std::string>(mData) << "\"";
break;
}
}
void Variant::setType (VarType type)
{
if (type!=mType)
{
switch (type)
{
case VT_Unknown:
case VT_None:
mData = std::monostate {};
break;
case VT_Short:
case VT_Int:
case VT_Long:
mData = std::visit(GetValue<int, true>{}, mData);
break;
case VT_Float:
mData = std::visit(GetValue<float, true>{}, mData);
break;
case VT_String:
mData = std::string {};
break;
}
mType = type;
}
}
void Variant::setString (const std::string& value)
{
std::get<std::string>(mData) = value;
}
void Variant::setString (std::string&& value)
{
std::get<std::string>(mData) = std::move(value);
}
void Variant::setInteger (int value)
{
std::visit(SetValue(value), mData);
}
void Variant::setFloat (float value)
{
std::visit(SetValue(value), mData);
}
std::ostream& operator<< (std::ostream& stream, const Variant& value)
{
value.write (stream);
return stream;
}
}