diff options
Diffstat (limited to 'source/base/StarAssets.cpp')
-rw-r--r-- | source/base/StarAssets.cpp | 161 |
1 files changed, 130 insertions, 31 deletions
diff --git a/source/base/StarAssets.cpp b/source/base/StarAssets.cpp index de7bc51..2515b5e 100644 --- a/source/base/StarAssets.cpp +++ b/source/base/StarAssets.cpp @@ -17,33 +17,35 @@ #include "StarLexicalCast.hpp" #include "StarSha256.hpp" #include "StarDataStreamDevices.hpp" +#include "StarLua.hpp" +#include "StarUtilityLuaBindings.hpp" namespace Star { -static void validatePath(AssetPath const& components, bool canContainSubPath, bool canContainDirectives) { - if (components.basePath.empty() || components.basePath[0] != '/') - throw AssetException(strf("Path '{}' must be absolute", components.basePath)); +static void validateBasePath(std::string_view const& basePath) { + if (basePath.empty() || basePath[0] != '/') + throw AssetException(strf("Path '{}' must be absolute", basePath)); bool first = true; bool slashed = true; bool dotted = false; - for (auto c : components.basePath) { + for (auto c : basePath) { if (c == '/') { if (!first) { if (slashed) - throw AssetException(strf("Path '{}' contains consecutive //, not allowed", components.basePath)); + throw AssetException(strf("Path '{}' contains consecutive //, not allowed", basePath)); else if (dotted) - throw AssetException(strf("Path '{}' '.' and '..' not allowed", components.basePath)); + throw AssetException(strf("Path '{}' '.' and '..' not allowed", basePath)); } slashed = true; dotted = false; } else if (c == ':') { if (slashed) - throw AssetException(strf("Path '{}' has ':' after directory", components.basePath)); + throw AssetException(strf("Path '{}' has ':' after directory", basePath)); break; } else if (c == '?') { if (slashed) - throw AssetException(strf("Path '{}' has '?' after directory", components.basePath)); + throw AssetException(strf("Path '{}' has '?' after directory", basePath)); break; } else { slashed = false; @@ -52,7 +54,11 @@ static void validatePath(AssetPath const& components, bool canContainSubPath, bo first = false; } if (slashed) - throw AssetException(strf("Path '{}' cannot be a file", components.basePath)); + throw AssetException(strf("Path '{}' cannot be a file", basePath)); +} + +static void validatePath(AssetPath const& components, bool canContainSubPath, bool canContainDirectives) { + validateBasePath(components.basePath.utf8()); if (!canContainSubPath && components.subPath) throw AssetException::format("Path '{}' cannot contain sub-path", components); @@ -60,6 +66,32 @@ static void validatePath(AssetPath const& components, bool canContainSubPath, bo throw AssetException::format("Path '{}' cannot contain directives", components); } +static void validatePath(StringView const& path, bool canContainSubPath, bool canContainDirectives) { + std::string_view const& str = path.utf8(); + + size_t end = str.find_first_of(":?"); + auto basePath = str.substr(0, end); + validateBasePath(basePath); + + bool subPath = false; + if (str[end] == ':') { + size_t beg = end + 1; + if (beg != str.size()) { + end = str.find_first_of('?', beg); + if (end == NPos && beg + 1 != str.size()) + subPath = true; + else if (size_t len = end - beg) + subPath = true; + } + } + + if (subPath) + throw AssetException::format("Path '{}' cannot contain sub-path", path); + + if (end != NPos && str[end] == '?' && !canContainDirectives) + throw AssetException::format("Path '{}' cannot contain directives", path); +} + Maybe<RectU> FramesSpecification::getRect(String const& frame) const { if (auto alias = aliases.ptr(frame)) { return frames.get(*alias); @@ -75,6 +107,22 @@ Assets::Assets(Settings settings, StringList assetSources) { m_stopThreads = false; m_assetSources = std::move(assetSources); + auto luaEngine = LuaEngine::create(); + auto decorateLuaContext = [this](LuaContext& context) { + context.setCallbacks("sb", LuaBindings::makeUtilityCallbacks()); + LuaCallbacks callbacks; + callbacks.registerCallbackWithSignature<StringSet, String>("byExtension", bind(&Assets::scanExtension, this, _1)); + callbacks.registerCallbackWithSignature<Json, String>("json", bind(&Assets::json, this, _1)); + callbacks.registerCallback("bytes", [this](String const& path) -> String { + auto assetBytes = bytes(path); + return String(assetBytes->ptr(), assetBytes->size()); + }); + callbacks.registerCallback("scan", [this](Maybe<String> const& a, Maybe<String> const& b) -> StringList { + return b ? scan(a.value(), *b) : scan(a.value()); + }); + context.setCallbacks("assets", callbacks); + }; + for (auto& sourcePath : m_assetSources) { Logger::info("Loading assets from: '{}'", sourcePath); AssetSourcePtr source; @@ -105,6 +153,25 @@ Assets::Assets(Settings settings, StringList assetSources) { auto& descriptor = m_files[filename]; descriptor.sourceName = filename; descriptor.source = source; + m_filesByExtension[AssetPath::extension(filename).toLower()].insert(filename); + } + auto metadata = source->metadata(); + if (auto scripts = metadata.ptr("scripts")) { + if (auto onLoad = scripts->optArray("onLoad")) { + Logger::info("Running onLoad scripts {}", *onLoad); + try { + auto context = luaEngine->createContext(); + decorateLuaContext(context); + for (auto& jPath : *onLoad) { + auto path = jPath.toString(); + auto script = source->read(path); + context.load(script, path); + } + } + catch (LuaException const& e) { + Logger::error("Exception while running onLoad scripts from asset source '{}': {}", sourcePath, e.what()); + } + } } } @@ -133,9 +200,6 @@ Assets::Assets(Settings settings, StringList assetSources) { m_digest = digest.compute(); - for (auto const& filename : m_files.keys()) - m_filesByExtension[AssetPath::extension(filename).toLower()].append(filename); - int workerPoolSize = m_settings.workerPoolSize; for (int i = 0; i < workerPoolSize; i++) m_workerThreads.append(Thread::invoke("Assets::workerMain", mem_fn(&Assets::workerMain), this)); @@ -194,7 +258,9 @@ Maybe<String> Assets::assetSourcePath(AssetSourcePtr const& source) const { StringList Assets::scan(String const& suffix) const { if (suffix.beginsWith(".") && !suffix.substr(1).hasChar('.')) { - return scanExtension(suffix); + return scanExtension(suffix).values(); + } else if (suffix.empty()) { + return m_files.keys(); } else { StringList result; for (auto const& fileEntry : m_files) { @@ -210,7 +276,7 @@ StringList Assets::scan(String const& suffix) const { StringList Assets::scan(String const& prefix, String const& suffix) const { StringList result; if (suffix.beginsWith(".") && !suffix.substr(1).hasChar('.')) { - StringList filesWithExtension = scanExtension(suffix); + StringSet filesWithExtension = scanExtension(suffix); for (auto const& file : filesWithExtension) { if (file.beginsWith(prefix, String::CaseInsensitive)) result.append(file); @@ -225,11 +291,11 @@ StringList Assets::scan(String const& prefix, String const& suffix) const { return result; } -StringList Assets::scanExtension(String const& extension) const { - if (extension.beginsWith(".")) - return m_filesByExtension.value(extension.substr(1)); - else - return m_filesByExtension.value(extension); +const StringSet NullStringSet; + +StringSet const& Assets::scanExtension(String const& extension) const { + auto find = m_filesByExtension.find(extension.beginsWith(".") ? extension.substr(1) : extension); + return find != m_filesByExtension.end() ? find->second : NullStringSet; } Json Assets::json(String const& path) const { @@ -255,6 +321,16 @@ void Assets::queueJsons(StringList const& paths) const { })); } +void Assets::queueJsons(StringSet const& paths) const { + MutexLocker assetsLocker(m_assetsMutex); + for (String const& path : paths) { + auto components = AssetPath::split(path); + validatePath(components, true, false); + + queueAsset(AssetId{AssetType::Json, {components.basePath, {}, {}}}); + }; +} + ImageConstPtr Assets::image(AssetPath const& path) const { validatePath(path, true, true); @@ -270,6 +346,16 @@ void Assets::queueImages(StringList const& paths) const { })); } +void Assets::queueImages(StringSet const& paths) const { + MutexLocker assetsLocker(m_assetsMutex); + for (String const& path : paths) { + auto components = AssetPath::split(path); + validatePath(components, true, true); + + queueAsset(AssetId{AssetType::Image, std::move(components)}); + }; +} + ImageConstPtr Assets::tryImage(AssetPath const& path) const { validatePath(path, true, true); @@ -296,13 +382,23 @@ AudioConstPtr Assets::audio(String const& path) const { void Assets::queueAudios(StringList const& paths) const { queueAssets(paths.transformed([](String const& path) { - const auto components = AssetPath::split(path); + auto components = AssetPath::split(path); validatePath(components, false, false); return AssetId{AssetType::Audio, std::move(components)}; })); } +void Assets::queueAudios(StringSet const& paths) const { + MutexLocker assetsLocker(m_assetsMutex); + for (String const& path : paths) { + auto components = AssetPath::split(path); + validatePath(components, false, true); + + queueAsset(AssetId{AssetType::Audio, std::move(components)}); + }; +} + AudioConstPtr Assets::tryAudio(String const& path) const { auto components = AssetPath::split(path); validatePath(components, false, false); @@ -490,17 +586,20 @@ FramesSpecification Assets::parseFramesSpecification(Json const& frameConfig, St void Assets::queueAssets(List<AssetId> const& assetIds) const { MutexLocker assetsLocker(m_assetsMutex); - for (auto const& id : assetIds) { - auto i = m_assetsCache.find(id); - if (i != m_assetsCache.end()) { - if (i->second) - freshen(i->second); - } else { - auto j = m_queue.find(id); - if (j == m_queue.end()) { - m_queue[id] = QueuePriority::Load; - m_assetsQueued.signal(); - } + for (auto const& id : assetIds) + queueAsset(id); +} + +void Assets::queueAsset(AssetId const& assetId) const { + auto i = m_assetsCache.find(assetId); + if (i != m_assetsCache.end()) { + if (i->second) + freshen(i->second); + } else { + auto j = m_queue.find(assetId); + if (j == m_queue.end()) { + m_queue[assetId] = QueuePriority::Load; + m_assetsQueued.signal(); } } } |