diff options
Diffstat (limited to 'source/base')
-rw-r--r-- | source/base/CMakeLists.txt | 2 | ||||
-rw-r--r-- | source/base/StarAssets.cpp | 92 | ||||
-rw-r--r-- | source/base/StarMemoryAssetSource.cpp | 86 | ||||
-rw-r--r-- | source/base/StarMemoryAssetSource.hpp | 29 |
4 files changed, 187 insertions, 22 deletions
diff --git a/source/base/CMakeLists.txt b/source/base/CMakeLists.txt index 712e201..f16257a 100644 --- a/source/base/CMakeLists.txt +++ b/source/base/CMakeLists.txt @@ -14,6 +14,7 @@ SET (star_base_HEADERS StarCellularLiquid.hpp StarConfiguration.hpp StarDirectoryAssetSource.hpp + StarMemoryAssetSource.hpp StarMixer.hpp StarPackedAssetSource.hpp StarVersion.hpp @@ -27,6 +28,7 @@ SET (star_base_SOURCES StarCellularLighting.cpp StarConfiguration.cpp StarDirectoryAssetSource.cpp + StarMemoryAssetSource.cpp StarMixer.cpp StarPackedAssetSource.cpp StarVersionOptionParser.cpp diff --git a/source/base/StarAssets.cpp b/source/base/StarAssets.cpp index 2515b5e..9ba7029 100644 --- a/source/base/StarAssets.cpp +++ b/source/base/StarAssets.cpp @@ -4,6 +4,7 @@ #include "StarTime.hpp" #include "StarDirectoryAssetSource.hpp" #include "StarPackedAssetSource.hpp" +#include "StarMemoryAssetSource.hpp" #include "StarJsonBuilder.hpp" #include "StarJsonExtra.hpp" #include "StarJsonPatch.hpp" @@ -108,18 +109,54 @@ Assets::Assets(Settings settings, StringList assetSources) { m_assetSources = std::move(assetSources); auto luaEngine = LuaEngine::create(); - auto decorateLuaContext = [this](LuaContext& context) { + auto decorateLuaContext = [this](LuaContext& context, MemoryAssetSourcePtr mySource) { 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()); }); + + callbacks.registerCallback("add", [this, &mySource](LuaEngine& engine, String const& path, LuaValue const& data) -> bool { + ByteArray bytes; + if (auto str = engine.luaMaybeTo<String>(data)) + bytes = ByteArray(str->utf8Ptr(), str->utf8Size()); + else { + auto json = engine.luaTo<Json>(data).repr(); + bytes = ByteArray(json.utf8Ptr(), json.utf8Size()); + } + return mySource->set(path, bytes); + }); + + callbacks.registerCallback("patch", [this, &mySource](String const& path, String const& patchPath) -> bool { + if (auto file = m_files.ptr(path)) { + if (mySource->contains(patchPath)) { + file->patchSources.append(make_pair(patchPath, mySource)); + return true; + } else { + if (auto asset = m_files.ptr(patchPath)) { + file->patchSources.append(make_pair(patchPath, asset->source)); + return true; + } + } + } + return false; + }); + + callbacks.registerCallback("erase", [this, &mySource](String const& path) -> bool { + bool erased = m_files.erase(path); + if (erased) + m_filesByExtension[AssetPath::extension(path).toLower()].erase(path); + return erased; + }); + context.setCallbacks("assets", callbacks); }; @@ -131,37 +168,46 @@ Assets::Assets(Settings settings, StringList assetSources) { else source = std::make_shared<PackedAssetSource>(sourcePath); - m_assetSourcePaths.add(sourcePath, source); - - for (auto const& filename : source->assetPaths()) { - if (filename.contains(AssetsPatchSuffix, String::CaseInsensitive)) { - if (filename.endsWith(AssetsPatchSuffix, String::CaseInsensitive)) { - auto targetPatchFile = filename.substr(0, filename.size() - strlen(AssetsPatchSuffix)); - if (auto p = m_files.ptr(targetPatchFile)) - p->patchSources.append({filename, source}); - } else { - for (int i = 0; i < 10; i++) { - if (filename.endsWith(AssetsPatchSuffix + toString(i), String::CaseInsensitive)) { - auto targetPatchFile = filename.substr(0, filename.size() - strlen(AssetsPatchSuffix) + 1); - if (auto p = m_files.ptr(targetPatchFile)) - p->patchSources.append({filename, source}); - break; + auto addSource = [&](String const& sourcePath, AssetSourcePtr source) { + m_assetSourcePaths.add(sourcePath, source); + + for (auto const& filename : source->assetPaths()) { + if (filename.contains(AssetsPatchSuffix, String::CaseInsensitive)) { + if (filename.endsWith(AssetsPatchSuffix, String::CaseInsensitive)) { + auto targetPatchFile = filename.substr(0, filename.size() - strlen(AssetsPatchSuffix)); + if (auto p = m_files.ptr(targetPatchFile)) + p->patchSources.append({filename, source}); + } else { + for (int i = 0; i < 10; i++) { + if (filename.endsWith(AssetsPatchSuffix + toString(i), String::CaseInsensitive)) { + auto targetPatchFile = filename.substr(0, filename.size() - strlen(AssetsPatchSuffix) + 1); + if (auto p = m_files.ptr(targetPatchFile)) + p->patchSources.append({filename, source}); + break; + } } } } + + auto& descriptor = m_files[filename]; + descriptor.sourceName = filename; + descriptor.source = source; + m_filesByExtension[AssetPath::extension(filename).toLower()].insert(filename); } - auto& descriptor = m_files[filename]; - descriptor.sourceName = filename; - descriptor.source = source; - m_filesByExtension[AssetPath::extension(filename).toLower()].insert(filename); - } + }; + addSource(sourcePath, source); + auto metadata = source->metadata(); if (auto scripts = metadata.ptr("scripts")) { if (auto onLoad = scripts->optArray("onLoad")) { + JsonObject memoryMetadata{ + {"name", strf("{}.onLoad", metadata.value("name", File::baseName(sourcePath)))} + }; + auto memoryAssets = make_shared<MemoryAssetSource>(memoryMetadata); Logger::info("Running onLoad scripts {}", *onLoad); try { auto context = luaEngine->createContext(); - decorateLuaContext(context); + decorateLuaContext(context, memoryAssets); for (auto& jPath : *onLoad) { auto path = jPath.toString(); auto script = source->read(path); @@ -171,6 +217,8 @@ Assets::Assets(Settings settings, StringList assetSources) { catch (LuaException const& e) { Logger::error("Exception while running onLoad scripts from asset source '{}': {}", sourcePath, e.what()); } + if (!memoryAssets->empty()) + addSource(memoryMetadata.get("name").toString(), memoryAssets); } } } diff --git a/source/base/StarMemoryAssetSource.cpp b/source/base/StarMemoryAssetSource.cpp new file mode 100644 index 0000000..9c777ed --- /dev/null +++ b/source/base/StarMemoryAssetSource.cpp @@ -0,0 +1,86 @@ +#include "StarMemoryAssetSource.hpp" +#include "StarDataStreamDevices.hpp" +#include "StarDataStreamExtra.hpp" +#include "StarSha256.hpp" + +namespace Star { + +MemoryAssetSource::MemoryAssetSource(JsonObject metadata) : m_metadata(metadata) {} + +JsonObject MemoryAssetSource::metadata() const { + return m_metadata; +} + +StringList MemoryAssetSource::assetPaths() const { + return m_files.keys(); +} + +IODevicePtr MemoryAssetSource::open(String const& path) { + struct AssetReader : public IODevice { + AssetReader(ByteArrayPtr assetData, String name) : assetData(assetData), name(name) { setMode(IOMode::Read); } + + size_t read(char* data, size_t len) override { + len = min<StreamOffset>(len, StreamOffset(assetData->size()) - assetPos); + assetData->copyTo(data, len); + return len; + } + + size_t write(char const*, size_t) override { + throw IOException("Assets IODevices are read-only"); + } + + StreamOffset size() override { return assetData->size(); } + StreamOffset pos() override { return assetPos; } + + String deviceName() const override { return name; } + + bool atEnd() override { + return assetPos >= assetData->size(); + } + + void seek(StreamOffset p, IOSeek mode) override { + if (mode == IOSeek::Absolute) + assetPos = p; + else if (mode == IOSeek::Relative) + assetPos = clamp<StreamOffset>(assetPos + p, 0, assetData->size()); + else + assetPos = clamp<StreamOffset>(assetPos - p, 0, assetData->size()); + } + + ByteArrayPtr assetData; + StreamOffset assetPos; + String name; + }; + + auto p = m_files.ptr(path); + if (!p) + throw AssetSourceException::format("Requested file '{}' does not exist in memory", path); + + return make_shared<AssetReader>(*p, path); +} + +bool MemoryAssetSource::empty() const { + return m_files.empty(); +} + +bool MemoryAssetSource::contains(String const& path) const { + return m_files.contains(path); +} + +bool MemoryAssetSource::erase(String const& path) { + return m_files.erase(path) != 0; +} + +bool MemoryAssetSource::set(String const& path, ByteArray data) { + return m_files.emplace(path, make_shared<ByteArray>(std::move(data))).second; +} + +ByteArray MemoryAssetSource::read(String const& path) { + auto p = m_files.ptr(path); + if (!p) + throw AssetSourceException::format("Requested file '{}' does not exist in memory", path); + else + return *p->get(); // this is a double indirection, and that freaking sucks!! +} + +} diff --git a/source/base/StarMemoryAssetSource.hpp b/source/base/StarMemoryAssetSource.hpp new file mode 100644 index 0000000..353a132 --- /dev/null +++ b/source/base/StarMemoryAssetSource.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include "StarAssetSource.hpp" +#include "StarIODevice.hpp" + +namespace Star { + +STAR_CLASS(MemoryAssetSource); + +class MemoryAssetSource : public AssetSource { +public: + MemoryAssetSource(JsonObject metadata = JsonObject()); + + JsonObject metadata() const override; + StringList assetPaths() const override; + + IODevicePtr open(String const& path) override; + + bool empty() const; + bool contains(String const& path) const; + bool erase(String const& path); + bool set(String const& path, ByteArray data); + ByteArray read(String const& path) override; +private: + JsonObject m_metadata; + StringMap<ByteArrayPtr> m_files; +}; + +} |