1#include "ResourceStore.h"
2#include "ResourceStoreStorage.h"
4#include "DataFetcherManager.h"
7#include "MemoryContext.h"
8#include "Services/Variables.h"
10#include "Rendering/IEffects.h"
12#include "Foundation/Logging/Logger.h"
13#include "Foundation/Platform/FileContents.h"
14#include "Foundation/Platform/IO.h"
15#include "Foundation/StringView.h"
29 constexpr operator bool()
const
37 struct PathAndProtocol
39 const std::string_view path;
40 ResourceProtocol protocol;
43 PathAndProtocol getPathAndProtocol(std::string_view resourceName)
45 auto protocol = ResourceProtocol::None;
47 if (resourceName.substr(0, 10) ==
"archive://") protocol = ResourceProtocol::Archive;
48 else if (resourceName.substr(0, 7) ==
"file://") protocol = ResourceProtocol::File;
49 else if (resourceName.substr(0, 7) ==
"http://") protocol = ResourceProtocol::Http;
51 auto offset = resourceName.find(
"://");
52 if (offset != std::string_view::npos) {
53 return { resourceName.substr(offset + 3), ResourceProtocol::Other };
57 if (protocol == ResourceProtocol::File)
return { resourceName.substr(7), protocol };
58 else if (protocol == ResourceProtocol::Archive)
return { resourceName.substr(10), protocol };
59 else return { resourceName, protocol };
64 Cogs::LockGuard lock(data->cache->lock);
65 if (
auto resourceIt = data->cache->resources.find(resourceName); resourceIt != data->cache->resources.end()) {
66 return ResourceLookup{ resourceIt->second.bytes ,
true };
69 return ResourceLookup{};
73 ResourceLookup getFileBytes(
ResourceStoreStorage* data,
const std::string& resourceName,
bool useCache)
75 auto contents = DataFetcherManager::fetchSync(resourceName);
79 return ResourceLookup{ {std::make_shared<Cogs::Memory::MemoryBuffer>(contents->take())},
true };
82 Cogs::LockGuard lock(data->cache->lock);
84 Resource& cached = data->cache->resources[resourceName] =
Resource{ resourceName, resourceName, {std::make_shared<Cogs::Memory::MemoryBuffer>(contents->take())} };
86 return ResourceLookup{ cached.bytes,
true };
90 return ResourceLookup{};
96 for (
auto& archive : data->archives) {
97 if (archive.hasResource(resourceName)) {
101 Cogs::LockGuard lock(data->cache->lock);
103 Resource& cached = data->cache->resources[resourceName] =
Resource{ resourceName, resourceName, {std::make_shared<Cogs::Memory::MemoryBuffer>(std::move(contents))} };
105 return ResourceLookup{ cached.bytes,
true };
110 return ResourceLookup{};
115 std::string name(resourceName);
117 std::replace(name.begin(), name.end(),
'\\',
'/');
119 ResourceLookup archiveContents = getArchiveBytes(data, name);
121 if (archiveContents) {
122 callback(archiveContents.buffer);
123 data->context->engine->setDirty();
130 ResourceLookup contents = getCacheBytes(data, resourceName);
133 callback(contents.buffer);
138 auto path = (resourceName.find(
"https://") == std::string::npos) ? data->dataDir + resourceName : resourceName;
141 std::string path = !Cogs::IO::isAbsolute(resourceName) && (resourceName.find(
"../") == std::string::npos) && (resourceName.find(
"..\\") == std::string::npos)
142 ? Cogs::IO::combine(data->dataDir, resourceName) : resourceName;
144 if (!Cogs::IO::isFile(path)) {
145 loadFromArchive(data, resourceName, callback);
150 DataFetcherManager::fetchAsync(data->context, path, [=](std::unique_ptr<Cogs::FileContents> contents)
153 ResourceBuffer result;
156 Cogs::LockGuard lock(data->cache->lock);
158 Resource& c = data->cache->resources[resourceName] = Resource{ resourceName, path, { std::make_shared<Cogs::Memory::MemoryBuffer>(contents->take()) } };
160 LOG_TRACE(logger,
"Cached file %s. Size: %zd", resourceName.c_str(), c.bytes.size());
166 data->context->engine->setDirty();
170 loadFromArchive(data, resourceName, callback);
197 if (resolvedContents.size()) {
198 content.append(resolvedContents.toStringView());
213 void pushSearchPaths()
override { }
220 void popSearchPaths()
override { }
222 std::string getPath(FileType type,
const std::string& fileName)
const
224 if (type == IIOHandler::FileType::ShaderCache) {
225 std::string path = context->
variables->get(
"cache.shaderCache", R
"(%TEMP%\Kongsberg Digital\Cogs\ShaderCache\)");
226 return Cogs::IO::expandPath(Cogs::IO::combine(path, fileName));
228 else if (type == IIOHandler::FileType::ShaderDump) {
229 std::string path = context->
variables->get(
"effects.dumpPath", R
"(%TEMP%\Kongsberg Digital\Cogs\ShaderDumps\)");
230 return Cogs::IO::expandPath(Cogs::IO::combine(path, fileName));
238 auto content = store->
getResourceContents(getPath(type, std::string(fileName)), ResourceStoreFlags::QueryOnly);
240 return !content.empty();
243 bool writeBinaryFile(FileType type,
const Cogs::StringView& fileName,
const void* content,
size_t contentSize)
override
252 return Cogs::IO::writeBinaryFile(getPath(type, std::string(fileName)), content, contentSize);
256 bool openBinaryFile(FileType type,
const Cogs::StringView& fileName, std::vector<uint8_t>& content)
override
259 if (!buffer.size())
return false;
261 const auto view = buffer.
toSpan<uint8_t>();
262 content.assign(view.begin(), view.end());
273Cogs::Core::ResourceStoreStorage::ResourceStoreStorage(
Context* context,
ResourceStore* resourceStore) :
276 ioHandler(
std::make_unique<
IOHandler>(context, resourceStore))
285 auto dataDirSetting = context->
variables->get(
"resources.dataDir",
"");
286 LOG_DEBUG(logger,
"Setting data directory to %s.", dataDirSetting);
287 data->dataDir = dataDirSetting;
289 addSearchPath(data->dataDir);
297Cogs::Core::ResourceStore::~ResourceStore() =
default;
302 for (
const std::string& name : resourceNames) {
304 LockGuard lock(data->cache->lock);
307 if (data->cache->loading.contains(name))
continue;
308 if (data->cache->resources.contains(name))
continue;
310 data->cache->loading.insert(name);
313 readFileAsync(data.get(), name, [
this, name](
const ResourceBuffer& bytes)
316 LOG_DEBUG(logger,
"Preloaded file %s.", name.c_str());
318 LOG_DEBUG(logger,
"Failed preloading file %s.", name.c_str());
322 LockGuard lock(data->cache->lock);
323 data->cache->loading.erase(data->cache->loading.find(name));
325 data->context->engine->setDirty();
334 for (
auto & name : resourceNames) {
335 loaded &= hasResource(name);
344 LockGuard lock(data->cache->lock);
345 if (
auto resourceIt = data->cache->resources.find(resourceName); resourceIt != data->cache->resources.end()) {
351 for (
auto & path : data->orderedSearchPaths) {
352 const auto resourcePath = path + resourceName;
354 for (
auto & archive : data->archives) {
355 if (archive.hasResource(resourcePath))
return true;
364 for (
auto & path : data->orderedSearchPaths) {
365 std::string resourcePath = IO::combine(path, std::string(resourceName));
366 std::replace(resourcePath.begin(), resourcePath.end(),
'\\',
'/');
373 auto cacheContents = getCacheBytes(data.get(), resourcePath);
376 return cacheContents.buffer;
381 if (!IO::exists(resourcePath)) {
383 auto cacheContents = getCacheBytes(data.get(), resourcePath);
385 return cacheContents.buffer;
392 auto contents = getFileBytes(data.get(), resourcePath, useCache);
395 return contents.buffer;
400 for (
auto & path : data->orderedSearchPaths) {
401 std::string resourcePath = IO::combine(path, std::string(resourceName));
403 std::replace(resourcePath.begin(), resourcePath.end(),
'\\',
'/');
405 auto archiveContents = getArchiveBytes(data.get(), resourcePath);
407 if (archiveContents) {
408 return archiveContents.buffer;
412 if ((flags & ResourceStoreFlags::QueryOnly) == 0) {
413 LOG_ERROR(logger,
"Could not resolve resource: %.*s.", StringViewFormat(resourceName));
422 auto bytes = getResourceContents(resourceName, flags);
426 std::string_view view = bytes.toStringView();
435 return data->dataDir;
441 Memory::MemoryBuffer buffer(content.size(), data->context->memory->ioAllocator, MemBlockType::IOResource);
442 std::memcpy(buffer.data(), content.data(), buffer.size());
444 std::string name(resourceName);
445 std::replace(name.begin(), name.end(),
'\\',
'/');
447 LockGuard lock(data->cache->lock);
448 data->cache->resources[name] =
Resource{ std::string(resourceName), std::string(resourceName), {std::make_shared<Memory::MemoryBuffer>(std::move(buffer))} };
453 Memory::MemoryBuffer buffer(content_size, data->context->memory->ioAllocator, MemBlockType::IOResource);
454 std::memcpy(buffer.data(), content_data, std::min(content_size, buffer.size()));
456 std::string name(resourceName);
457 std::replace(name.begin(), name.end(),
'\\',
'/');
459 LockGuard lock(data->cache->lock);
460 data->cache->resources[name] =
Resource{ std::string(resourceName), std::string(resourceName), {std::make_shared<Memory::MemoryBuffer>(std::move(buffer))} };
466 data->archives.emplace(data->archives.begin(),
this, archiveName);
469 data->archives.emplace_back(
this, archiveName);
473std::string Cogs::Core::ResourceStore::getResourcePath(std::string_view resourceName,
ResourceStoreFlags flags)
const
475 for (
auto & searchPath : data->orderedSearchPaths) {
476 std::string path = IO::combine(searchPath, std::string(resourceName));
478 std::replace(path.begin(), path.end(),
'\\',
'/');
481 auto cacheContents = getCacheBytes(data.get(), path);
483 if (cacheContents)
return path;
486 if (IO::exists(path) && IO::isFile(path)) {
496 if (resourceName.empty())
return {};
498 auto [resourcePath, protocol] = getPathAndProtocol(resourceName);
500 if (protocol == ResourceProtocol::File || protocol == ResourceProtocol::None) {
501 std::string str(resourcePath);
503 if (protocol == ResourceProtocol::None && !IO::isRelative(str)) {
505 auto cacheContents = getCacheBytes(data.get(), str);
507 if (cacheContents)
return { str, ResourceProtocol::Cache };
510 return { str, ResourceProtocol::File };
512 for (
auto & searchPath : data->orderedSearchPaths) {
513 const auto path = IO::combine(searchPath, str);
516 auto cacheContents = getCacheBytes(data.get(), path);
518 if (cacheContents)
return { path, ResourceProtocol::Cache };
521 if (IO::exists(path) && IO::isFile(path)) {
522 if (protocol == ResourceProtocol::None) {
523 return { path, ResourceProtocol::File };
530 if (protocol == ResourceProtocol::None || protocol == ResourceProtocol::Archive) {
531 for (
auto & searchPath : data->orderedSearchPaths) {
532 const auto archivePath = IO::combine(searchPath, std::string(resourcePath));
534 for (
auto & archive : data->archives) {
535 if (archive.hasResource(archivePath)) {
536 return { archivePath, ResourceProtocol::Archive };
542 return { std::string(resourceName), ResourceProtocol::None };
547 if (!data->searchPaths.contains(path)) {
548 data->searchPaths.insert(path);
550 data->orderedSearchPaths.insert(data->orderedSearchPaths.begin(), path);
553 data->orderedSearchPaths.emplace_back(path);
560 LockGuard lock(data->cache->lock);
561 if (
auto it = data->cache->resources.find(resourceName); it != data->cache->resources.end()) {
562 data->cache->resources.erase(it);
565 auto it = data->archives.begin();
566 for (
auto& archive : data->archives)
568 if (archive.hasResource(resourceName))
570 data->archives.erase(it);
579 LockGuard lock(data->cache->lock);
580 data->cache->resources.clear();
585 return data->ioHandler.get();
A Context instance contains all the services, systems and runtime components needed to use Cogs.
std::unique_ptr< class Variables > variables
Variables service instance.
Provides handling of reading and caching of external resources.
const std::string & getDataDir() const
Get the path of the data directory.
void addResource(std::string_view resourceName, const std::string_view content)
Add the given resource string to the resource store.
void addResourceArchive(const std::string &archiveName, bool prepend=false)
Add the given resource archive to the store.
ResourceQueryResult getResourceLocation(std::string_view resourceName, ResourceStoreFlags flags=ResourceStoreFlags::None) const
Get location data for the given resource.
IIOHandler * getIOHandler()
Retrieve the IO handler interface.
std::string getResourceContentString(std::string_view resourceName, ResourceStoreFlags flags=ResourceStoreFlags::None) const
Get the contents of the resource with the given copied into a string.
void addSearchPath(const std::string &path, bool prepend=false)
Add the given path to the set of search paths used to look up resources.
bool hasResource(const std::string &resourceName)
Check if the given resource is present in the resource store.
bool hasResources(const std::vector< std::string > &resourceNames)
Check if all the given resources are present in the resource store.
void purge(const std::string &resourceName)
Purge the given file from the cache.
ResourceBuffer getResourceContents(std::string_view resourceName, ResourceStoreFlags flags=ResourceStoreFlags::None) const
Get the contents of the resource with the given name.
void preloadResources(const std::vector< std::string > &resourceNames)
Tells the resource system to fetch and cache the given resources.
void clear()
Clear the cache.
Log implementation class.
Provides a weakly referenced view over the contents of a string.
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
@ PreferUncachedContent
Try fetching data before resorting to cached data.
@ NoCachedContent
Never use cached data.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
bool resolveFile(const Cogs::StringView &source, const Cogs::StringView &fileName, std::string &content) override
Callback method used to resolve include statements in shader code and get the contents to include.
bool openFile(const Cogs::StringView &fileName, std::string &content) override
Callback method used to open files.
std::span< const T > toSpan(size_t offset=0) const