Mercurial > molko
changeset 641:fcd124e513ea
core: reintroduce VFS
author | David Demelier <markand@malikania.fr> |
---|---|
date | Sun, 01 Oct 2023 09:18:01 +0200 |
parents | 9850089c9671 |
children | 05b585720d3b |
files | CMakeLists.txt doc/Doxyfile libmlk-core/mlk/core/font.c libmlk-core/mlk/core/font.h libmlk-core/mlk/core/image.c libmlk-core/mlk/core/image.h libmlk-core/mlk/core/music.c libmlk-core/mlk/core/music.h libmlk-core/mlk/core/sound.c libmlk-core/mlk/core/sound.h libmlk-core/mlk/core/vfs-dir.c libmlk-core/mlk/core/vfs-dir.h libmlk-core/mlk/core/vfs-zip.c libmlk-core/mlk/core/vfs-zip.h libmlk-core/mlk/core/vfs.c libmlk-core/mlk/core/vfs.h tests/test-vfs-dir.c tests/test-vfs-zip.c |
diffstat | 18 files changed, 632 insertions(+), 162 deletions(-) [+] |
line wrap: on
line diff
--- a/CMakeLists.txt Wed Sep 27 22:03:17 2023 +0200 +++ b/CMakeLists.txt Sun Oct 01 09:18:01 2023 +0200 @@ -128,6 +128,7 @@ FILES ${molko_BINARY_DIR}/cmake/MlkOptions.cmake ${molko_SOURCE_DIR}/cmake/FindSndFile.cmake + ${molko_SOURCE_DIR}/cmake/FindZIP.cmake ${molko_SOURCE_DIR}/cmake/MlkBcc.cmake ${molko_SOURCE_DIR}/cmake/MlkMap.cmake ${molko_SOURCE_DIR}/cmake/MlkTileset.cmake
--- a/doc/Doxyfile Wed Sep 27 22:03:17 2023 +0200 +++ b/doc/Doxyfile Sun Oct 01 09:18:01 2023 +0200 @@ -40,6 +40,7 @@ libmlk-core/mlk/core/core_p.h \ libmlk-core/mlk/core/sys_p.h \ libmlk-core/mlk/core/texture_p.h \ + libmlk-core/mlk/core/vfs_p.h \ libmlk-core/mlk/core/window_p.h \ libmlk-ui/mlk/ui/ui_p.h
--- a/libmlk-core/mlk/core/font.c Wed Sep 27 22:03:17 2023 +0200 +++ b/libmlk-core/mlk/core/font.c Sun Oct 01 09:18:01 2023 +0200 @@ -26,6 +26,8 @@ #include "font.h" #include "texture_p.h" #include "util.h" +#include "vfs.h" +#include "vfs_p.h" int mlk_font_open(struct mlk_font *font, const char *path, unsigned int size) @@ -58,6 +60,22 @@ } int +mlk_font_openvfs(struct mlk_font *font, struct mlk_vfs_file *file, unsigned int size) +{ + assert(font); + assert(file); + + SDL_RWops *ops; + + if (!(ops = mlk__vfs_to_rw(file))) + return -1; + if (!(font->handle = TTF_OpenFontRW(ops, 1, size))) + return mlk_errf("%s", SDL_GetError()); + + return 0; +} + +int mlk_font_render(struct mlk_font *font, struct mlk_texture *tex, const char *text, unsigned long color) { assert(font);
--- a/libmlk-core/mlk/core/font.h Wed Sep 27 22:03:17 2023 +0200 +++ b/libmlk-core/mlk/core/font.h Sun Oct 01 09:18:01 2023 +0200 @@ -29,6 +29,7 @@ #include <stddef.h> struct mlk_texture; +struct mlk_vfs_file; /** * \enum mlk_font_style @@ -83,7 +84,7 @@ * \param font the font to open * \param path path to the font file (e.g. .ttf, .otf, etc) * \param size desired font height in pixels - * \return 0 on success or an error code on failure + * \return 0 on success or -1 on error */ int mlk_font_open(struct mlk_font *font, const char *path, unsigned int size); @@ -98,7 +99,7 @@ * \param data the font content * \param datasz the font content length * \param size desired font height in pixels - * \return 0 on success or an error code on failure + * \return 0 on success or -1 on error */ int mlk_font_openmem(struct mlk_font *font, @@ -107,19 +108,34 @@ unsigned int size); /** + * Open a font from a virtual file system. + * + * The VFS file can be discarded after loading the font. + * + * \pre font != NULL + * \pre file != NULL + * \param font the font to initialize + * \param file the VFS file + * \param size desired font height in pixels + * \return 0 on success or -1 on error + */ +int +mlk_font_openvfs(struct mlk_font *font, struct mlk_vfs_file *file, unsigned int size); + +/** * Render some text using the font and generate a texture. * * The texture destination must be deallocated once no longer used using * ::mlk_texture_finish. * - * \pre mlk_font_ok(font) + * \pre font != NULL * \pre texture != NULL * \pre text != NULL && strlen(text) > 0 * \param font the font to use * \param texture the texture to initialize * \param text the non NULL and non empty UTF-8 text * \param color foreground color - * \return 0 on success or an error code on failure + * \return 0 on success or -1 on error */ int mlk_font_render(struct mlk_font *font, @@ -130,7 +146,7 @@ /** * Return the font height in pixels * - * \pre mlk_font_ok(font) + * \pre font != NULL * \param font the font to use * \return the font height */ @@ -144,12 +160,12 @@ * * If the function fails, *w and *h are set to 0. * - * \pre mlk_font_ok(font) + * \pre font != NULL * \param font the font to use * \param text the non NULL and non empty UTF-8 text * \param w pointer receiving width (or NULL) * \param h pointer receiving height (or NULL) - * \return 0 on success or an error code on failure + * \return 0 on success or -1 on error */ int mlk_font_query(const struct mlk_font *font,
--- a/libmlk-core/mlk/core/image.c Wed Sep 27 22:03:17 2023 +0200 +++ b/libmlk-core/mlk/core/image.c Sun Oct 01 09:18:01 2023 +0200 @@ -22,6 +22,8 @@ #include "err.h" #include "texture.h" +#include "vfs.h" +#include "vfs_p.h" #include "window.h" #include "window_p.h" @@ -55,6 +57,7 @@ int mlk_image_openmem(struct mlk_texture *tex, const void *buffer, size_t size) { + assert(tex); assert(buffer); SDL_RWops *ops = SDL_RWFromConstMem(buffer, size); @@ -66,3 +69,21 @@ return 0; } + +int +mlk_image_openvfs(struct mlk_texture *tex, struct mlk_vfs_file *file) +{ + assert(tex); + assert(file); + + SDL_RWops *ops; + + if (!(ops = mlk__vfs_to_rw(file))) + return -1; + if (!(tex->handle = IMG_LoadTexture_RW(MLK__RENDERER(), ops, 1))) + return mlk_errf("%s", SDL_GetError()); + + dimensions(tex); + + return 0; +}
--- a/libmlk-core/mlk/core/image.h Wed Sep 27 22:03:17 2023 +0200 +++ b/libmlk-core/mlk/core/image.h Sun Oct 01 09:18:01 2023 +0200 @@ -27,6 +27,7 @@ #include <stddef.h> struct mlk_texture; +struct mlk_vfs_file; #if defined(__cplusplus) extern "C" { @@ -60,6 +61,20 @@ int mlk_image_openmem(struct mlk_texture *texture, const void *data, size_t datasz); +/** + * Open an image from a virtual file system. + * + * The VFS file can be discarded after loading the image. + * + * \pre texture != NULL + * \pre file != NULL + * \param texture the texture to initialize + * \param file the VFS file + * \return 0 on success or -1 on error + */ +int +mlk_image_openvfs(struct mlk_texture *texture, struct mlk_vfs_file *file); + #if defined(__cplusplus) } #endif
--- a/libmlk-core/mlk/core/music.c Wed Sep 27 22:03:17 2023 +0200 +++ b/libmlk-core/mlk/core/music.c Sun Oct 01 09:18:01 2023 +0200 @@ -19,9 +19,12 @@ #include <assert.h> #include <string.h> +#include "alloc.h" #include "err.h" #include "music.h" #include "sys_p.h" +#include "vfs.h" +#include "vfs_p.h" #define SOURCE(mus) ((const struct mlk__audiostream *)mus->handle)->source @@ -44,6 +47,26 @@ } int +mlk_music_openvfs(struct mlk_music *music, struct mlk_vfs_file *file) +{ + assert(music); + assert(file); + + char *data; + size_t datasz; + int ret = 0; + + if (!(data = mlk_vfs_file_read_all(file, &datasz))) + return -1; + if (mlk__audiostream_openmem((struct mlk__audiostream **)&music->handle, data, datasz) < 0) + ret = -1; + + mlk_alloc_free(data); + + return ret; +} + +int mlk_music_ok(const struct mlk_music *mus) { return mus && mus->handle;
--- a/libmlk-core/mlk/core/music.h Wed Sep 27 22:03:17 2023 +0200 +++ b/libmlk-core/mlk/core/music.h Sun Oct 01 09:18:01 2023 +0200 @@ -29,6 +29,8 @@ #include <stddef.h> +struct mlk_vfs_file; + /** * \enum mlk_music_flags * \brief Music flags @@ -70,7 +72,7 @@ * \pre path != NULL * \param music the music to initialize * \param path the path to the music file (e.g. .ogg, .wav, .mp3, etc) - * \return 0 on success or an error code on failure + * \return 0 on success or -1 on error */ int mlk_music_open(struct mlk_music *music, const char *path); @@ -83,29 +85,34 @@ * \pre music != NULL * \pre path != NULL * \param music the music to initialize - * \param data the font content - * \param datasz the font content length - * \return 0 on success or an error code on failure + * \param data the music data + * \param datasz the music data length + * \return 0 on success or -1 on error */ int mlk_music_openmem(struct mlk_music *music, const void *data, size_t datasz); /** - * Tells if the music structure is usable. + * Open a music from a virtual file system. + * + * The VFS file can be discarded after loading the sound. * - * \param music the music to check - * \return non-zero if the music structure is usable + * \pre music != NULL + * \pre file != NULL + * \param music the music to initialize + * \param file the VFS file + * \return 0 on success or -1 on error */ int -mlk_music_ok(const struct mlk_music *music); +mlk_music_openvfs(struct mlk_music *music, struct mlk_vfs_file *file); /** * Start playing the music. * - * \pre mlk_music_ok(music) + * \pre music != NULL * \param music the music to play * \param flags optional flags to pass - * \return 0 on success or an error code on failure + * \return 0 on success or -1 on error */ int mlk_music_play(struct mlk_music *music, enum mlk_music_flags flags); @@ -113,7 +120,7 @@ /** * Pause the music playback. * - * \pre mlk_music_ok(music) + * \pre music != NULL * \param music the music to pause * \sa ::mlk_music_resume */ @@ -123,7 +130,7 @@ /** * Resume the music where it was stopped. * - * \pre mlk_music_ok(music) + * \pre music != NULL * \param music the music to resume * \sa ::mlk_music_pause */ @@ -135,7 +142,7 @@ * * Calling ::mlk_music_resume on it will restart from the beginning. * - * \pre mlk_music_ok(music) + * \pre music != NULL * \param music the music to stop * \sa ::mlk_music_resume * \sa ::mlk_music_play @@ -148,7 +155,7 @@ * * If the music is being played, it is stopped immediately. * - * \pre mlk_music_ok(music) + * \pre music != NULL * \param music the music to destroy */ void
--- a/libmlk-core/mlk/core/sound.c Wed Sep 27 22:03:17 2023 +0200 +++ b/libmlk-core/mlk/core/sound.c Sun Oct 01 09:18:01 2023 +0200 @@ -20,8 +20,10 @@ #include <stdio.h> #include <string.h> +#include "alloc.h" #include "sound.h" #include "sys_p.h" +#include "vfs.h" #define SOURCE(snd) ((const struct mlk__audiostream *)snd->handle)->source @@ -44,15 +46,29 @@ } int -mlk_sound_ok(const struct mlk_sound *snd) +mlk_sound_openvfs(struct mlk_sound *snd, struct mlk_vfs_file *file) { - return snd && snd->handle; + assert(snd); + assert(file); + + char *data; + size_t datasz; + int ret = 0; + + if (!(data = mlk_vfs_file_read_all(file, &datasz))) + return -1; + if (mlk__audiostream_openmem((struct mlk__audiostream **)&snd->handle, data, datasz) < 0) + ret = -1; + + mlk_alloc_free(data); + + return ret; } int mlk_sound_play(struct mlk_sound *snd) { - assert(mlk_sound_ok(snd)); + assert(snd); alSourcePlay(SOURCE(snd)); @@ -62,7 +78,7 @@ void mlk_sound_pause(struct mlk_sound *snd) { - assert(mlk_sound_ok(snd)); + assert(snd); alSourcePause(SOURCE(snd)); } @@ -70,7 +86,7 @@ void mlk_sound_resume(struct mlk_sound *snd) { - assert(mlk_sound_ok(snd)); + assert(snd); alSourcePlay(SOURCE(snd)); } @@ -78,7 +94,7 @@ void mlk_sound_stop(struct mlk_sound *snd) { - assert(mlk_sound_ok(snd)); + assert(snd); alSourceStop(SOURCE(snd)); }
--- a/libmlk-core/mlk/core/sound.h Wed Sep 27 22:03:17 2023 +0200 +++ b/libmlk-core/mlk/core/sound.h Sun Oct 01 09:18:01 2023 +0200 @@ -31,6 +31,8 @@ #include <stddef.h> +struct mlk_vfs_file; + /** * \struct mlk_music * \brief Music structure @@ -54,7 +56,7 @@ * \pre path != NULL * \param sound the sound to initialize * \param path the path to the music file (e.g. .ogg, .wav, .mp3, etc) - * \return 0 on success or an error code on failure + * \return 0 on success or -1 on error */ int mlk_sound_open(struct mlk_sound *sound, const char *path); @@ -67,28 +69,33 @@ * \pre music != NULL * \pre path != NULL * \param sound the sound to initialize - * \param data the font content - * \param datasz the font content length - * \return 0 on success or an error code on failure + * \param data the sound data + * \param datasz the sound data length + * \return 0 on success or -1 on error */ int mlk_sound_openmem(struct mlk_sound *sound, const void *data, size_t datasz); /** - * Tells if the sound structure is usable. + * Open a sound from a virtual file system. + * + * The VFS file can be discarded after loading the sound. * - * \param sound the sound to check - * \return non-zero if the sound structure is usable + * \pre sound != NULL + * \pre file != NULL + * \param sound the sound to initialize + * \param file the VFS file + * \return 0 on success or -1 on error */ int -mlk_sound_ok(const struct mlk_sound *sound); +mlk_sound_openvfs(struct mlk_sound *snd, struct mlk_vfs_file *file); /** * Start playing the sound. * - * \pre mlk_sound_ok(sound) + * \pre sound != NULL * \param sound the sound to play - * \return 0 on success or an error code on failure + * \return 0 on success or -1 on error */ int mlk_sound_play(struct mlk_sound *sound); @@ -96,7 +103,7 @@ /** * Pause the sound playback. * - * \pre mlk_sound_ok(sound) + * \pre sound != NULL * \param sound the sound to pause * \sa ::mlk_sound_resume */ @@ -106,7 +113,7 @@ /** * Resume the sound where it was stopped. * - * \pre mlk_sound_ok(sound) + * \pre sound != NULL * \param sound the sound to resume * \sa ::mlk_sound_pause */ @@ -118,7 +125,7 @@ * * Calling ::mlk_sound_resume on it will restart from the beginning. * - * \pre mlk_sound_ok(sound) + * \pre sound != NULL * \param sound the sound to stop * \sa ::mlk_sound_resume * \sa ::mlk_sound_play
--- a/libmlk-core/mlk/core/vfs-dir.c Wed Sep 27 22:03:17 2023 +0200 +++ b/libmlk-core/mlk/core/vfs-dir.c Sun Oct 01 09:18:01 2023 +0200 @@ -38,7 +38,9 @@ static inline void normalize(char *path) { - size_t len = strlen(path); + size_t len; + + len = strlen(path); for (char *p = path; *p; ++p) if (*p == '\\') @@ -48,73 +50,129 @@ path[--len] = 0; } +/* + * TODO + * + * Add an optional POSIX implementation based on open(2) if function is + * available. + */ +static FILE * +makefile(const char *path, const char *mode) +{ + FILE *fp; + const char *fmode = NULL; + int r = 0, w = 0, e = 0, t = 0, seek = 0, flags, whence; + long offset; + size_t len; + + /* + * Determine which read-write mode we need, if unset by the user assumes + * read-only for convenience. + * + * Conversion as following: + * + * | mode | fopen mode | fseek? | tewr | + * |-------+------------+---------+-------------| + * | r | rb | no | 0001 (0x01) | + * | w | r+b | yes, 0 | 0010 (0x02) | + * | we | r+b | yes, -1 | 0110 (0x06) | + * | rw | r+b | yes, 0 | 0011 (0x03) | + * | rwe | r+b | yes, -1 | 0111 (0x07) | + * | wt | wb | no | 1010 (0x0a) | + * | rwt | w+b | no | 1011 (0x0b) | + * + * Notes: + * + * Standard C function fopen does not have support to open a file for + * writing only at the beginning (without destroying content) so we have + * to read-write with a call to fseek if at-end is requested. The mode + * 'a' is never applicable because it always append to the file no + * matter fseek position. + * + * We could use open(2) POSIX function but that would make the code less + * portable. + */ + len = strlen(mode); + + r = strcspn(mode, "r") != len; + w = strcspn(mode, "w") != len; + e = strcspn(mode, "e") != len; + t = strcspn(mode, "t") != len; + + /* For simplicity, convert the individual open mode into a bitmask. */ + flags = r << 0 | w << 1 | e << 2 | t << 3; + + switch (flags) { + case 0x01: + fmode = "rb"; + break; + case 0x02: + case 0x03: + fmode = "r+b"; + seek = 1; + offset = 0; + whence = SEEK_SET; + break; + case 0x06: + case 0x07: + fmode = "r+b"; + seek = 1; + offset = 0; + whence = SEEK_END; + break; + case 0x0a: + fmode = "wb"; + break; + case 0x0b: + fmode = "w+b"; + break; + default: + mlk_errf("incompatible modes specified"); + return NULL; + } + + if (!(fp = fopen(path, fmode))) { + mlk_errf("%s", strerror(errno)); + return NULL; + } + + if (seek && fseek(fp, offset, whence) < 0) { + fclose(fp); + mlk_errf("%s", strerror(errno)); + return NULL; + } + + return fp; +} + static size_t file_read(struct mlk_vfs_file *self, void *buf, size_t bufsz) { - struct mlk_vfs_dir_file *file = MLK_VFS_DIR_FILE(self); - size_t rv; - - rv = fread(buf, 1, bufsz, file->handle); - - if (ferror(file->handle)) - return -1; - - return rv; + return mlk_vfs_dir_file_read(MLK_VFS_DIR_FILE(self), buf, bufsz); } static size_t file_write(struct mlk_vfs_file *self, const void *buf, size_t bufsz) { - struct mlk_vfs_dir_file *file = MLK_VFS_DIR_FILE(self); - size_t rv; - - rv = fwrite(buf, 1, bufsz, file->handle); - - if (ferror(file->handle)) - return -1; - - return rv; + return mlk_vfs_dir_file_write(MLK_VFS_DIR_FILE(self), buf, bufsz); } static int file_flush(struct mlk_vfs_file *self) { - struct mlk_vfs_dir_file *file = MLK_VFS_DIR_FILE(self); - - return fflush(file->handle) == EOF ? -1 : 0; + return mlk_vfs_dir_file_flush(MLK_VFS_DIR_FILE(self)); } static void -file_free(struct mlk_vfs_file *self) +file_finish(struct mlk_vfs_file *self) { - struct mlk_vfs_dir_file *file = MLK_VFS_DIR_FILE(self); - - fclose(file->handle); - mlk_alloc_free(file); + mlk_vfs_dir_file_finish(MLK_VFS_DIR_FILE(self)); } static struct mlk_vfs_file * vfs_open(struct mlk_vfs *self, const char *entry, const char *mode) { - struct mlk_vfs_dir *dir = MLK_VFS_DIR(self); - struct mlk_vfs_dir_file *file; - - file = mlk_alloc_new0(1, sizeof (*file)); - - snprintf(file->path, sizeof (file->path), "%s/%s", dir->path, entry); - - if (!(file->handle = fopen(file->path, mode))) { - mlk_errf("%s: %s", file->path, strerror(errno)); - mlk_alloc_free(file); - return NULL; - } - - file->file.read = file_read; - file->file.write = file_write; - file->file.flush = file_flush; - file->file.free = file_free; - - return &file->file; + return mlk_vfs_dir_open(MLK_VFS_DIR(self), entry, mode); } void @@ -132,3 +190,83 @@ dir->vfs.open = vfs_open; dir->vfs.finish = NULL; } + +struct mlk_vfs_file * +mlk_vfs_dir_open(struct mlk_vfs_dir *dir, const char *entry, const char *mode) +{ + struct mlk_vfs_dir_file *file; + + file = mlk_alloc_new0(1, sizeof (*file)); + + snprintf(file->path, sizeof (file->path), "%s/%s", dir->path, entry); + + if (!(file->handle = makefile(file->path, mode))) { + mlk_alloc_free(file); + return NULL; + } + + file->file.read = file_read; + file->file.write = file_write; + file->file.flush = file_flush; + file->file.finish = file_finish; + + return &file->file; +} + +void +mlk_vfs_dir_finish(struct mlk_vfs_dir *dir) +{ + assert(dir); + + /* Kept for future use. */ + (void)dir; +} + +size_t +mlk_vfs_dir_file_read(struct mlk_vfs_dir_file *file, void *buf, size_t bufsz) +{ + assert(file); + assert(buf); + + size_t rv; + + rv = fread(buf, 1, bufsz, file->handle); + + if (ferror(file->handle)) + return -1; + + return rv; +} + +size_t +mlk_vfs_dir_file_write(struct mlk_vfs_dir_file *file, const void *buf, size_t bufsz) +{ + assert(file); + assert(buf); + + size_t rv; + + rv = fwrite(buf, 1, bufsz, file->handle); + + if (ferror(file->handle)) + return -1; + + return rv; +} + +int +mlk_vfs_dir_file_flush(struct mlk_vfs_dir_file *file) +{ + assert(file); + + return fflush(file->handle) == EOF ? -1 : 0; +} + +void +mlk_vfs_dir_file_finish(struct mlk_vfs_dir_file *file) +{ + assert(file); + + fclose(file->handle); + mlk_alloc_free(file); +}
--- a/libmlk-core/mlk/core/vfs-dir.h Wed Sep 27 22:03:17 2023 +0200 +++ b/libmlk-core/mlk/core/vfs-dir.h Sun Oct 01 09:18:01 2023 +0200 @@ -19,13 +19,52 @@ #ifndef MLK_CORE_VFS_DIR_H #define MLK_CORE_VFS_DIR_H +/** + * \file mlk/core/vfs-dir.h + * \brief VFS subsystem for directories. + * + * This module can be used to read files relative to a filesystem directory path + * using mlk/core/vfs.h abstract VFS module. + * + * It is implemented using the ::MLK_CONTAINER_OF macro which means you can + * use it and derive from it to add or modify its functions. + * + * ## Members used + * + * The following VFS members are used: + * + * - ::mlk_vfs::finish + * - ::mlk_vfs::open + * + * The following VFS file member are used: + * + * - ::mlk_vfs_file::finish + * - ::mlk_vfs_file::flush + * - ::mlk_vfs_file::read + * - ::mlk_vfs_file::write + */ + #include <mlk/util/util.h> #include "vfs.h" +/** + * \struct mlk_vfs_dir_file + * \brief VFS file implementation for filesystem files. + */ struct mlk_vfs_dir_file { + /** + * (read-only) + * + * Path to the opened file. + */ char path[MLK_PATH_MAX]; + /** + * (read-write) + * + * Abstract VFS file to implement. + */ struct mlk_vfs_file file; /** \cond MLK_PRIVATE_DECLS */ @@ -33,6 +72,10 @@ /** \endcond MLK_PRIVATE_DECLS */ }; +/** + * \struct mlk_vfs_zip + * \brief VFS implementation for filesystem directories. + */ struct mlk_vfs_dir { /** * (read-only) @@ -41,6 +84,11 @@ */ char path[MLK_PATH_MAX]; + /** + * (read-write) + * + * Abstract VFS to implement. + */ struct mlk_vfs vfs; }; @@ -48,9 +96,53 @@ extern "C" { #endif +/** + * Initialize the directory object and its underlying VFS module. + * + * \pre dir != NULL + * \pre path != NULL + * \param dir the dir implementation to initialize + * \param path the path to the directory + */ void mlk_vfs_dir_init(struct mlk_vfs_dir *dir, const char *path); +/** + * Implements ::mlk_vfs::open virtual function. + */ +struct mlk_vfs_file * +mlk_vfs_dir_open(struct mlk_vfs_dir *dir, const char *entry, const char *mode); + +/** + * Implements ::mlk_vfs::finish virtual function. + */ +void +mlk_vfs_dir_finish(struct mlk_vfs_dir *dir); + +/** + * Implements ::mlk_vfs_file::read virtual function. + */ +size_t +mlk_vfs_dir_file_read(struct mlk_vfs_dir_file *file, void *buf, size_t bufsz); + +/** + * Implements ::mlk_vfs_file::write virtual function. + */ +size_t +mlk_vfs_dir_file_write(struct mlk_vfs_dir_file *file, const void *buf, size_t bufsz); + +/** + * Implements ::mlk_vfs_file::flush virtual function. + */ +int +mlk_vfs_dir_file_flush(struct mlk_vfs_dir_file *file); + +/** + * Implements ::mlk_vfs_file::finish virtual function. + */ +void +mlk_vfs_dir_file_finish(struct mlk_vfs_dir_file *file); + #if defined(__cplusplus) } #endif
--- a/libmlk-core/mlk/core/vfs-zip.c Wed Sep 27 22:03:17 2023 +0200 +++ b/libmlk-core/mlk/core/vfs-zip.c Sun Oct 01 09:18:01 2023 +0200 @@ -68,57 +68,25 @@ static size_t file_read(struct mlk_vfs_file *self, void *buf, size_t bufsz) { - struct mlk_vfs_zip_file *file = MLK_VFS_ZIP_FILE(self); - - return zip_fread(file->handle, buf, bufsz); -} - -static int -file_flush(struct mlk_vfs_file *self) -{ - (void)self; - - return 0; + return mlk_vfs_zip_file_read(MLK_VFS_ZIP_FILE(self), buf, bufsz); } static void -file_free(struct mlk_vfs_file *self) +file_finish(struct mlk_vfs_file *self) { - struct mlk_vfs_zip_file *file = MLK_VFS_ZIP_FILE(self); - - zip_fclose(file->handle); - mlk_alloc_free(self); + mlk_vfs_zip_file_finish(MLK_VFS_ZIP_FILE(self)); } static struct mlk_vfs_file * vfs_open(struct mlk_vfs *self, const char *entry, const char *mode) { - (void)mode; - - struct mlk_vfs_zip *zip = MLK_VFS_ZIP(self); - struct mlk_vfs_zip_file *file; - - file = mlk_alloc_new0(1, sizeof (*file)); - - if (!(file->handle = zip_fopen(zip->handle, entry, 0))) { - mlk_errf("unable to open file in archive"); - mlk_alloc_free(file); - return NULL; - } - - file->file.read = file_read; - file->file.flush = file_flush; - file->file.free = file_free; - - return &file->file; + return mlk_vfs_zip_open(MLK_VFS_ZIP(self), entry, mode); } static void vfs_finish(struct mlk_vfs *self) { - struct mlk_vfs_zip *zip = MLK_VFS_ZIP(self); - - zip_close(zip->handle); + mlk_vfs_zip_finish(MLK_VFS_ZIP(self)); } int @@ -140,4 +108,59 @@ return 0; } +struct mlk_vfs_file * +mlk_vfs_zip_open(struct mlk_vfs_zip *zip, const char *entry, const char *mode) +{ + (void)mode; + + struct mlk_vfs_zip_file *file; + + file = mlk_alloc_new0(1, sizeof (*file)); + + if (!(file->handle = zip_fopen(zip->handle, entry, 0))) { + mlk_errf("unable to open file in archive"); + mlk_alloc_free(file); + return NULL; + } + + file->file.read = file_read; + file->file.finish = file_finish; + + return &file->file; +} + +void +mlk_vfs_zip_finish(struct mlk_vfs_zip *zip) +{ + assert(zip); + + zip_close(zip->handle); + zip->handle = NULL; +} + +size_t +mlk_vfs_zip_file_read(struct mlk_vfs_zip_file *file, void *buf, size_t bufsz) +{ + assert(file); + assert(buf); + + zip_int64_t rv; + + if ((rv = zip_fread(file->handle, buf, bufsz)) < 0) { + mlk_errf("%s", zip_file_strerror(file->handle)); + return -1; + } + + return rv; +} + +void +mlk_vfs_zip_file_finish(struct mlk_vfs_zip_file *file) +{ + assert(file); + + zip_fclose(file->handle); + mlk_alloc_free(file); +} + #endif /* !MLK_WITH_ZIP */
--- a/libmlk-core/mlk/core/vfs-zip.h Wed Sep 27 22:03:17 2023 +0200 +++ b/libmlk-core/mlk/core/vfs-zip.h Sun Oct 01 09:18:01 2023 +0200 @@ -19,16 +19,46 @@ #ifndef MLK_CORE_VFS_ZIP_H #define MLK_CORE_VFS_ZIP_H +/** + * \file mlk/core/vfs-zip.h + * \brief VFS subsystem for zip archives. + * + * This module can be used to read file from a ZIP archives using the + * mlk/core/vfs.h abstract VFS module. + * + * It is implemented using the ::MLK_CONTAINER_OF macro which means you can + * use it and derive from it to add or modify its functions. + * + * \note It currently supports reading files but not writing. + * + * ## Members used + * + * The following VFS members are used: + * + * - ::mlk_vfs::finish + * - ::mlk_vfs::open + * + * The following VFS file member are used: + * + * - ::mlk_vfs_file::finish + * - ::mlk_vfs_file::read + */ + #include "sysconfig.h" #include "vfs.h" #if defined(MLK_WITH_ZIP) -#if defined(__cplusplus) -extern "C" { -#endif - +/** + * \struct mlk_vfs_zip_file + * \brief VFS file implementation for ZIP files. + */ struct mlk_vfs_zip_file { + /** + * (read-write) + * + * Abstract VFS file to implement. + */ struct mlk_vfs_file file; /** \cond MLK_PRIVATE_DECLS */ @@ -36,7 +66,16 @@ /** \endcond MLK_PRIVATE_DECLS */ }; +/** + * \struct mlk_vfs_zip + * \brief VFS implementation for ZIP files. + */ struct mlk_vfs_zip { + /** + * (read-write) + * + * Abstract VFS to implement. + */ struct mlk_vfs vfs; /** \cond MLK_PRIVATE_DECLS */ @@ -44,8 +83,47 @@ /** \endcond MLK_PRIVATE_DECLS */ }; +#if defined(__cplusplus) +extern "C" { +#endif + +/** + * Initialize the ZIP object and its underlying VFS module. + * + * \pre zip != NULL + * \pre file != NULL + * \pre mode != NULL + * \param zip the zip implementation to initialize + * \param path the path to the ZIP file + * \param mode the open mode + * \return 0 on success or -1 on error + */ int -mlk_vfs_zip_init(struct mlk_vfs_zip *zip, const char *file, const char *mode); +mlk_vfs_zip_init(struct mlk_vfs_zip *zip, const char *path, const char *mode); + +/** + * Implements ::mlk_vfs::open virtual function. + */ +struct mlk_vfs_file * +mlk_vfs_zip_open(struct mlk_vfs_zip *zip, const char *entry, const char *mode); + +/** + * Implements ::mlk_vfs::open virtual function. + */ +void +mlk_vfs_zip_finish(struct mlk_vfs_zip *zip); + +/** + * Implements ::mlk_vfs_file::read virtual function. + */ +size_t +mlk_vfs_zip_file_read(struct mlk_vfs_zip_file *file, void *buf, size_t bufsz); + +/** + * Implements ::mlk_vfs_file::finish virtual function. + */ +void +mlk_vfs_zip_file_finish(struct mlk_vfs_zip_file *file); #if defined(__cplusplus) }
--- a/libmlk-core/mlk/core/vfs.c Wed Sep 27 22:03:17 2023 +0200 +++ b/libmlk-core/mlk/core/vfs.c Sun Oct 01 09:18:01 2023 +0200 @@ -28,9 +28,7 @@ #include "vfs_p.h" struct mlk_vfs_file * -mlk_vfs_open(struct mlk_vfs *vfs, - const char *entry, - const char *mode) +mlk_vfs_open(struct mlk_vfs *vfs, const char *entry, const char *mode) { assert(vfs); assert(entry); @@ -58,21 +56,23 @@ } char * -mlk_vfs_file_aread(struct mlk_vfs_file *file, size_t *outlen) +mlk_vfs_file_read_all(struct mlk_vfs_file *file, size_t *outlen) { char data[BUFSIZ], *str; - size_t nr, len = 0, cap = 128; + size_t nr, len = 0, cap = sizeof (data); /* Initial allocation. */ str = mlk_alloc_new0(cap, 1); while ((nr = mlk_vfs_file_read(file, data, sizeof (data))) > 0) { if (nr >= cap - len) { - cap *= 2; - str = mlk_alloc_resize(str, cap); + while (nr >= cap - len) + cap *= 2; + + str = mlk_alloc_resize(str, cap); } - sprintf(&str[len], "%.*s", (int)nr, data); + memcpy(&str[len], data, nr); len += nr; } @@ -108,12 +108,12 @@ } void -mlk_vfs_file_free(struct mlk_vfs_file *file) +mlk_vfs_file_finish(struct mlk_vfs_file *file) { assert(file); - if (file->free) - file->free(file); + if (file->finish) + file->finish(file); } /* private */ @@ -134,7 +134,7 @@ char *data; size_t datasz; - if (!(data = mlk_vfs_file_aread(file, &datasz))) + if (!(data = mlk_vfs_file_read_all(file, &datasz))) return NULL; if (!(ops = SDL_RWFromConstMem(data, datasz))) { free(data);
--- a/libmlk-core/mlk/core/vfs.h Wed Sep 27 22:03:17 2023 +0200 +++ b/libmlk-core/mlk/core/vfs.h Sun Oct 01 09:18:01 2023 +0200 @@ -34,17 +34,33 @@ * | mlk/core/vfs-dir.h | read, write | opens file relative to a directory | * | mlk/core/vfs-zip.h | read | zip archive files extractor | * + * ## Opening mode + * + * Both VFS interfaces and function can take a `const char *` mode argument to + * indicate user open mode. Each implementation can take specific interface + * options but the following are reserved and must be understood by each: + * + * - `e`: start writing at end + * - `r`: open for reading + * - `t`: truncate file (can't be used without `w`) + * - `w`: open for writing (create file but keep content if exists) + * + * Mode `e` is meaning less when combined with `t`. + * + * \note In contrast to C `fopen` function, modes can be mixed which means `r`, + * `w` and `rw` means read-only, write-only and read-write respectively. + * * ## Initialize a VFS * - * To use this module, you must first open a VFS interface. It is usually implemented - * using the ::MLK_CONTAINER_OF macro. + * To use this module, you must first open a VFS interface. It is usually + * implemented using the ::MLK_CONTAINER_OF macro. * * Example with mlk/core/vfs-dir.h * * ```c * struct mlk_vfs_dir dir; * - * mlk_vfs_directory_init(&dir, "/usr/share/mario"); + * mlk_vfs_dir_init(&dir, "/usr/share/mario"); * ``` * * The abstract interface will be located in the `vfs` field for each @@ -60,16 +76,16 @@ * char content[1024]; * size_t len; * - * file = mlk_vfs_open(vfs, "block.png"); + * file = mlk_vfs_open(&dir.vfs, "block.png", "r"); * * if (!file) - * handle_failure(); + * your_handle_failure_function(); * * // The function does not append a NUL terminator. * len = mlk_vfs_file_read(file, content, sizeof (content) - 1); * * if (len == (size_t)-1) - * handle_failure(); + * your_handle_failure_function(); * * // Use content freely... * ``` @@ -81,7 +97,7 @@ * direct references to the VFS module. * * ```c - * mlk_vfs_file_free(file); + * mlk_vfs_file_finish(file); * mlk_vfs_finish(&dir.vfs); * ``` */ @@ -206,7 +222,7 @@ * \pre self != NULL * \param self this VFS file */ - void (*free)(struct mlk_vfs_file *self); + void (*finish)(struct mlk_vfs_file *self); }; #if defined(__cplusplus) @@ -214,21 +230,19 @@ #endif /** - * Wrapper of ::mlk_vfs::open if not NULL. + * Invoke ::mlk_vfs::open if not NULL. */ struct mlk_vfs_file * -mlk_vfs_open(struct mlk_vfs *vfs, - const char *entry, - const char *mode); +mlk_vfs_open(struct mlk_vfs *vfs, const char *entry, const char *mode); /** - * Wrapper of ::mlk_vfs::finish if not NULL. + * Invoke ::mlk_vfs::finish if not NULL. */ void mlk_vfs_finish(struct mlk_vfs *vfs); /** - * Wrapper of ::mlk_vfs_file::read if not NULL. + * Invoke ::mlk_vfs_file::read if not NULL. */ size_t mlk_vfs_file_read(struct mlk_vfs_file *file, void *buf, size_t bufsz); @@ -243,28 +257,28 @@ * \pre file != NULL * \param file this VFS file * \param len pointer receiving the number of bytes read (can be NULL) - * \return the string content + * \return the string content (user owned) */ char * -mlk_vfs_file_aread(struct mlk_vfs_file *file, size_t *len); +mlk_vfs_file_read_all(struct mlk_vfs_file *file, size_t *len); /** - * Wrapper of ::mlk_vfs::finish if not NULL. + * Invoke ::mlk_vfs_file::finish if not NULL. */ size_t mlk_vfs_file_write(struct mlk_vfs_file *file, void *buf, size_t bufsz); /** - * Wrapper of ::mlk_vfs::finish if not NULL. + * Invoke ::mlk_vfs_file::flush if not NULL. */ int mlk_vfs_file_flush(struct mlk_vfs_file *file); /** - * Wrapper of ::mlk_vfs::finish if not NULL. + * Invoke ::mlk_vfs_file::finish if not NULL. */ void -mlk_vfs_file_free(struct mlk_vfs_file *file); +mlk_vfs_file_finish(struct mlk_vfs_file *file); #if defined(__cplusplus) }
--- a/tests/test-vfs-dir.c Wed Sep 27 22:03:17 2023 +0200 +++ b/tests/test-vfs-dir.c Sun Oct 01 09:18:01 2023 +0200 @@ -33,7 +33,7 @@ DT_EQ_UINT(mlk_vfs_file_read(file, data, sizeof (data)), 13U); DT_EQ_STR(data, "Hello World!\n"); - mlk_vfs_file_free(file); + mlk_vfs_file_finish(file); mlk_vfs_finish(&dir.vfs); }
--- a/tests/test-vfs-zip.c Wed Sep 27 22:03:17 2023 +0200 +++ b/tests/test-vfs-zip.c Sun Oct 01 09:18:01 2023 +0200 @@ -32,7 +32,7 @@ DT_EQ_UINT(mlk_vfs_file_read(file, data, sizeof (data)), 21U); DT_EQ_STR(data, "Hello from zip file!\n"); - mlk_vfs_file_free(file); + mlk_vfs_file_finish(file); mlk_vfs_finish(&zip.vfs); }