192 lines
5.2 KiB
C++
192 lines
5.2 KiB
C++
/*
|
|
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <ox/std/defines.hpp>
|
|
|
|
#include <ox/claw/claw.hpp>
|
|
#include <ox/fs/fs.hpp>
|
|
#include <ox/model/metadata.hpp>
|
|
|
|
|
|
#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<std::size_t> getPreloadAddr(Context &ctx, ox::FileAddress const&addr) noexcept;
|
|
ox::Result<std::size_t> 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::UUID, ox::Vector<ox::String>>;
|
|
|
|
ox::Result<ox::UUID> pathToUuid(Context &ctx, ox::StringViewCR path) noexcept;
|
|
|
|
ox::Result<ox::UUID> getUuid(Context &ctx, ox::FileAddress const&fileAddr) noexcept;
|
|
|
|
ox::Result<ox::UUID> getUuid(Context &ctx, ox::StringViewCR path) noexcept;
|
|
|
|
ox::Result<ox::CStringView> getPath(Context &ctx, ox::FileAddress const&fileAddr) noexcept;
|
|
|
|
ox::Result<ox::CStringView> getPath(Context &ctx, ox::CStringViewCR fileId) noexcept;
|
|
|
|
ox::Error updatePath(Context &ctx, ox::StringViewCR oldPath, ox::StringViewCR newPath) noexcept;
|
|
|
|
constexpr ox::Result<ox::UUID> uuidUrlToUuid(ox::StringViewCR uuidUrl) noexcept {
|
|
return ox::UUID::fromString(substr(uuidUrl, 7));
|
|
}
|
|
|
|
ox::Result<ox::CStringView> uuidUrlToPath(Context &ctx, ox::StringViewCR uuid) noexcept;
|
|
|
|
ox::Result<ox::CStringView> uuidToPath(Context &ctx, ox::StringViewCR uuid) noexcept;
|
|
|
|
ox::Result<ox::CStringView> uuidToPath(Context &ctx, ox::UUID const&uuid) noexcept;
|
|
|
|
#ifndef OX_BARE_METAL
|
|
|
|
namespace detail {
|
|
template<typename T>
|
|
constexpr auto makeLoader(Context &ctx) {
|
|
return [&ctx](ox::StringView assetId) -> ox::Result<T> {
|
|
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<T>(buff);
|
|
if (err) {
|
|
if (err != ox::Error_ClawTypeVersionMismatch && err != ox::Error_ClawTypeMismatch) {
|
|
return err;
|
|
}
|
|
OX_RETURN_ERROR(convert<T>(ctx, buff, obj));
|
|
}
|
|
return std::move(obj);
|
|
};
|
|
};
|
|
}
|
|
|
|
template<typename T>
|
|
ox::Result<AssetRef<T>> readObjFile(
|
|
Context &ctx,
|
|
ox::StringViewCR assetId,
|
|
bool const forceLoad) noexcept {
|
|
static constexpr auto load = [](
|
|
keel::Context &ctx,
|
|
ox::StringViewCR assetId,
|
|
bool forceLoad) -> ox::Result<keel::AssetRef<T>> {
|
|
if (forceLoad) {
|
|
ctx.assetManager.initTypeManager<T>(detail::makeLoader<T>, ctx);
|
|
return ctx.assetManager.loadAsset<T>(assetId);
|
|
} else {
|
|
auto [cached, err] = ctx.assetManager.getAsset<T>(assetId);
|
|
if (err) {
|
|
ctx.assetManager.initTypeManager<T>(detail::makeLoader<T>, ctx);
|
|
OX_RETURN_ERROR(ctx.assetManager.loadAsset<T>(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<typename T>
|
|
ox::Result<keel::AssetRef<T>> readObjNoCache(
|
|
keel::Context &ctx,
|
|
ox::StringViewCR assetId) noexcept {
|
|
if constexpr(ox::preloadable<T>::value) {
|
|
OX_REQUIRE(addr, getPreloadAddr(ctx, assetId));
|
|
return keel::AssetRef<T>(std::bit_cast<T const*>(uintptr_t{addr}));
|
|
} else {
|
|
return ox::Error(1);
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
ox::Error reloadAsset(Context &ctx, ox::StringViewCR assetId) noexcept;
|
|
|
|
template<typename T>
|
|
ox::Result<AssetRef<T>> readObj(
|
|
Context &ctx,
|
|
ox::StringViewCR assetId,
|
|
[[maybe_unused]] bool forceLoad = false) noexcept {
|
|
#ifndef OX_BARE_METAL
|
|
return readObjFile<T>(ctx, assetId, forceLoad);
|
|
#else
|
|
return readObjNoCache<T>(ctx, assetId);
|
|
#endif
|
|
}
|
|
|
|
template<typename T>
|
|
ox::Result<AssetRef<T>> readObj(
|
|
Context &ctx,
|
|
ox::FileAddress const&file,
|
|
[[maybe_unused]] bool forceLoad = false) noexcept {
|
|
#ifndef OX_BARE_METAL
|
|
OX_REQUIRE(assetId, file.getPath());
|
|
return readObj<T>(ctx, ox::StringView(assetId), forceLoad);
|
|
#else
|
|
if constexpr(ox::preloadable<T>::value) {
|
|
OX_REQUIRE(addr, getPreloadAddr(ctx, file));
|
|
return keel::AssetRef<T>(std::bit_cast<T const*>(uintptr_t{addr}));
|
|
} else {
|
|
return ox::Error(1);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
template<typename T>
|
|
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<ox::FileSystem> &&fs, DuplicateSet &duplicateSet) noexcept;
|
|
|
|
ox::Error setRomFs(Context &ctx, ox::UPtr<ox::FileSystem> &&fs) noexcept;
|
|
|
|
ox::Result<ox::UPtr<ox::FileSystem>> loadRomFs(ox::StringViewCR path) noexcept;
|
|
|
|
ox::Result<char*> loadRom(ox::StringViewCR path = "") noexcept;
|
|
|
|
void unloadRom(char*) noexcept;
|
|
|
|
}
|