#include "variant.hpp" #include #include "esmreader.hpp" #include "variantimp.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 struct GetValue { constexpr T operator()(int32_t value) const { return static_cast(value); } constexpr T operator()(float value) const { return static_cast(value); } template constexpr T operator()(const V&) const { if constexpr (orDefault) return T{}; else throw std::runtime_error("cannot convert variant"); } }; template struct SetValue { T mValue; explicit SetValue(T value) : mValue(value) { } void operator()(int32_t& value) const { value = static_cast(mValue); } void operator()(float& value) const { value = static_cast(mValue); } template void operator()(V&) const { throw std::runtime_error("cannot convert variant"); } }; } const std::string& Variant::getString() const { return std::get(mData); } int32_t Variant::getInteger() const { return std::visit(GetValue{}, mData); } float Variant::getFloat() const { return std::visit(GetValue{}, 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(mData); break; case VT_Int: stream << "variant int: " << std::get(mData); break; case VT_Long: stream << "variant long: " << std::get(mData); break; case VT_Float: stream << "variant float: " << std::get(mData); break; case VT_String: stream << "variant string: \"" << std::get(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{}, mData); break; case VT_Float: mData = std::visit(GetValue{}, mData); break; case VT_String: mData = std::string{}; break; } mType = type; } } void Variant::setString(const std::string& value) { std::get(mData) = value; } void Variant::setString(std::string&& value) { std::get(mData) = std::move(value); } void Variant::setInteger(int32_t 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; } }