/* * Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved. */ #pragma once #include #include #include #include #include "asset.hpp" #include "context.hpp" #include "typeconv.hpp" namespace keel { // Pointer to preloaded data that can be stored in FS in place of the actual // data. struct PreloadPtr { static constexpr auto TypeName = "net.drinkingtea.keel.PreloadPtr"; static constexpr auto TypeVersion = 2; uint64_t preloadAddr = 0; }; OX_MODEL_BEGIN(PreloadPtr) OX_MODEL_FIELD(preloadAddr) OX_MODEL_END() ox::Result getPreloadAddr(Context &ctx, ox::FileAddress const&addr) noexcept; ox::Result getPreloadAddr(Context &ctx, ox::StringViewCR path) noexcept; void createUuidMapping(Context &ctx, ox::StringViewCR filePath, ox::UUID const&uuid) noexcept; // map of UUIDs to paths using DuplicateSet = ox::HashMap>; ox::Result pathToUuid(Context &ctx, ox::StringViewCR path) noexcept; ox::Result getUuid(Context &ctx, ox::FileAddress const&fileAddr) noexcept; ox::Result getUuid(Context &ctx, ox::StringViewCR path) noexcept; ox::Result getPath(Context &ctx, ox::FileAddress const&fileAddr) noexcept; ox::Result getPath(Context &ctx, ox::CStringViewCR fileId) noexcept; ox::Error updatePath(Context &ctx, ox::StringViewCR oldPath, ox::StringViewCR newPath) noexcept; constexpr ox::Result uuidUrlToUuid(ox::StringViewCR uuidUrl) noexcept { return ox::UUID::fromString(substr(uuidUrl, 7)); } ox::Result uuidUrlToPath(Context &ctx, ox::StringViewCR uuid) noexcept; ox::Result uuidToPath(Context &ctx, ox::StringViewCR uuid) noexcept; ox::Result uuidToPath(Context &ctx, ox::UUID const&uuid) noexcept; #ifndef OX_BARE_METAL namespace detail { template constexpr auto makeLoader(Context &ctx) { return [&ctx](ox::StringView assetId) -> ox::Result { if (!beginsWith(assetId, "/")) { auto const p = ctx.uuidToPath.at(assetId); if (p.error) { oxErrf("Could not find asset: {}\n", assetId); return ox::Error{1, "Asset ID not found"}; } assetId = *p.value; } OX_REQUIRE(buff, ctx.rom->read(assetId)); auto [obj, err] = readAsset(buff); if (err) { if (err != ox::Error_ClawTypeVersionMismatch && err != ox::Error_ClawTypeMismatch) { return err; } OX_RETURN_ERROR(convert(ctx, buff, obj)); } return std::move(obj); }; }; } template ox::Result> readObjFile( Context &ctx, ox::StringViewCR assetId, bool const forceLoad) noexcept { static constexpr auto load = []( keel::Context &ctx, ox::StringViewCR assetId, bool forceLoad) -> ox::Result> { if (forceLoad) { ctx.assetManager.initTypeManager(detail::makeLoader, ctx); return ctx.assetManager.loadAsset(assetId); } else { auto [cached, err] = ctx.assetManager.getAsset(assetId); if (err) { ctx.assetManager.initTypeManager(detail::makeLoader, ctx); OX_RETURN_ERROR(ctx.assetManager.loadAsset(assetId).moveTo(cached)); } return cached; } }; if (beginsWith(assetId, "uuid://")) { return load(ctx, substr(assetId, 7), forceLoad); } else { auto const [uuid, uuidErr] = getUuid(ctx, assetId); if (!uuidErr) { return load(ctx, uuid.toString(), forceLoad); } else { return load(ctx, assetId, forceLoad); } } } #else template ox::Result> readObjNoCache( keel::Context &ctx, ox::StringViewCR assetId) noexcept { if constexpr(ox::preloadable::value) { OX_REQUIRE(addr, getPreloadAddr(ctx, assetId)); return keel::AssetRef(std::bit_cast(uintptr_t{addr})); } else { return ox::Error(1); } } #endif ox::Error reloadAsset(Context &ctx, ox::StringViewCR assetId) noexcept; template ox::Result> readObj( Context &ctx, ox::StringViewCR assetId, [[maybe_unused]] bool forceLoad = false) noexcept { #ifndef OX_BARE_METAL return readObjFile(ctx, assetId, forceLoad); #else return readObjNoCache(ctx, assetId); #endif } template ox::Result> readObj( Context &ctx, ox::FileAddress const&file, [[maybe_unused]] bool forceLoad = false) noexcept { #ifndef OX_BARE_METAL OX_REQUIRE(assetId, file.getPath()); return readObj(ctx, ox::StringView(assetId), forceLoad); #else if constexpr(ox::preloadable::value) { OX_REQUIRE(addr, getPreloadAddr(ctx, file)); return keel::AssetRef(std::bit_cast(uintptr_t{addr})); } else { return ox::Error(1); } #endif } template ox::Error writeObj( Context &ctx, ox::FileAddress const&file, T const&obj, ox::ClawFormat fmt = ox::ClawFormat::Metal) noexcept { OX_REQUIRE(objBuff, ox::writeClaw(obj, fmt)); return ctx.rom->write(file, objBuff.data(), objBuff.size()); } ox::Error setRomFs(Context &ctx, ox::UPtr &&fs, DuplicateSet &duplicateSet) noexcept; ox::Error setRomFs(Context &ctx, ox::UPtr &&fs) noexcept; ox::Result> loadRomFs(ox::StringViewCR path) noexcept; ox::Result loadRom(ox::StringViewCR path = "") noexcept; void unloadRom(char*) noexcept; }