1
0
Fork 0
mirror of https://github.com/bjornbytes/lovr.git synced 2024-07-22 21:53:35 +00:00
This commit is contained in:
bjorn 2024-07-07 23:17:00 -07:00
parent 9587dc8532
commit 0ec8cc7c3c
9 changed files with 361 additions and 191 deletions

View file

@ -160,8 +160,9 @@ static int l_lovrDataNewRasterizer(lua_State* L) {
}
Rasterizer* rasterizer = lovrRasterizerCreate(blob, size, luax_readfile);
luax_pushtype(L, Rasterizer, rasterizer);
lovrRelease(blob, lovrBlobDestroy);
luax_assert(L, rasterizer);
luax_pushtype(L, Rasterizer, rasterizer);
lovrRelease(rasterizer, lovrRasterizerDestroy);
return 1;
}

View file

@ -78,7 +78,7 @@ static int luax_loadfile(lua_State* L, const char* path, const char* debug, cons
void* buffer = luax_readfile(path, &size);
if (!buffer) {
lua_pushnil(L);
lua_pushfstring(L, "Could not load file '%s'", path);
lua_pushfstring(L, "Could not read file '%s': %s", path, lovrGetError());
return 2;
}
int status = luax_loadbufferx(L, buffer, size, debug, mode);
@ -92,6 +92,7 @@ static int luax_loadfile(lua_State* L, const char* path, const char* debug, cons
static int l_lovrFilesystemAppend(lua_State* L) {
const char* path = luaL_checkstring(L, 1);
size_t size;
const char* data;
Blob* blob = luax_totype(L, 2, Blob);
@ -103,15 +104,27 @@ static int l_lovrFilesystemAppend(lua_State* L) {
} else {
return luax_typeerror(L, 2, "string or Blob");
}
bool success = lovrFilesystemWrite(path, data, size, true);
lua_pushboolean(L, success);
return 1;
if (lovrFilesystemWrite(path, data, size, true)) {
lua_pushboolean(L, true);
return 1;
} else {
lua_pushboolean(L, false);
lua_pushstring(L, lovrGetError());
return 2;
}
}
static int l_lovrFilesystemCreateDirectory(lua_State* L) {
const char* path = luaL_checkstring(L, 1);
lua_pushboolean(L, lovrFilesystemCreateDirectory(path));
return 1;
if (lovrFilesystemCreateDirectory(path)) {
lua_pushboolean(L, true);
return 1;
} else {
lua_pushboolean(L, false);
lua_pushstring(L, lovrGetError());
return 2;
}
}
static int l_lovrFilesystemGetAppdataDirectory(lua_State* L) {
@ -196,15 +209,15 @@ static int l_lovrFilesystemGetIdentity(lua_State* L) {
static int l_lovrFilesystemGetLastModified(lua_State* L) {
const char* path = luaL_checkstring(L, 1);
uint64_t lastModified = lovrFilesystemGetLastModified(path);
if (lastModified == ~0ull) {
lua_pushnil(L);
uint64_t modtime;
if (lovrFilesystemGetLastModified(path, &modtime)) {
lua_pushinteger(L, modtime);
return 1;
} else {
lua_pushinteger(L, lastModified);
lua_pushnil(L);
lua_pushstring(L, lovrGetError());
return 2;
}
return 1;
}
static int l_lovrFilesystemGetRealDirectory(lua_State* L) {
@ -225,12 +238,15 @@ static int l_lovrFilesystemGetSaveDirectory(lua_State* L) {
static int l_lovrFilesystemGetSize(lua_State* L) {
const char* path = luaL_checkstring(L, 1);
uint64_t size = lovrFilesystemGetSize(path);
if (size == ~0ull) {
return luaL_error(L, "File does not exist"), 0;
uint64_t size;
if (lovrFilesystemGetSize(path, &size)) {
lua_pushinteger(L, size);
return 1;
} else {
lua_pushnil(L);
lua_pushstring(L, lovrGetError());
return 2;
}
lua_pushinteger(L, size);
return 1;
}
static int l_lovrFilesystemGetSource(lua_State* L) {
@ -299,38 +315,60 @@ static int l_lovrFilesystemMount(lua_State* L) {
const char* mountpoint = luaL_optstring(L, 2, NULL);
bool append = lua_toboolean(L, 3);
const char* root = luaL_optstring(L, 4, NULL);
lua_pushboolean(L, lovrFilesystemMount(path, mountpoint, append, root));
return 1;
bool success = lovrFilesystemMount(path, mountpoint, append, root);
if (success) {
lua_pushboolean(L, true);
return 1;
} else {
lua_pushboolean(L, false);
lua_pushstring(L, lovrGetError());
return 2;
}
}
static int l_lovrFilesystemNewBlob(lua_State* L) {
const char* path = luaL_checkstring(L, 1);
size_t size;
void* data = luax_readfile(path, &size);
lovrAssert(data, "Could not load file '%s'", path);
Blob* blob = lovrBlobCreate(data, size, path);
luax_pushtype(L, Blob, blob);
lovrRelease(blob, lovrBlobDestroy);
return 1;
if (data) {
Blob* blob = lovrBlobCreate(data, size, path);
luax_pushtype(L, Blob, blob);
lovrRelease(blob, lovrBlobDestroy);
return 1;
} else {
lua_pushnil(L);
lua_pushstring(L, lovrGetError());
return 2;
}
}
static int l_lovrFilesystemRead(lua_State* L) {
const char* path = luaL_checkstring(L, 1);
size_t size;
void* data = lovrFilesystemRead(path, &size);
if (!data) {
lua_pushnil(L);
if (data) {
lua_pushlstring(L, data, size);
lovrFree(data);
return 1;
} else {
lua_pushnil(L);
lua_pushstring(L, lovrGetError());
return 2;
}
lua_pushlstring(L, data, size);
lovrFree(data);
return 1;
}
static int l_lovrFilesystemRemove(lua_State* L) {
const char* path = luaL_checkstring(L, 1);
lua_pushboolean(L, lovrFilesystemRemove(path));
return 1;
if (lovrFilesystemRemove(path)) {
lua_pushboolean(L, true);
return 1;
} else {
lua_pushnil(L);
lua_pushstring(L, lovrGetError());
return 2;
}
}
static int l_lovrFilesystemSetIdentity(lua_State* L) {
@ -347,7 +385,7 @@ static int l_lovrFilesystemSetRequirePath(lua_State* L) {
static int l_lovrFilesystemSetSource(lua_State* L) {
const char* source = luaL_checkstring(L, 1);
lovrFilesystemSetSource(source);
luax_assert(L, lovrFilesystemSetSource(source));
return 0;
}
@ -388,15 +426,14 @@ static int l_lovrFilesystemWrite(lua_State* L) {
static int l_lovrFilesystemNewFile(lua_State* L) {
const char* path = luaL_checkstring(L, 1);
OpenMode mode = luax_checkenum(L, 2, OpenMode, NULL);
const char* error;
File* file = lovrFileCreate(path, mode, &error);
File* file = lovrFileCreate(path, mode);
if (file) {
luax_pushtype(L, File, file);
lovrRelease(file, lovrFileDestroy);
return 1;
} else {
lua_pushnil(L);
lua_pushstring(L, error);
lua_pushstring(L, lovrGetError());
return 2;
}
}
@ -612,7 +649,7 @@ int luaopen_lovr_filesystem(lua_State* L) {
luax_registerloader(L, luaLoader, 2);
luax_registerloader(L, libLoader, 3);
luax_registerloader(L, libLoaderAllInOne, 4);
lovrFilesystemInit();
luax_assert(L, lovrFilesystemInit());
luax_atexit(L, lovrFilesystemDestroy);
return 1;
}

View file

@ -190,8 +190,31 @@ bool fs_list(const char* path, fs_list_cb* callback, void* context) {
#include <dirent.h>
#include <limits.h>
#include <unistd.h>
#include <errno.h>
bool fs_open(const char* path, char mode, fs_handle* file) {
static int check(int result) {
if (result < 0) {
switch (errno) {
case EACCES: return FS_PERMISSION;
case EPERM: return FS_PERMISSION;
case EROFS: return FS_READ_ONLY;
case EEXIST: return FS_EXISTS;
case ENOENT: return FS_NOT_FOUND;
case EDQUOT: return FS_NO_SPACE;
case ENOSPC: return FS_NO_SPACE;
case ENOTDIR: return FS_NOT_DIR;
case EISDIR: return FS_IS_DIR;
case ELOOP: return FS_LOOP;
case ETXTBSY: return FS_BUSY;
case EIO: return FS_IO;
default: return FS_UNKNOWN_ERROR;
}
} else {
return FS_OK;
}
}
int fs_open(const char* path, char mode, fs_handle* file) {
int flags;
switch (mode) {
case 'r': flags = O_RDONLY; break;
@ -199,94 +222,104 @@ bool fs_open(const char* path, char mode, fs_handle* file) {
case 'a': flags = O_APPEND | O_WRONLY | O_CREAT; break;
default: return false;
}
struct stat stats;
file->fd = open(path, flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
return file->fd >= 0 && !fstat(file->fd, &stats) && !S_ISDIR(stats.st_mode);
return check(file->fd);
}
bool fs_close(fs_handle file) {
return close(file.fd) == 0;
int fs_close(fs_handle file) {
return check(close(file.fd));
}
bool fs_read(fs_handle file, void* data, size_t size, size_t* count) {
int fs_read(fs_handle file, void* data, size_t size, size_t* count) {
ssize_t result = read(file.fd, data, size);
if (result < 0 || result > SSIZE_MAX) {
if (result < 0) {
*count = 0;
return false;
return check(result);
} else {
*count = result;
return true;
return FS_OK;
}
}
bool fs_write(fs_handle file, const void* data, size_t size, size_t* count) {
int fs_write(fs_handle file, const void* data, size_t size, size_t* count) {
ssize_t result = write(file.fd, data, size);
if (result < 0 || result > SSIZE_MAX) {
if (result < 0) {
*count = 0;
return false;
return check(result);
} else {
*count = (size_t) result;
return true;
*count = result;
return FS_OK;
}
}
bool fs_seek(fs_handle file, uint64_t offset) {
return lseek(file.fd, (off_t) offset, SEEK_SET) != (off_t) -1;
int fs_seek(fs_handle file, uint64_t offset) {
return check(lseek(file.fd, (off_t) offset, SEEK_SET));
}
bool fs_fstat(fs_handle file, FileInfo* info) {
int fs_fstat(fs_handle file, FileInfo* info) {
struct stat stats;
if (fstat(file.fd, &stats)) {
return false;
}
int result = fstat(file.fd, &stats);
info->size = (uint64_t) stats.st_size;
info->lastModified = (uint64_t) stats.st_mtime;
info->type = S_ISDIR(stats.st_mode) ? FILE_DIRECTORY : FILE_REGULAR;
return true;
return check(result);
}
void* fs_map(const char* path, size_t* size) {
int fs_map(const char* path, void** pointer, size_t* size) {
int error;
FileInfo info;
if ((error = fs_stat(path, &info)) != FS_OK) {
return error;
}
fs_handle file;
if (!fs_stat(path, &info) || !fs_open(path, 'r', &file)) {
return NULL;
if ((error = fs_open(path, 'r', &file)) != FS_OK) {
return error;
}
*pointer = mmap(NULL, info.size, PROT_READ, MAP_PRIVATE, file.fd, 0);
*size = info.size;
void* data = mmap(NULL, *size, PROT_READ, MAP_PRIVATE, file.fd, 0);
fs_close(file);
return data;
}
bool fs_unmap(void* data, size_t size) {
return munmap(data, size) == 0;
}
bool fs_stat(const char* path, FileInfo* info) {
struct stat stats;
if (stat(path, &stats)) {
return false;
if (*pointer == MAP_FAILED) {
return check(-1);
}
return FS_OK;
}
int fs_unmap(void* data, size_t size) {
return check(munmap(data, size));
}
int fs_stat(const char* path, FileInfo* info) {
struct stat stats;
int result = stat(path, &stats);
info->size = (uint64_t) stats.st_size;
info->lastModified = (uint64_t) stats.st_mtime;
info->type = S_ISDIR(stats.st_mode) ? FILE_DIRECTORY : FILE_REGULAR;
return true;
return check(result);
}
bool fs_remove(const char* path) {
return unlink(path) == 0 || rmdir(path) == 0;
}
bool fs_mkdir(const char* path) {
return mkdir(path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == 0;
}
bool fs_list(const char* path, fs_list_cb* callback, void* context) {
DIR* dir = opendir(path);
if (!dir) {
return false;
int fs_remove(const char* path) {
if (unlink(path)) {
if (errno == EISDIR) {
return check(rmdir(path));
} else {
return check(-1);
}
}
return FS_OK;
}
int fs_mkdir(const char* path) {
return check(mkdir(path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH));
}
int fs_list(const char* path, fs_list_cb* callback, void* context) {
DIR* dir = opendir(path);
if (!dir) return check(-1);
struct dirent* entry;
while ((entry = readdir(dir)) != NULL) {
@ -294,7 +327,7 @@ bool fs_list(const char* path, fs_list_cb* callback, void* context) {
}
closedir(dir);
return true;
return FS_OK;
}
#endif

View file

@ -4,6 +4,21 @@
#pragma once
typedef enum {
FS_OK,
FS_UNKNOWN_ERROR,
FS_PERMISSION,
FS_READ_ONLY,
FS_NOT_FOUND,
FS_EXISTS,
FS_IS_DIR,
FS_NOT_DIR,
FS_LOOP,
FS_NO_SPACE,
FS_BUSY,
FS_IO
} FileError;
typedef enum {
FILE_DIRECTORY,
FILE_REGULAR
@ -18,15 +33,15 @@ typedef struct {
typedef void fs_list_cb(void*, const char*);
typedef union { int fd; void* handle; } fs_handle;
bool fs_open(const char* path, char mode, fs_handle* file);
bool fs_close(fs_handle file);
bool fs_read(fs_handle file, void* data, size_t size, size_t* count);
bool fs_write(fs_handle file, const void* data, size_t size, size_t* count);
bool fs_seek(fs_handle file, uint64_t offset);
bool fs_fstat(fs_handle file, FileInfo* info);
void* fs_map(const char* path, size_t* size);
bool fs_unmap(void* data, size_t size);
bool fs_stat(const char* path, FileInfo* info);
bool fs_remove(const char* path);
bool fs_mkdir(const char* path);
bool fs_list(const char* path, fs_list_cb* callback, void* context);
int fs_open(const char* path, char mode, fs_handle* file);
int fs_close(fs_handle file);
int fs_read(fs_handle file, void* data, size_t size, size_t* count);
int fs_write(fs_handle file, const void* data, size_t size, size_t* count);
int fs_seek(fs_handle file, uint64_t offset);
int fs_fstat(fs_handle file, FileInfo* info);
int fs_map(const char* path, void** pointer, size_t* size);
int fs_unmap(void* data, size_t size);
int fs_stat(const char* path, FileInfo* info);
int fs_remove(const char* path);
int fs_mkdir(const char* path);
int fs_list(const char* path, fs_list_cb* callback, void* context);

View file

@ -41,17 +41,17 @@ static Glyph* lovrRasterizerGetGlyph(Rasterizer* rasterizer, uint32_t codepoint)
return index == MAP_NIL ? NULL : &rasterizer->glyphs.data[index];
}
static Rasterizer* lovrRasterizerCreateTTF(Blob* blob, float size) {
static bool lovrRasterizerCreateTTF(Rasterizer** result, Blob* blob, float size) {
const unsigned char* data = blob ? blob->data : etc_VarelaRound_ttf;
int offset = stbtt_GetFontOffsetForIndex(data, 0);
if (offset == -1) {
return NULL;
return true;
}
stbtt_fontinfo font;
if (!stbtt_InitFont(&font, data, offset)) {
return NULL;
return true;
}
Rasterizer* rasterizer = lovrCalloc(sizeof(Rasterizer));
@ -73,7 +73,8 @@ static Rasterizer* lovrRasterizerCreateTTF(Blob* blob, float size) {
map_init(&rasterizer->kerning, 0);
return rasterizer;
*result = rasterizer;
return true;
}
static int64_t parseNumber(const char* string, size_t length, map_t* map, const char* key) {
@ -115,17 +116,25 @@ static int16_t readi16(const uint8_t* p) { int16_t x; memcpy(&x, p, sizeof(x));
static uint16_t readu16(const uint8_t* p) { uint16_t x; memcpy(&x, p, sizeof(x)); return x; }
static uint32_t readu32(const uint8_t* p) { uint32_t x; memcpy(&x, p, sizeof(x)); return x; }
static Rasterizer* lovrRasterizerCreateBMF(Blob* blob, RasterizerIO* io) {
if (!blob || blob->size < 4) return NULL;
static bool lovrRasterizerCreateBMF(Rasterizer** result, Blob* blob, RasterizerIO* io) {
if (!blob || blob->size < 4) return true;
uint8_t magic[] = { 'B', 'M', 'F' };
bool text = !memcmp(blob->data, "info", 4);
bool binary = !memcmp(blob->data, magic, sizeof(magic));
if (!text && !binary) {
return NULL;
return true;
}
char fullpath[1024];
size_t nameLength = strlen(blob->name);
lovrCheck(nameLength < sizeof(fullpath), "BMFont Blob filename is too long");
memcpy(fullpath, blob->name, nameLength + 1);
char* slash = strrchr(fullpath, '/');
char* filename = slash ? slash + 1 : fullpath;
size_t maxLength = sizeof(fullpath) - 1 - (filename - fullpath);
Rasterizer* rasterizer = lovrCalloc(sizeof(Rasterizer));
rasterizer->ref = 1;
rasterizer->type = RASTERIZER_BMF;
@ -135,14 +144,6 @@ static Rasterizer* lovrRasterizerCreateBMF(Blob* blob, RasterizerIO* io) {
arr_reserve(&rasterizer->glyphs, 36);
map_init(&rasterizer->glyphLookup, 36);
char fullpath[1024];
size_t nameLength = strlen(blob->name);
lovrCheck(nameLength < sizeof(fullpath), "BMFont Blob filename is too long");
memcpy(fullpath, blob->name, nameLength + 1);
char* slash = strrchr(fullpath, '/');
char* filename = slash ? slash + 1 : fullpath;
size_t maxLength = sizeof(fullpath) - 1 - (filename - fullpath);
if (text) {
map_t map;
map_init(&map, 8);
@ -184,11 +185,21 @@ static Rasterizer* lovrRasterizerCreateBMF(Blob* blob, RasterizerIO* io) {
if (!memcmp(tag, "info", tagLength)) {
rasterizer->size = parseNumber(string, lineLength, &map, "size");
} else if (!memcmp(tag, "common", tagLength)) {
lovrCheck(parseNumber(string, lineLength, &map, "pages") == 1, "Currently, BMFont files with multiple images are not supported");
lovrCheck(parseNumber(string, lineLength, &map, "packed") == 0, "Currently, packed BMFont files are not supported");
rasterizer->leading = parseNumber(string, lineLength, &map, "lineHeight");
rasterizer->ascent = parseNumber(string, lineLength, &map, "base");
rasterizer->descent = rasterizer->leading - rasterizer->ascent; // Best effort
if (parseNumber(string, lineLength, &map, "pages") != 1) {
lovrSetError("Currently, BMFont files with multiple images are not supported");
map_free(&map);
goto fail;
}
if (parseNumber(string, lineLength, &map, "packed") != 0) {
lovrSetError("Currently, packed BMFont files are not supported");
map_free(&map);
goto fail;
}
} else if (!memcmp(tag, "page", tagLength)) {
size_t fileLength;
const char* file = parseString(string, lineLength, &map, "file", &fileLength);
@ -293,19 +304,36 @@ static Rasterizer* lovrRasterizerCreateBMF(Blob* blob, RasterizerIO* io) {
size_t atlasSize;
void* atlasData = io(fullpath, &atlasSize);
lovrCheck(atlasData, "Failed to read BMFont image from %s", fullpath);
if (!atlasData) {
lovrSetError("Failed to read BMFont image from %s", fullpath);
goto fail;
}
Blob* atlasBlob = lovrBlobCreate(atlasData, atlasSize, "BMFont atlas");
rasterizer->atlas = lovrImageCreateFromFile(atlasBlob);
lovrRelease(atlasBlob, lovrBlobDestroy);
return rasterizer;
if (!rasterizer->atlas) {
lovrSetError("Failed to load BMFont atlas image: %s", lovrGetError());
goto fail;
}
*result = rasterizer;
return true;
fail:
map_free(&rasterizer->kerning);
arr_free(&rasterizer->glyphs);
map_free(&rasterizer->glyphLookup);
return false;
}
Rasterizer* lovrRasterizerCreate(Blob* blob, float size, RasterizerIO* io) {
Rasterizer* rasterizer = NULL;
if ((rasterizer = lovrRasterizerCreateTTF(blob, size)) != NULL) return rasterizer;
if ((rasterizer = lovrRasterizerCreateBMF(blob, io)) != NULL) return rasterizer;
lovrThrow("Problem loading font: not recognized as TTF or BMFont");
return NULL;
if (!rasterizer && !lovrRasterizerCreateTTF(&rasterizer, blob, size)) return NULL;
if (!rasterizer && !lovrRasterizerCreateBMF(&rasterizer, blob, io)) return NULL;
if (!rasterizer) lovrSetError("Problem loading font: not recognized as TTF or BMFont");
return rasterizer;
}
void lovrRasterizerDestroy(void* ref) {
@ -532,7 +560,7 @@ bool lovrRasterizerGetPixels(Rasterizer* rasterizer, uint32_t codepoint, float*
int id = stbtt_FindGlyphIndex(&rasterizer->font, codepoint);
if (!id || stbtt_IsGlyphEmpty(&rasterizer->font, id)) {
return false;
return true;
}
stbtt_vertex* vertices;

View file

@ -80,14 +80,32 @@ static struct {
size_t savePathLength;
char savePath[1024];
char source[1024];
char requirePath[1024];
char* requirePath;
char identity[64];
} state;
static bool checkfs(int result) {
switch (result) {
case FS_OK: return true;
case FS_UNKNOWN_ERROR: lovrSetError("unknown error"); return false;
case FS_PERMISSION: lovrSetError("permission denied"); return false;
case FS_READ_ONLY: lovrSetError("read only"); return false;
case FS_NOT_FOUND: lovrSetError("not found"); return false;
case FS_EXISTS: lovrSetError("already exists"); return false;
case FS_IS_DIR: lovrSetError("is directory"); return false;
case FS_NOT_DIR: lovrSetError("not a directory"); return false;
case FS_LOOP: lovrSetError("symlink loop"); return false;
case FS_NO_SPACE: lovrSetError("out of space"); return false;
case FS_BUSY: lovrSetError("busy"); return false;
case FS_IO: lovrSetError("io error"); return false;
default: lovrUnreachable();
}
}
// Rejects any path component that would escape the virtual filesystem (./, ../, :, and \)
static bool valid(const char* path) {
if (path[0] == '.' && (path[1] == '\0' || path[1] == '.')) {
return false;
return lovrSetError("Invalid path");
}
do {
@ -97,7 +115,7 @@ static bool valid(const char* path) {
(*path == '/' && path[1] == '.' &&
(path[2] == '.' ? (path[3] == '/' || path[3] == '\0') : (path[2] == '/' || path[2] == '\0')))
) {
return false;
return lovrSetError("Invalid path");
}
} while (*path++ != '\0');
@ -106,7 +124,7 @@ static bool valid(const char* path) {
// Does not work with empty strings
static bool concat(char* buffer, const char* p1, size_t length1, const char* p2, size_t length2) {
if (length1 + 1 + length2 >= LOVR_PATH_MAX) return false;
if (length1 + 1 + length2 >= LOVR_PATH_MAX) return lovrSetError("Path is too long");
memcpy(buffer + length1 + 1, p2, length2);
buffer[length1 + 1 + length2] = '\0';
memcpy(buffer, p1, length1);
@ -133,13 +151,13 @@ static size_t normalize(const char* path, size_t length, char* buffer) {
static bool sanitize(const char* path, char* buffer, size_t* length) {
if (!valid(path)) return false;
size_t pathLength = strlen(path);
if (pathLength >= *length) return false;
if (pathLength >= *length) return lovrSetError("Path is too long");
*length = normalize(path, pathLength, buffer);
return true;
}
bool lovrFilesystemInit(void) {
if (atomic_fetch_add(&state.ref, 1)) return false;
if (atomic_fetch_add(&state.ref, 1)) return true;
lovrFilesystemSetRequirePath("?.lua;?/init.lua");
@ -177,15 +195,17 @@ void lovrFilesystemDestroy(void) {
archive = next;
}
lovrFilesystemUnwatch();
lovrFree(state.requirePath);
memset(&state, 0, sizeof(state));
}
void lovrFilesystemSetSource(const char* source) {
bool lovrFilesystemSetSource(const char* source) {
lovrCheck(!state.source[0], "Source is already set!");
size_t length = strlen(source);
lovrCheck(sizeof(state.source) > length, "Source is too long!");
memcpy(state.source, source, length);
state.source[length] = '\0';
return true;
}
const char* lovrFilesystemGetSource(void) {
@ -225,7 +245,7 @@ void lovrFilesystemWatch(void) {
const char* path = state.source;
#endif
FileInfo info;
if (!watcher.id && fs_stat(path, &info) && info.type == FILE_DIRECTORY) {
if (!watcher.id && fs_stat(path, &info) == FS_OK && info.type == FILE_DIRECTORY) {
dmon_init();
watcher = dmon_watch(path, onFileEvent, DMON_WATCHFLAGS_RECURSIVE, NULL);
}
@ -250,6 +270,7 @@ bool lovrFilesystemIsFused(void) {
bool lovrFilesystemMount(const char* path, const char* mountpoint, bool append, const char* root) {
FOREACH_ARCHIVE(archive) {
if (!strcmp(archive->path, path)) {
lovrSetError("Already mounted");
return false;
}
}
@ -300,6 +321,8 @@ static Archive* archiveStat(const char* p, FileInfo* info, bool needTime) {
if (archiveContains(archive, path, length)) {
if (archive->stat(archive, path, info, needTime)) {
return archive;
} else {
return NULL;
}
} else if (mountpointContains(archive, path, length)) {
info->type = FILE_DIRECTORY;
@ -308,7 +331,11 @@ static Archive* archiveStat(const char* p, FileInfo* info, bool needTime) {
return archive;
}
}
lovrSetError("File not found");
return NULL;
}
return NULL;
}
@ -333,9 +360,14 @@ uint64_t lovrFilesystemGetSize(const char* path) {
return archiveStat(path, &info, false) && info.type == FILE_REGULAR ? info.size : 0;
}
uint64_t lovrFilesystemGetLastModified(const char* path) {
bool lovrFilesystemGetLastModified(const char* path, uint64_t* modtime) {
FileInfo info;
return archiveStat(path, &info, true) ? info.lastModified : ~0ull;
if (archiveStat(path, &info, true)) {
*modtime = info.lastModified;
return true;
} else {
return false;
}
}
void* lovrFilesystemRead(const char* p, size_t* size) {
@ -349,13 +381,19 @@ void* lovrFilesystemRead(const char* p, size_t* size) {
}
if (!archive->open(archive, path, &handle)) {
continue;
return NULL;
}
uint64_t bytes;
if (!archive->fsize(archive, &handle, &bytes) || bytes > SIZE_MAX) {
if (!archive->fsize(archive, &handle, &bytes)) {
archive->close(archive, &handle);
continue;
return NULL;
}
if (bytes > SIZE_MAX) {
archive->close(archive, &handle);
lovrSetError("File is too big");
return NULL;
}
*size = (size_t) bytes;
@ -364,10 +402,14 @@ void* lovrFilesystemRead(const char* p, size_t* size) {
if (archive->read(archive, &handle, data, *size, size)) {
archive->close(archive, &handle);
return data;
} else {
archive->close(archive, &handle);
lovrFree(data);
return NULL;
}
archive->close(archive, &handle);
}
lovrSetError("File not found");
}
return NULL;
}
@ -401,8 +443,13 @@ const char* lovrFilesystemGetIdentity(void) {
bool lovrFilesystemSetIdentity(const char* identity, bool precedence) {
size_t length = strlen(identity);
// Identity can only be set once, and can't be empty
if (state.identity[0] != '\0' || length == 0) {
if (state.identity[0] != '\0') {
lovrSetError("Identity is already set");
return false;
}
if (length == 0) {
lovrSetError("Identity can not be empty");
return false;
}
@ -411,11 +458,13 @@ bool lovrFilesystemSetIdentity(const char* identity, bool precedence) {
// If the data path was too long or unavailable, fail
if (cursor == 0) {
lovrSetError("Could not get appdata path");
return false;
}
// Make sure there is enough room to tack on /LOVR/<identity>
if (cursor + 1 + strlen("LOVR") + 1 + length >= sizeof(state.savePath)) {
lovrSetError("Identity path is too long");
return false;
}
@ -433,14 +482,15 @@ bool lovrFilesystemSetIdentity(const char* identity, bool precedence) {
// mkdir -p
FileInfo info;
if (!fs_stat(state.savePath, &info)) {
if (fs_stat(state.savePath, &info) != FS_OK) {
for (char* slash = strchr(state.savePath, SLASH); slash; slash = strchr(slash + 1, SLASH)) {
*slash = '\0';
fs_mkdir(state.savePath);
*slash = SLASH;
}
if (!fs_mkdir(state.savePath)) {
if (!checkfs(fs_mkdir(state.savePath))) {
lovrSetError("Failed to create identity folder: %s", lovrGetError());
return false;
}
}
@ -463,7 +513,6 @@ const char* lovrFilesystemGetSaveDirectory(void) {
bool lovrFilesystemCreateDirectory(const char* path) {
char resolved[LOVR_PATH_MAX];
if (!valid(path) || !concat(resolved, state.savePath, state.savePathLength, path, strlen(path))) {
return false;
}
@ -480,12 +529,16 @@ bool lovrFilesystemCreateDirectory(const char* path) {
cursor++;
}
return fs_mkdir(resolved);
return checkfs(fs_mkdir(resolved));
}
bool lovrFilesystemRemove(const char* path) {
char resolved[LOVR_PATH_MAX];
return valid(path) && concat(resolved, state.savePath, state.savePathLength, path, strlen(path)) && fs_remove(resolved);
if (!valid(path) || !concat(resolved, state.savePath, state.savePathLength, path, strlen(path))) {
return false;
}
return checkfs(fs_remove(resolved));
}
bool lovrFilesystemWrite(const char* path, const char* content, size_t size, bool append) {
@ -495,16 +548,21 @@ bool lovrFilesystemWrite(const char* path, const char* content, size_t size, boo
}
fs_handle file;
if (!fs_open(resolved, append ? 'a' : 'w', &file)) {
if (!checkfs(fs_open(resolved, append ? 'a' : 'w', &file))) {
return false;
}
size_t count;
if (!fs_write(file, content, size, &count) || count != size) {
if (!checkfs(fs_write(file, content, size, &count))) {
return false;
}
return fs_close(file);
if (count != size) {
lovrSetError("Incomplete write");
return false;
}
return checkfs(fs_close(file));
}
// Paths
@ -535,9 +593,9 @@ const char* lovrFilesystemGetRequirePath(void) {
void lovrFilesystemSetRequirePath(const char* requirePath) {
size_t length = strlen(requirePath);
lovrCheck(length < sizeof(state.requirePath), "Require path is too long");
memcpy(state.requirePath, requirePath, length);
state.requirePath[length] = '\0';
lovrFree(state.requirePath);
state.requirePath = lovrMalloc(length + 1);
memcpy(state.requirePath, requirePath, length + 1);
}
// Archive: dir
@ -549,48 +607,49 @@ static bool dir_resolve(Archive* archive, const char* fullpath, char* buffer) {
static bool dir_init(Archive* archive, const char* path, const char* root) {
FileInfo info;
return fs_stat(path, &info) && info.type == FILE_DIRECTORY;
return checkfs(fs_stat(path, &info)) && info.type == FILE_DIRECTORY;
}
static bool dir_open(Archive* archive, const char* path, Handle* handle) {
char resolved[LOVR_PATH_MAX];
return dir_resolve(archive, path, resolved) && fs_open(resolved, 'r', &handle->file);
return dir_resolve(archive, path, resolved) && checkfs(fs_open(resolved, 'r', &handle->file));
}
static bool dir_close(Archive* archive, Handle* handle) {
return fs_close(handle->file);
return checkfs(fs_close(handle->file));
}
static bool dir_read(Archive* archive, Handle* handle, uint8_t* data, size_t size, size_t* count) {
if (!fs_read(handle->file, data, size, count)) {
return false;
} else {
if (checkfs(fs_read(handle->file, data, size, count))) {
handle->offset += *count;
return true;
} else {
return false;
}
}
static bool dir_seek(Archive* archive, Handle* handle, uint64_t offset) {
if (fs_seek(handle->file, offset)) {
if (checkfs(fs_seek(handle->file, offset))) {
handle->offset = offset;
return true;
} else {
return false;
}
return false;
}
static bool dir_fsize(Archive* archive, Handle* handle, uint64_t* size) {
FileInfo info;
if (!fs_fstat(handle->file, &info)) {
return false;
} else {
if (checkfs(fs_fstat(handle->file, &info))) {
*size = info.size;
return true;
} else {
return false;
}
}
static bool dir_stat(Archive* archive, const char* path, FileInfo* info, bool needTime) {
char resolved[LOVR_PATH_MAX];
return dir_resolve(archive, path, resolved) && fs_stat(resolved, info);
return dir_resolve(archive, path, resolved) && checkfs(fs_stat(resolved, info));
}
static void dir_list(Archive* archive, const char* path, fs_list_cb callback, void* context) {
@ -613,9 +672,7 @@ static void zip_free(Archive* archive) {
static bool zip_init(Archive* archive, const char* filename, const char* root) {
// Map the zip file into memory
archive->data = fs_map(filename, &archive->size);
if (!archive->data) {
zip_free(archive);
if (!checkfs(fs_map(filename, (void**) &archive->data, &archive->size))) {
return false;
}
@ -886,6 +943,7 @@ static bool zip_read(Archive* archive, Handle* handle, uint8_t* data, size_t siz
}
if (!decompress(node, stream, NULL, handle->offset - stream->outputCursor, NULL)) {
lovrSetError("Could not decompress file");
return false;
}
}
@ -912,6 +970,7 @@ static bool zip_read(Archive* archive, Handle* handle, uint8_t* data, size_t siz
return true;
}
lovrSetError("Could not decompress file");
return false;
}
@ -1011,11 +1070,11 @@ void lovrArchiveDestroy(void* ref) {
// File
File* lovrFileCreate(const char* p, OpenMode mode, const char** error) {
File* lovrFileCreate(const char* p, OpenMode mode) {
char path[1024];
size_t length = sizeof(path);
if (!sanitize(p, path, &length)) {
return *error = "File path is too long or contains invalid characters", NULL;
return NULL;
}
Handle handle = { 0 };
@ -1024,24 +1083,23 @@ File* lovrFileCreate(const char* p, OpenMode mode, const char** error) {
if (mode == OPEN_READ) {
FOREACH_ARCHIVE(a) {
if (archiveContains(a, path, length)) {
if (a->open(a, path, &handle)) {
archive = a;
break;
if (!a->open(a, path, &handle)) {
return NULL;
}
archive = a;
break;
}
}
if (!archive) {
return *error = "File does not exist", NULL;
}
lovrAssert(archive, "File not found");
} else {
char fullpath[LOVR_PATH_MAX];
if (!concat(fullpath, state.savePath, state.savePathLength, path, length)) {
return *error = "File path is too long", NULL;
return NULL;
}
if (!fs_open(fullpath, mode == OPEN_APPEND ? 'a' : 'w', &handle.file)) {
return *error = "Failed to open file for writing", NULL; // TODO better errors pl0x
if (!checkfs(fs_open(fullpath, mode == OPEN_APPEND ? 'a' : 'w', &handle.file))) {
return NULL;
}
}
@ -1050,11 +1108,9 @@ File* lovrFileCreate(const char* p, OpenMode mode, const char** error) {
file->mode = mode;
file->handle = handle;
file->archive = archive;
lovrRetain(archive);
file->path = lovrMalloc(length + 1);
memcpy(file->path, path, length + 1);
lovrRetain(archive);
return file;
}
@ -1074,9 +1130,8 @@ OpenMode lovrFileGetMode(File* file) {
return file->mode;
}
uint64_t lovrFileGetSize(File* file) {
uint64_t size;
return file->archive->fsize(file->archive, &file->handle, &size) ? size : ~0ull;
bool lovrFileGetSize(File* file, uint64_t* size) {
return file->archive->fsize(file->archive, &file->handle, &size);
}
bool lovrFileRead(File* file, void* data, size_t size, size_t* count) {
@ -1086,7 +1141,7 @@ bool lovrFileRead(File* file, void* data, size_t size, size_t* count) {
bool lovrFileWrite(File* file, const void* data, size_t size, size_t* count) {
lovrCheck(file->mode != OPEN_READ, "File was not opened for writing");
return fs_write(file->handle.file, data, size, count);
return checkfs(fs_write(file->handle.file, data, size, count));
}
bool lovrFileSeek(File* file, uint64_t offset) {

View file

@ -18,7 +18,7 @@ typedef enum {
bool lovrFilesystemInit(void);
void lovrFilesystemDestroy(void);
void lovrFilesystemSetSource(const char* source);
bool lovrFilesystemSetSource(const char* source);
const char* lovrFilesystemGetSource(void);
bool lovrFilesystemIsFused(void);
void lovrFilesystemWatch(void);
@ -28,8 +28,8 @@ bool lovrFilesystemUnmount(const char* path);
const char* lovrFilesystemGetRealDirectory(const char* path);
bool lovrFilesystemIsFile(const char* path);
bool lovrFilesystemIsDirectory(const char* path);
uint64_t lovrFilesystemGetSize(const char* path);
uint64_t lovrFilesystemGetLastModified(const char* path);
bool lovrFilesystemGetSize(const char* path, uint64_t* size);
bool lovrFilesystemGetLastModified(const char* path, uint64_t* modtime);
void* lovrFilesystemRead(const char* path, size_t* size);
void lovrFilesystemGetDirectoryItems(const char* path, void (*callback)(void* context, const char* path), void* context);
const char* lovrFilesystemGetIdentity(void);
@ -59,7 +59,7 @@ typedef enum {
OPEN_APPEND
} OpenMode;
File* lovrFileCreate(const char* path, OpenMode mode, const char** error);
File* lovrFileCreate(const char* path, OpenMode mode);
void lovrFileDestroy(void* ref);
const char* lovrFileGetPath(File* file);
OpenMode lovrFileGetMode(File* file);

View file

@ -57,11 +57,12 @@ const char* lovrGetError(void) {
return error;
}
void lovrSetError(const char* format, ...) {
int lovrSetError(const char* format, ...) {
va_list args;
va_start(args, format);
vsnprintf(error, sizeof(error), format, args);
va_end(args);
return false;
}
// Exceptions

View file

@ -40,7 +40,7 @@ void lovrRelease(void* ref, void (*destructor)(void*));
// Errors
const char* lovrGetError(void);
void lovrSetError(const char* format, ...);
int lovrSetError(const char* format, ...);
#define lovrAssert(c, ...) do { if (!(c)) { lovrSetError(__VA_ARGS__); return 0; } } while (0)
#ifdef LOVR_UNCHECKED