#pragma once #include #include #include #include #include #include "constants.hpp" #include "data/dv.hpp" #include "typedefs.hpp" #include "Content.hpp" #include "data/StructLayout.hpp" #include "files/world_regions_fwd.hpp" namespace fs = std::filesystem; enum class ContentIssueType { REORDER, MISSING, REGION_FORMAT_UPDATE, BLOCK_DATA_LAYOUTS_UPDATE, }; struct ContentIssue { ContentIssueType issueType; union { ContentType contentType; RegionLayerIndex regionLayer; }; }; struct ContentEntry { ContentType type; std::string name; }; class WorldFiles; /// @brief Content unit lookup table /// @tparam T index type /// @tparam U unit class template class ContentUnitLUT { std::vector indices; std::vector names; bool missingContent = false; bool reorderContent = false; /// @brief index that will be used to mark missing unit T missingValue; ContentType type; public: ContentUnitLUT( size_t count, const ContentUnitIndices& unitIndices, T missingValue, ContentType type ) : missingValue(missingValue), type(type) { for (size_t i = 0; i < count; i++) { indices.push_back(i); } for (auto unit : unitIndices.getIterable()) { names.push_back(unit->name); } for (size_t i = unitIndices.count(); i < count; i++) { names.emplace_back(""); } } void setup(const dv::value& list, const ContentUnitDefs& defs) { if (list != nullptr) { for (size_t i = 0; i < list.size(); i++) { const std::string& name = list[i].asString(); if (auto def = defs.find(name)) { set(i, name, def->rt.id); } else { set(i, name, missingValue); } } } } void getMissingContent(std::vector& entries) const { for (size_t i = 0; i < count(); i++) { if (indices[i] == missingValue) { auto& name = names[i]; entries.push_back(ContentEntry {type, name}); } } } inline const std::string& getName(T index) const { return names[index]; } inline T getId(T index) const { return indices[index]; } inline void set(T index, std::string name, T id) { indices[index] = id; names[index] = std::move(name); if (id == missingValue) { missingContent = true; } else if (index != id) { reorderContent = true; } } inline ContentType getContentType() const { return type; } inline size_t count() const { return indices.size(); } inline bool hasContentReorder() const { return reorderContent; } inline bool hasMissingContent() const { return missingContent; } }; /// @brief Content incapatibility report used to convert world. /// Building with indices.json class ContentReport { public: ContentUnitLUT blocks; ContentUnitLUT items; uint regionsVersion; std::unordered_map blocksDataLayouts; std::vector issues; std::vector dataLoss; bool dataLayoutsUpdated = false; ContentReport( const ContentIndices* indices, size_t blocks, size_t items, uint regionsVersion ); static std::shared_ptr create( const std::shared_ptr& worldFiles, const fs::path& filename, const Content* content ); inline const std::vector& getDataLoss() const { return dataLoss; } inline bool hasUpdatedLayouts() { return dataLayoutsUpdated; } inline bool hasContentReorder() const { return blocks.hasContentReorder() || items.hasContentReorder(); } inline bool hasMissingContent() const { return blocks.hasMissingContent() || items.hasMissingContent(); } inline bool isUpgradeRequired() const { return regionsVersion < REGION_FORMAT_VERSION; } inline bool hasDataLoss() const { return !dataLoss.empty(); } void buildIssues(); const std::vector& getIssues() const; std::vector getMissingContent() const; };