/* * * _____ _ _ * / ___|| | | | * \ `--. | |_ _ __ __ _ | |_ ___ ___ * `--. \| __|| '__| / _` || __| / _ \ / __| * /\__/ /| |_ | | | (_| || |_ | (_) |\__ \ * \____/ \__||_| \__,_| \__| \___/ |___/ * * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Copyright (C) 2025 Armen Deroian * */ #ifndef TAG_H #define TAG_H #include <cstdint> #include <memory> namespace stratos::nbt { class NBTBuffer; enum class TagType : std::int8_t { End = 0, Byte = 1, Short = 2, Int = 3, Long = 4, Float = 5, Double = 6, ByteArray = 7, String = 8, List = 9, Compound = 10, IntArray = 11, LongArray = 12, Null = -1 }; bool isValidType(int type, bool allowEnd = false); namespace internal { template<class T, class... Args> std::unique_ptr<T> makeUnique(Args&&... args) { return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); } } // namespace stratos::nbt::internal class Tag { public: virtual ~Tag() = default; [[nodiscard]] virtual TagType getType() const = 0; [[nodiscard]] virtual std::unique_ptr<Tag> clone() const& = 0; virtual std::unique_ptr<Tag> moveClone() && = 0; std::unique_ptr<Tag> clone() &&; template<class T> T& as(); template<class T> const T& as() const; virtual Tag& assign(Tag&& rhs) = 0; virtual void read(NBTBuffer& buffer) = 0; virtual void write(NBTBuffer& buffer) const = 0; static std::unique_ptr<Tag> create(TagType type); friend bool operator==(const Tag& lhs, const Tag& rhs); friend bool operator!=(const Tag& lhs, const Tag& rhs); private: [[nodiscard]] virtual bool equals(const Tag& other) const = 0; }; template<class T> T& Tag::as() { static_assert(std::is_base_of_v<Tag, T>, "T must be a subclass of Tag"); return dynamic_cast<T&>(*this); } template<class T> const T& Tag::as() const { static_assert(std::is_base_of_v<Tag, T>, "T must be a subclass of Tag"); return dynamic_cast<const T&>(*this); } template <class Sub> class CRTPTag : public Tag { public: ~CRTPTag() noexcept override = default; // Ensure CRTPTag is abstract [[nodiscard]] TagType getType() const noexcept final { return Sub::type; } [[nodiscard]] std::unique_ptr<Tag> clone() const& final { return internal::makeUnique<Sub>(SubThis()); } std::unique_ptr<Tag> moveClone() && final { return internal::makeUnique<Sub>(std::move(SubThis())); } Tag& assign(Tag&& rhs) final { return SubThis() = dynamic_cast<Sub&&>(rhs); } private: [[nodiscard]] bool equals(const Tag& rhs) const final { return SubThis() == static_cast<const Sub&>(rhs); } Sub& SubThis() { return static_cast<Sub&>(*this); } const Sub& SubThis() const { return static_cast<const Sub&>(*this); } }; } // namespace stratos::nbt #endif //TAG_H