Chira Engine
A customizable MIT-licensed game engine.
FilesystemResourceProvider.cpp
1 #include "FilesystemResourceProvider.h"
2 
3 #include <fstream>
4 #include <filesystem>
5 #include <utility>
6 #include <core/Platform.h>
7 #include <resource/Resource.h>
8 
9 #ifdef CHIRA_PLATFORM_APPLE
10  #include "CoreFoundation/CoreFoundation.h"
11 #endif
12 
13 using namespace chira;
14 
15 CHIRA_CREATE_LOG(FILESYSTEM);
16 
17 FilesystemResourceProvider::FilesystemResourceProvider(std::string path_, bool isPathAbsolute, const std::string& name_)
18  : IResourceProvider(name_)
19  , path(std::move(path_))
20  , absolute(isPathAbsolute) {
21  FilesystemResourceProvider::nixifyPath(this->path);
22  this->path = String::stripRight(this->path, '/');
23  if (!this->absolute) {
24  // MacOS is a lot more complex. There's more than 1 place our resources could be at
25  // One requires a bit of work to access though.
26 #ifdef CHIRA_PLATFORM_APPLE
27  // Finding the bundle and it's resources directory
28  bool error = false;
29  if (CFBundleRef mainBundle = CFBundleGetMainBundle()) {
30  CFURLRef appUrlRef = CFBundleCopyBundleURL(mainBundle);
31  CFStringRef macPath;
32  if (appUrlRef)
33  macPath = CFURLCopyFileSystemPath(appUrlRef, kCFURLPOSIXPathStyle);
34  else
35  macPath = nullptr;
36 
37  const char* rawpath;
38 
39  if (macPath)
40  rawpath = CFStringGetCStringPtr(macPath, kCFStringEncodingASCII);
41  else
42  rawpath = nullptr;
43 
44  CFRelease(macPath);
45  CFRelease(appUrlRef);
46  std::string append = "/Contents/Resource";
47  if (std::filesystem::exists(std::filesystem::path{rawpath + append + FILESYSTEM_ROOT_FOLDER + '/' + this->path})) {
48  LOG_FILESYSTEM.info("Found resources in app bundle!");
49  this->path = rawpath + append + FILESYSTEM_ROOT_FOLDER + '/' + this->path;
50  } else {
51  error = true;
52  }
53  } else {
54  error = true;
55  }
56  if (error) {
57  LOG_FILESYSTEM.warning("Could not find resources in our app bundle! Falling back to working directory...");
58  if (std::filesystem::exists(std::filesystem::path{FILESYSTEM_ROOT_FOLDER + '/' + this->path})) {
59  LOG_FILESYSTEM.info("Found resources in working directory!");
60  this->path = FILESYSTEM_ROOT_FOLDER + '/' + this->path;
61  } else {
62  LOG_FILESYSTEM.error("No known search path contains our required resources! Did you mistype something?");
63  }
64  }
65 #else
66  // Other platforms can just look in the working directory
67  this->path = FILESYSTEM_ROOT_FOLDER + '/' + this->path;
68 #endif
69  }
70 }
71 
72 bool FilesystemResourceProvider::hasResource(std::string_view name) const {
73  if (this->absolute)
74  return std::filesystem::exists(std::filesystem::path{this->path}.append(name));
75  else
76  return std::filesystem::exists(std::filesystem::current_path().append(this->path).append(name));
77 }
78 
79 void FilesystemResourceProvider::compileResource(std::string_view name, Resource* resource) const {
80  std::filesystem::path resourcePath;
81  if (this->absolute)
82  resourcePath = std::filesystem::path{this->path}.append(name);
83  else
84  resourcePath = std::filesystem::current_path().append(this->path).append(name);
85  std::uintmax_t fileSize = std::filesystem::file_size(resourcePath);
86  std::ifstream ifs(resourcePath.string().c_str(), std::ios::in | std::ios::binary);
87  ifs.seekg(0, std::ios::beg);
88  auto* bytes = new byte[(std::size_t) fileSize + 1];
89  ifs.read(reinterpret_cast<char*>(bytes), static_cast<std::streamsize>(fileSize));
90  bytes[fileSize] = '\0';
91  resource->compile(bytes, (std::size_t) fileSize + 1);
92  delete[] bytes;
93 }
94 
95 std::string FilesystemResourceProvider::getFolder() const {
96  return String::stripLeft(std::string{this->getPath().data()}, FILESYSTEM_ROOT_FOLDER + '/');
97 }
98 
99 std::string FilesystemResourceProvider::getLocalResourceAbsolutePath(const std::string& identifier) const {
100  // Make sure we've been passed a valid identifier
101  auto name = Resource::splitResourceIdentifier(identifier).second;
102  if (!this->hasResource(name))
103  return "";
104  auto absPath = (this->absolute ? std::filesystem::path{this->path} : std::filesystem::current_path().append(this->path)).append(name).string();
105  // Replace cringe Windows-style backslashes
107  return absPath;
108 }
109 
110 void FilesystemResourceProvider::nixifyPath(std::string& path) {
111  std::replace(path.begin(), path.end(), '\\', '/');
112 }
113 
114 std::string FilesystemResourceProvider::getResourceIdentifier(std::string_view absolutePath) {
115  // Add the resource provider prefix
116  if (auto path = FilesystemResourceProvider::getResourceFolderPath(absolutePath); !path.empty())
117  return FILESYSTEM_PROVIDER_NAME + RESOURCE_ID_SEPARATOR.data() + path;
118  return "";
119 }
120 
121 std::string FilesystemResourceProvider::getResourceFolderPath(std::string_view absolutePath) {
122  // Make sure we've been passed a valid resource path
123  if (absolutePath.find(FILESYSTEM_ROOT_FOLDER) == std::string_view::npos)
124  return "";
125 
126  std::string path{absolutePath.data()};
127 
128  // Replace cringe Windows-style backslashes
130 
131  // Remove everything before the root folder, the root folder, and the forward slash
132  auto index = path.rfind(FILESYSTEM_ROOT_FOLDER) + FILESYSTEM_ROOT_FOLDER.size() + 1;
133  if (index > path.length())
134  return "";
135  path = path.substr(index);
136 
137  // Remove the namespace
138  index = path.find('/') + 1;
139  path = path.substr(index);
140  return path;
141 }
142 
143 std::string FilesystemResourceProvider::getResourceAbsolutePath(const std::string& identifier) {
144  if (auto provider = Resource::getResourceProviderWithResource(identifier))
145  return assert_cast<FilesystemResourceProvider*>(provider)->getLocalResourceAbsolutePath(identifier);
146  return "";
147 }
static std::string getResourceIdentifier(std::string_view absolutePath)
Takes an absolute path of a resource file and converts it to a resource identifier.
static void nixifyPath(std::string &path)
Converts all backslashes in a string to forward slashes.
static std::string getResourceAbsolutePath(const std::string &identifier)
Takes a resource identifier and returns the full absolute path, if it exists.
static std::string getResourceFolderPath(std::string_view absolutePath)
Takes an absolute path of a resource folder and converts it to a valid input path for a FilesystemRes...
A chunk of data, usually a file. Is typically cached and shared.
Definition: Resource.h:19