Cogs.Core
ResourceArchive.cpp
1#include "ResourceArchive.h"
2#include "Context.h"
3#include "Resources/ResourceStore.h"
4
5#include "Foundation/Logging/Logger.h"
6#include "Foundation/Platform/IO.h"
7#include "Foundation/Platform/Threads.h"
8
9#include <../miniz/miniz.h>
10
11#include <unordered_map>
12
13namespace
14{
15 const Cogs::Logging::Log logger = Cogs::Logging::getLogger("ResourceArchive");
16}
17
19{
20 mz_zip_archive archive = {};
21 // TODO Adding custom hashing to allow string_view lookup - when Linux compiler allows.
22 std::unordered_map<std::string, mz_zip_archive_file_stat> entries;
23 mutable Mutex m;
24};
25
26extern "C"
27{
28 int ResourceArchiveDummy() { return 42; }
29}
30
31Cogs::Core::ResourceArchive::ResourceArchive(ResourceStore * store, std::string_view archivePath)
32 : storage(std::make_unique<Archive>())
33{
34 std::string archiveName(archivePath);
35 std::string fileName = archiveName;
36
37#ifndef EMSCRIPTEN
38 if (!IO::exists(fileName)) {
39 // Couldn't locate the archive in the current working directory, try the directory containing the dll we are running from:
40 fileName = IO::getPathRelativeToDll(archiveName, (void *)&ResourceArchiveDummy);
41 }
42
43 if (!IO::exists(fileName)) {
44 // Look in our parent directory. This is the most likely location during development.
45 fileName = IO::combine(IO::parentPath(IO::parentPath(fileName)), IO::fileName(archivePath));
46
47 if (!IO::exists(fileName)) {
48 // If we could not locate the default resource archive in our current directory, we try once more using
49 // the COGSDIR variable.
50 auto cogsDir = IO::expandEnvironmentVariable("COGSDIR");
51
52 if (cogsDir.size() && (cogsDir != "%COGSDIR%")) {
53 fileName = cogsDir + "/bin/" + archiveName;
54
55 if (!IO::exists(fileName)) {
56 // Revert to lookup in resource system.
57 fileName = archiveName;
58 }
59 }
60 else {
61 LOG_WARNING(logger, "Could not locate resource file %s, no COGSDIR environment variable found.", fileName.c_str());
62 }
63 }
64 }
65#endif
66
67 auto contents = store->getResourceContents(fileName);
68
69 if (contents.empty()) {
70 LOG_ERROR(logger, "File or Resource not found? Empty resource archive. %s", fileName.c_str());
71 return;
72 }
73 mz_uint numEntries = 0;
74
75 if (mz_zip_reader_init_mem(&storage->archive, contents.data(), contents.size(), 0)) {
76 numEntries = mz_zip_reader_get_num_files(&storage->archive);
77 LOG_DEBUG(logger, "Loaded resource file (Entries=%u) %s.", static_cast<uint32_t>(numEntries), fileName.c_str());
78 } else {
79 LOG_ERROR(logger, "Failed to open archive %s.", fileName.c_str());
80 }
81
82 if (!numEntries) {
83 LOG_WARNING(logger, "Archive %s contains no entries.", fileName.c_str());
84 }
85
86 for (mz_uint i = 0; i < numEntries; ++i) {
87 mz_zip_archive_file_stat entry = {};
88 mz_zip_reader_file_stat(&storage->archive, i, &entry);
89
90 storage->entries[entry.m_filename] = entry;
91 }
92}
93
94Cogs::Core::ResourceArchive::~ResourceArchive()
95{
96 if (storage && storage->archive.m_archive_size) {
97 if (!mz_zip_reader_end(&storage->archive)) {
98 LOG_WARNING(logger, "Failed to release zip archive.");
99 }
100 }
101}
102
103Cogs::Core::ResourceArchive::ResourceArchive(ResourceArchive && other)
104{
105 storage = std::move(other.storage);
106}
107
109{
110 storage = std::move(other.storage);
111 return *this;
112}
113
114
115bool Cogs::Core::ResourceArchive::hasResource(std::string_view path) const
116{
117 LockGuard lock(storage->m);
118
119 auto archiveIt = storage->entries.find(std::string(path));
120
121 return archiveIt != storage->entries.end();
122}
123
124Cogs::Memory::MemoryBuffer Cogs::Core::ResourceArchive::getResource(std::string_view path) const
125{
126 LockGuard lock(storage->m);
127
128 auto archiveIt = storage->entries.find(std::string(path));
129 if (archiveIt != storage->entries.end()) {
130 const auto& entry = archiveIt->second;
131 Cogs::Memory::MemoryBuffer bytes(entry.m_uncomp_size, Cogs::MemBlockType::IOResource);
132 mz_zip_reader_extract_to_mem(&storage->archive, entry.m_file_index, bytes.data(), bytes.size(), 0);
133 return bytes;
134 }
135 else {
136 LOG_WARNING(logger, "Internal Error: read of non-existing resource. Must check hasResource.");
138 return bytes;
139 }
140}
Represents a resource archive used to load resources at runtime.
ResourceArchive(class ResourceStore *context, std::string_view archiveName)
Construct a resource archive with the given archive file.
ResourceArchive & operator=(ResourceArchive &&other)
Move assign operator.
Provides handling of reading and caching of external resources.
ResourceBuffer getResourceContents(std::string_view resourceName, ResourceStoreFlags flags=ResourceStoreFlags::None) const
Get the contents of the resource with the given name.
Log implementation class.
Definition: LogManager.h:139
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:180
STL namespace.