#pragma once #include "compat/assert.hpp" #include "compat/exception.hpp" #include "compat/os-file.hpp" #include "atlas-loader.hpp" #include "atlas-loader-storage.hpp" #include "loader/loader.hpp" #include #include #include namespace floormat::loader_detail { template atlas_loader::atlas_loader(TRAITS&& traits): // NOLINT(*-rvalue-reference-param-not-moved) t{Utility::move(traits)} {} template atlas_loader::atlas_loader() requires std::is_default_constructible_v: atlas_loader{TRAITS{}} {} template atlas_loader(TRAITS&& traits) noexcept -> atlas_loader; template auto atlas_loader::ensure_atlas_list() -> ArrayView { if (!s.cell_array.empty()) [[likely]] return { s.cell_array.data(), s.cell_array.size() }; t.ensure_atlases_loaded(s); for (Cell& c : s.cell_array) if (String& name{t.name_of(c)}; name.isSmall()) name = String{AllocatedInit, name}; fm_assert(!s.cell_array.empty()); return { s.cell_array.data(), s.cell_array.size() }; } template const std::shared_ptr& atlas_loader::get_atlas(StringView name, loader_policy p) { ensure_atlas_list(); const std::shared_ptr& invalid_atlas = t.atlas_of(t.make_invalid_atlas(s)); fm_debug_assert(invalid_atlas); switch (p) { using enum loader_policy; case error: case ignore: case warn: break; default: fm_abort("invalid loader_policy (%d)", (int)p); } if (name == loader.INVALID) [[unlikely]] switch (p) { using enum loader_policy; case error: goto error; case ignore: case warn: return invalid_atlas; } fm_soft_assert(loader.check_atlas_name(name)); if (auto it = s.name_map.find(name); it != s.name_map.end()) [[likely]] { if (it->second == -1uz) [[unlikely]] { switch (p) { using enum loader_policy; case error: goto error; case warn: case ignore: return invalid_atlas; } } else { Cell& c = s.cell_array[it->second]; fm_assert(t.name_of(c)); std::shared_ptr& atlas = t.atlas_of(c); if (!atlas) [[unlikely]] { atlas = make_atlas(name, c); fm_debug_assert(atlas); return atlas; } else { fm_assert(t.name_of(c) == name); return atlas; } } } else if (Optional c_{t.make_cell(name)}) // todo! { size_t index = s.cell_array.size(); s.cell_array.emplace_back(Utility::move(*c_)); Cell& c = s.cell_array.back(); fm_debug_assert(!t.name_of(c)); fm_debug_assert(!t.atlas_of(c)); t.atlas_of(c) = make_atlas(name, c); t.name_of(c) = String{AllocatedInit, name}; fm_debug_assert(t.name_of(c)); fm_debug_assert(t.atlas_of(c)); s.name_map[t.name_of(c)] = index; return t.atlas_of(c); } else { switch (p) { using enum loader_policy; case error: goto error; case warn: goto missing_warn; case ignore: return invalid_atlas; } } std::unreachable(); fm_assert(false); error: fm_throw("no such atlas '{}'"_cf, name); missing_warn: s.missing_atlas_names.push_back(String { AllocatedInit, name }); s.name_map[ s.missing_atlas_names.back() ] = -1uz; if (name != loader.INVALID) DBG_nospace << t.loader_name() << " '" << name << "' doesn't exist"; return invalid_atlas; } template auto atlas_loader::make_atlas(StringView name, const Cell& c) -> std::shared_ptr { fm_assert(name != ""_s); fm_soft_assert(!c.name || t.name_of(c) == name); fm_soft_assert(loader.check_atlas_name(name)); return t.make_atlas(name, c); } template auto atlas_loader::get_invalid_atlas() -> const Cell& { const auto& cell = t.make_invalid_atlas(s); fm_assert(t.atlas_of(cell)); return cell; } } // namespace floormat::loader_detail