3#include "FileContents.h"
5#include "../Logging/Logger.h"
6#include "../Collections/SmallVector.h"
8#include <emscripten/emscripten.h>
9#include <emscripten/fetch.h>
18 MallocedFileContents(
const uint8_t* ptr,
size_t len,
const std::string& origin,
Cogs::FileContentsHints hints) : FileContents(ptr, len, hints), _origin(origin) {}
20 ~MallocedFileContents() {
30 emscripten_fetch_t* fetch =
nullptr;
32 FetchBackedFileContents(emscripten_fetch_t* fetch,
Cogs::FileContentsHints hints) : FileContents((const uint8_t*)fetch->data, fetch->numBytes, hints), fetch(fetch) {}
34 ~FetchBackedFileContents() {
36 emscripten_fetch_close(fetch);
46 Cogs::FileContents::Callback callback;
58 void(*EmscriptenIO_fetchHandler)(
const char*, UserData*,
double offset,
double size) =
nullptr;
60 void downloadSucceeded(emscripten_fetch_t *fetch) {
61 std::unique_ptr<UserData> userData(
reinterpret_cast<UserData *
>(fetch->userData));
63 LOG_TRACE(logger,
"Fetched %s (%llu bytes)", fetch->url, fetch->numBytes);
65 auto contents = std::make_unique<FetchBackedFileContents>(fetch, userData->hints);
66 userData->callback(std::move(contents));
69 void downloadFailed(emscripten_fetch_t *fetch) {
70 std::unique_ptr<UserData> userData(
reinterpret_cast<UserData *
>(fetch->userData));
72 LOG_ERROR(logger,
"Failed to fetch %s (error=%d, status=%s)", fetch->url, fetch->status, fetch->statusText);
74 emscripten_fetch_close(fetch);
75 userData->callback(std::unique_ptr<Cogs::FileContents>());
79 EMSCRIPTEN_KEEPALIVE
void registerFetchHandler(
void* handler_callback)
81 EmscriptenIO_fetchHandler =
reinterpret_cast<void(*)(
const char*, UserData*,
double offset,
double size)
>(handler_callback);
82 LOG_DEBUG(logger,
"Client fetch handler set (function=%zu)",
size_t(EmscriptenIO_fetchHandler));
85 EMSCRIPTEN_KEEPALIVE
void handleFetchSuccess(
void * ,
const uint8_t* data,
size_t len, UserData* userData)
87 LOG_TRACE(logger,
"Fetched %s (data=%#zX, len=%zu)", userData->name.c_str(),
size_t(data), len);
88 auto contents = std::make_unique<MallocedFileContents>(data, len, userData->name, userData->hints);
89 userData->callback(std::move(contents));
93 EMSCRIPTEN_KEEPALIVE
void handleFetchFailure(
void * , UserData* userData)
95 LOG_ERROR(logger,
"Failed to fetch %s", userData->name.c_str());
96 userData->callback(std::unique_ptr<Cogs::FileContents>());
102void Cogs::IO::readFileAsync(
const std::string & path,
const FileContents::Callback& callback, uint64_t offset, uint64_t size, FileContentsHints hints)
104 if(size == 0 && offset != 0) {
105 LOG_ERROR(logger,
"readFileAsync: Illegal range, trying to read zero bytes from offset (%" PRIu64
")", offset);
106 callback(std::unique_ptr<Cogs::FileContents>());
110 auto userData =
new UserData{ path, callback, hints };
112 if (EmscriptenIO_fetchHandler) {
113 EmscriptenIO_fetchHandler(path.c_str(), userData, offset, size);
118 char rangeBuffer[64];
123 int n = snprintf(rangeBuffer,
sizeof(rangeBuffer),
124 "bytes=%" PRIu64
"-%" PRIu64,
125 offset, offset + std::max(uint64_t(1), size) - 1);
126 if( (n < 0) || (
sizeof(rangeBuffer) <=
static_cast<size_t>(n)) ) {
127 LOG_ERROR(logger,
"snprintf failed for: bytes=%" PRIu64
"-%" PRIu64, offset, offset + std::max(uint64_t(1), size) - 1);
128 callback(std::unique_ptr<Cogs::FileContents>());
131 headers.push_back(
"Range");
132 headers.push_back(rangeBuffer);
133 headers.push_back(
nullptr);
136 emscripten_fetch_attr_t attr;
137 emscripten_fetch_attr_init(&attr);
138 attr.userData = userData;
139 strcpy(attr.requestMethod,
"GET");
140 attr.attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY;
141 attr.onsuccess = downloadSucceeded;
142 attr.onerror = downloadFailed;
143 if(0 < headers.size()) {
145 attr.requestHeaders = headers.data();
147 emscripten_fetch(&attr, path.c_str());
151Cogs::FileContents::Ptr Cogs::IO::readFileSync(
const std::string& , uint64_t , uint64_t , FileContentsHints ) {
152 assert(
false &&
"Should not happen");
156bool Cogs::IO::readTextFile(std::string& ,
const std::string& ) {
160void Cogs::IO::autoExpandEnvironmentVariables(std::string& ) {
163std::string Cogs::IO::expandEnvironmentVariables(
const std::string& input) {
167std::string Cogs::IO::expandEnvironmentVariable(
const std::string& ) {
168 return std::string();
171char Cogs::IO::pathSeparator() {
175bool Cogs::IO::exists(std::string_view ) {
179bool Cogs::IO::isFile(std::string_view ) {
183bool Cogs::IO::isDirectory(std::string_view ) {
187std::string Cogs::IO::absolute(std::string_view path) {
188 return std::string(path);
191std::string Cogs::IO::canonical(std::string_view path) {
192 return std::string(path);
195bool Cogs::IO::remove(std::string_view ) {
199std::string Cogs::IO::combine(std::string_view path, std::string_view extensionPath) {
200 if (path.empty())
return std::string(extensionPath);
203 std::string combined;
204 combined.reserve(path.length() + extensionPath.length() + 1U);
207 if (path.back() !=
'=') {
210 combined += extensionPath;
212 const std::string sep =
"://";
213 size_t sepI = combined.find(sep);
214 sepI = (sepI == std::string::npos) ? 0 : sepI + sep.size();
216 std::string prefix = combined.substr(0, sepI);
217 std::string body = combined.substr(sepI);
219 replaceAll(body,
"\\",
"/");
222 for (
size_t i = 0, n = body.size(); i < n; i++) {
223 body[newSize++] = body[i];
226 while (i + 1 < n && body[i] ==
'/' && body[i + 1] ==
'/') i++;
228 body.resize(newSize);
230 return prefix + body;
233std::string Cogs::IO::expandPath(std::string_view ) {
234 return std::string();
237std::string Cogs::IO::parentPath(std::string_view str) {
238 std::string path(str);
240 replaceAll(path,
"\\",
"/");
241 return path.substr(0, path.find_last_of(
'/'));
244std::string Cogs::IO::relativePath(std::string_view str) {
245 return std::string(str);
248std::string Cogs::IO::relativePath(std::string_view str, std::string_view ) {
249 return std::string(str);
252std::string Cogs::IO::getPathToExe() {
254 return std::string();
257std::string Cogs::IO::getPathRelativeToDll(
const std::string& ,
void* ) {
259 return std::string();
262std::string Cogs::IO::getTempPath() {
263 return std::string();
266std::string Cogs::IO::getAppDataPath() {
270std::string Cogs::IO::getOrgLocalDataPath() {
274bool Cogs::IO::isAbsolute(std::string_view str) {
276 return str.find(
"://") != std::string_view::npos;
279bool Cogs::IO::isRelative(std::string_view str) {
281 return str.find(
"://") == std::string_view::npos;
284std::string Cogs::IO::fileName(std::string_view str) {
285 size_t lastSep = str.find_last_of(
"/\\");
286 return lastSep == std::string_view::npos ? std::string(str) :
std::string(str.substr(lastSep + 1U));
289std::string Cogs::IO::extension(std::string_view str) {
290 size_t found = str.find_last_of(
'.');
291 size_t lastSep = str.find_last_of(
"/\\");
292 if (found == std::string_view::npos) {
293 return std::string();
295 else if (lastSep != std::string_view::npos && lastSep > found) {
296 return std::string();
298 return std::string(str.substr(found));
301std::string Cogs::IO::stem(std::string_view str) {
302 std::string fn = Cogs::IO::fileName(str);
303 std::string ext = Cogs::IO::extension(fn);
304 return fn.substr(0, fn.length() - ext.length());
307void Cogs::IO::replaceAll(std::string& str, std::string_view from, std::string_view to) {
308 if (from.empty())
return;
311 while ((startPos = str.find(from, startPos)) != std::string::npos) {
312 str.replace(startPos, from.length(), to);
313 startPos += to.length();
317Cogs::IO::FileTimeType Cogs::IO::getFileTime(std::string_view ) {
321uint64_t Cogs::IO::getFileSize(std::string_view ) {
325bool Cogs::IO::writeBinaryFile(
const std::string& ,
const void* ,
size_t ) {
329bool Cogs::IO::createDirectories(std::string_view ) {
333Cogs::IO::RecursiveDirIterator Cogs::IO::directoryIterator(std::string_view ) {
337std::string Cogs::IO::getPath(
const DirectoryEntry& entry) {
Log implementation class.
Provides a weakly referenced view over the contents of a string.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Abstract base class storing data read from a file.
virtual Memory::MemoryBuffer take()=0
Take ownership of underlying memorybuffer if exists.
const uint8_t * ptr
Start of buffer storing file data. Use.