56 Commits

Author SHA1 Message Date
David Reid 347321b27c Version 0.11.24 2026-01-17 09:37:44 +10:00
David Reid da94cf2bc6 Update fs. 2026-01-17 09:34:51 +10:00
David Reid 8e6283aa31 Fix a warning. 2026-01-17 09:34:34 +10:00
David Reid d0b98eee6b Update change history. 2026-01-17 09:25:06 +10:00
David Reid 74912d525b Add SECURITY.md 2026-01-17 08:58:02 +10:00
Richard Keller a551f0e70b Free pa_context if connecting to PulseAudio fails. 2026-01-17 08:50:10 +10:00
David Reid 7dae981ad5 Add some helpers for resetting a sound after a fade and stop.
Public issue https://github.com/mackron/miniaudio/issues/714
2026-01-17 07:32:18 +10:00
David Reid 88776cedb7 Whitespace. 2026-01-10 08:49:12 +10:00
spevnev e00cee2af1 Cast tv_sec to 64-bit int before converting 2026-01-10 08:47:03 +10:00
David Reid 5ef2e1ec57 Update fs. 2026-01-07 18:19:59 +10:00
David Reid ee8a65bed9 Update dr_libs. 2026-01-07 18:13:08 +10:00
David Reid 44b847fbf8 Update fs. 2026-01-07 18:10:39 +10:00
David Reid 5f3de510b2 Make ma_is_spatial_channel_position() a bit more robust.
This makes it less error prone when new channel positions are added to
the enum.
2026-01-07 12:36:18 +10:00
David Reid 53116ad6da Minor change to an enum to make it less error prone. 2026-01-07 12:31:03 +10:00
David Reid b83869eb09 Update the spatializer to require a listener when processing. 2026-01-07 12:24:30 +10:00
David Reid bedfd053cb Fix a bug in the gainer where a null pointer can be offset. 2026-01-07 12:13:18 +10:00
David Reid 32cc6d53cd Fix a possible null pointer dereference. 2026-01-07 12:08:20 +10:00
David Reid bd26454c26 Fix a possible null pointer dereference. 2026-01-07 12:07:25 +10:00
David Reid d791c16d8d Remove some redundant error checks. 2026-01-07 12:05:57 +10:00
David Reid 8c4535c6c5 Fix a bug with sound node processing. 2026-01-07 10:33:13 +10:00
David Reid 27d2d6ac87 Add support for custom resamplers to sounds.
Public issue https://github.com/mackron/miniaudio/issues/965
2026-01-06 19:25:32 +10:00
David Reid 919a01ae4a Use ma_resampler instead of ma_linear_resampler for sound nodes.
This is infrastructure work for supporting custom resamplers for the
pitching and Doppler effects for sounds.

Public issue https://github.com/mackron/miniaudio/issues/965
2026-01-06 18:37:05 +10:00
David Reid 065e6eadb5 Minor code rearrangement. 2026-01-06 18:18:16 +10:00
caturria 962d11b4ce Resource manager can now have a custom resampler. 2026-01-06 18:13:57 +10:00
David Reid b62249ceaf Fix an infinite loop bug. 2026-01-06 16:29:42 +10:00
David Reid 525b04db04 Make ma_sound node processing a bit more robust.
This removes the dependency on querying the required input frame count
from the resampler. This should in turn enable future work to support
custom resamplers.

Public issue https://github.com/mackron/miniaudio/issues/965
2026-01-06 16:11:35 +10:00
David Reid e93e1dbba1 Set up some infrastructure for improvements to ma_sound processing. 2026-01-06 15:15:43 +10:00
David Reid 111d620c63 Fix some node timing errors.
This commit fixes a bug relating to nodes with a scheduled start/stop
time. Whether or not the node is considered started or stopped is being
incorrectly reported by `ma_node_get_state_by_time_range()`.

Another issue is fixed in `ma_node_read_pcm_frames()`, which is related
to the fix above, where the frame count can underflow thereby resulting
in a crash.

Public issue https://github.com/mackron/miniaudio/issues/969
2026-01-06 13:05:40 +10:00
David Reid 3b4e87848b Fix a typo. 2026-01-06 10:11:16 +10:00
David Reid 35acd7a65b Relocate the sound end callback to after the sound is stopped.
Public issue https://github.com/mackron/miniaudio/issues/1013
2026-01-06 10:05:36 +10:00
David Reid 92fb865387 Update some comments. 2026-01-06 08:06:49 +10:00
François Hautier c44ec3f46a Better comment 2026-01-06 07:56:41 +10:00
François Hautier 8c3b213a7c WebAudio: Try to fix a startup noise 2026-01-06 07:56:41 +10:00
David Reid 88797e9dee Fix a double-uninit error with decoders.
Public issue https://github.com/mackron/miniaudio/issues/1080
2026-01-04 15:03:47 +10:00
David Reid ad85d0c3c4 Update dr_libs. 2026-01-04 14:37:14 +10:00
David Reid 364844231d Fix an bug with error recovery when failing to initialize a decoder.
Public issue https://github.com/mackron/miniaudio/issues/1080
2025-12-23 19:04:53 +10:00
David Reid 9ea38e9f3a Update dr_wav and dr_flac. 2025-12-14 06:38:32 +10:00
David Reid af19bdb6ff Fix a bug where MA_NO_DECODING would disable the WAV encoder.
Public issue https://github.com/mackron/miniaudio/issues/1076
2025-12-13 17:26:06 +10:00
Marty f513f462df cmake: add public include directories for extra decoders
It seems that when linking, for example, `miniaudio_libvorbis`, it doesn't add the necessary include directories for use in the project. This is because the include directories were not being added to the target at all.

Here, I fix that by adding them with PUBLIC scope.
2025-12-12 19:12:13 +10:00
David Reid 8c1dc255db Remove Cosmopolitan pre-processing checks.
https://github.com/mackron/miniaudio/pull/1070
2025-11-30 05:39:49 +10:00
David Delassus dbf8e114f9 CMake: make install directives optional 2025-11-29 14:42:38 +10:00
Louis du Verdier 6d65be5e0e Do not set POSIX thread scheduler policy on systems reporting that they do not support it 2025-11-29 14:20:45 +10:00
Kjetil Berg 6a895501cf Fix: exclude Emscripten from ALSA support check on Linux 2025-11-29 08:58:30 +10:00
David Reid 787318fd8f Update dr_wav and dr_mp3. 2025-11-29 08:36:33 +10:00
David Reid 4a8467852a Fix a shadow declaration warning.
Public issue https://github.com/mackron/miniaudio/issues/1059
2025-11-29 08:34:40 +10:00
David Reid 80cf7b2deb Update dr_flac.
Public issue https://github.com/mackron/miniaudio/issues/1050
2025-09-28 08:10:20 +10:00
David Reid 2db0984566 Fix a possible crash in the resource manager.
This code was prematurely freeing a data buffer node which was resulting
in a dereference of an invalid pointer.
2025-09-27 12:34:21 +10:00
David Reid 669ed3e844 Update dr_mp3. 2025-09-24 16:04:56 +10:00
David Reid 81410769ae Update c89atomic.
There was a stray line continuation in a macro which was resulting in an
error with MSVC.
2025-09-19 13:03:10 +10:00
David Reid ffe558437f Update change history. 2025-09-19 12:47:58 +10:00
David Reid 089f041120 Update c89atomic.
Public issue https://github.com/mackron/miniaudio/issues/1045
2025-09-19 12:46:50 +10:00
David Reid 2e02046c6d Update dr_libs. 2025-09-19 12:45:59 +10:00
David Reid b22a0cbdb1 Update documentation generator. 2025-09-19 12:45:20 +10:00
David Reid b3c6bcec39 Update change history and version number. 2025-09-14 07:39:08 +10:00
David Reid ed2c5270c8 Fix a typo. 2025-09-14 07:36:30 +10:00
Michael Müller 3dfcefc75b Fix access to miniaudio in ma_context_uninit__webaudio. 2025-09-14 07:26:31 +10:00
8 changed files with 1519 additions and 532 deletions
+4
View File
@@ -0,0 +1,4 @@
I deal with all security related issues publicly and transparently, and it can sometimes take a while before I
get a chance to address it. If this is an issue for you, you need to use another library. The fastest way to get
a bug fixed is to submit a pull request, but if this is impractical for you please post a ticket to the public
GitHub issue tracker.
+16
View File
@@ -1,3 +1,19 @@
v0.11.24 - 2026-01-17
=====================
* Fixed a possible glitch when processing the audio of a `ma_sound` when doing resampling.
* Fixed a possible crash in the node graph relating to scheduled starts and stops.
* Fixed a bug where MA_NO_DECODING would disable the WAV encoder.
* Fixed a pthread compatibility issue, particularly with Android.
* Fixed a possible crash in the resource manager.
* Fixed a possible double-uninit error when a decoder fails to initialize.
* Fixed a compilation error with the MSVC Aarch64 build.
* Addressed a few errors found through static analysis, particularly around possible null pointer dereferences.
* `ma_sound_is_playing()` will now correctly return false when called from inside the end callback of a sound.
* Miscellaneous compiler compatibility and warning fixes.
* PulseAudio: Fix a resource leak when a context fails to connect.
* Web: Fixed an error when uninitializing a context.
v0.11.23 - 2025-09-11 v0.11.23 - 2025-09-11
===================== =====================
* Fixed an error in `ma_channel_map_to_string()` where the output string is not null terminated correctly. * Fixed an error in `ma_channel_map_to_string()` where the output string is not null terminated correctly.
+24 -12
View File
@@ -70,7 +70,7 @@ option(MINIAUDIO_NO_NEON "Disable NEON optimizations"
option(MINIAUDIO_NO_RUNTIME_LINKING "Disable runtime linking" OFF) option(MINIAUDIO_NO_RUNTIME_LINKING "Disable runtime linking" OFF)
option(MINIAUDIO_USE_STDINT "Use <stdint.h> for sized types" OFF) option(MINIAUDIO_USE_STDINT "Use <stdint.h> for sized types" OFF)
option(MINIAUDIO_DEBUG_OUTPUT "Enable stdout debug output" OFF) option(MINIAUDIO_DEBUG_OUTPUT "Enable stdout debug output" OFF)
option(MINIAUDIO_INSTALL "Enable installation targets" ON)
include(GNUInstallDirs) include(GNUInstallDirs)
@@ -508,7 +508,9 @@ add_library(miniaudio
) )
list(APPEND LIBS_TO_INSTALL miniaudio) list(APPEND LIBS_TO_INSTALL miniaudio)
install(FILES miniaudio.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/miniaudio) if(MINIAUDIO_INSTALL)
install(FILES miniaudio.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/miniaudio)
endif()
target_include_directories(miniaudio PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_include_directories(miniaudio PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_compile_options (miniaudio PRIVATE ${COMPILE_OPTIONS}) target_compile_options (miniaudio PRIVATE ${COMPILE_OPTIONS})
@@ -534,11 +536,14 @@ if(HAS_LIBVORBIS)
) )
list(APPEND LIBS_TO_INSTALL miniaudio_libvorbis) list(APPEND LIBS_TO_INSTALL miniaudio_libvorbis)
install(FILES extras/decoders/libvorbis/miniaudio_libvorbis.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/miniaudio/extras/decoders/libvorbis) if(MINIAUDIO_INSTALL)
install(FILES extras/decoders/libvorbis/miniaudio_libvorbis.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/miniaudio/extras/decoders/libvorbis)
endif()
target_compile_options (miniaudio_libvorbis PRIVATE ${COMPILE_OPTIONS}) target_compile_options (miniaudio_libvorbis PRIVATE ${COMPILE_OPTIONS})
target_compile_definitions(miniaudio_libvorbis PRIVATE ${COMPILE_DEFINES}) target_compile_definitions(miniaudio_libvorbis PRIVATE ${COMPILE_DEFINES})
target_link_libraries (miniaudio_libvorbis PRIVATE libvorbis_interface) target_link_libraries (miniaudio_libvorbis PRIVATE libvorbis_interface)
target_include_directories(miniaudio_libvorbis PUBLIC extras/decoders/libvorbis/)
endif() endif()
@@ -562,11 +567,14 @@ if(HAS_LIBOPUS)
list(APPEND LIBS_TO_INSTALL miniaudio_libopus) list(APPEND LIBS_TO_INSTALL miniaudio_libopus)
install(FILES extras/decoders/libopus/miniaudio_libopus.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/miniaudio/extras/decoders/libopus) if(MINIAUDIO_INSTALL)
install(FILES extras/decoders/libopus/miniaudio_libopus.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/miniaudio/extras/decoders/libopus)
endif()
target_compile_options (miniaudio_libopus PRIVATE ${COMPILE_OPTIONS}) target_compile_options (miniaudio_libopus PRIVATE ${COMPILE_OPTIONS})
target_compile_definitions(miniaudio_libopus PRIVATE ${COMPILE_DEFINES}) target_compile_definitions(miniaudio_libopus PRIVATE ${COMPILE_DEFINES})
target_link_libraries (miniaudio_libopus PRIVATE libopus_interface) target_link_libraries (miniaudio_libopus PRIVATE libopus_interface)
target_include_directories(miniaudio_libopus PUBLIC extras/decoders/libopus/)
endif() endif()
@@ -581,7 +589,9 @@ if (NOT MINIAUDIO_NO_EXTRA_NODES)
list(APPEND libs miniaudio_${name}_node) list(APPEND libs miniaudio_${name}_node)
set(LIBS_TO_INSTALL "${libs}" PARENT_SCOPE) # without PARENT_SCOPE, any changes are lost set(LIBS_TO_INSTALL "${libs}" PARENT_SCOPE) # without PARENT_SCOPE, any changes are lost
install(FILES extras/nodes/ma_${name}_node/ma_${name}_node.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/miniaudio/extras/nodes/ma_${name}_node) if(MINIAUDIO_INSTALL)
install(FILES extras/nodes/ma_${name}_node/ma_${name}_node.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/miniaudio/extras/nodes/ma_${name}_node)
endif()
target_include_directories(miniaudio_${name}_node PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/extras/nodes/ma_${name}_node) target_include_directories(miniaudio_${name}_node PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/extras/nodes/ma_${name}_node)
target_compile_options (miniaudio_${name}_node PRIVATE ${COMPILE_OPTIONS}) target_compile_options (miniaudio_${name}_node PRIVATE ${COMPILE_OPTIONS})
@@ -856,11 +866,13 @@ string(JOIN " " MINIAUDIO_PC_CFLAGS ${COMPILE_DEFINES})
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/miniaudio.pc.in" "${CMAKE_CURRENT_BINARY_DIR}/miniaudio.pc" @ONLY) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/miniaudio.pc.in" "${CMAKE_CURRENT_BINARY_DIR}/miniaudio.pc" @ONLY)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/miniaudio.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") if(MINIAUDIO_INSTALL)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/miniaudio.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
message(STATUS "Library list: ${LIBS_TO_INSTALL}") message(STATUS "Library list: ${LIBS_TO_INSTALL}")
install(TARGETS ${LIBS_TO_INSTALL} install(TARGETS ${LIBS_TO_INSTALL}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
) )
endif()
+1 -4
View File
@@ -205,10 +205,7 @@ Backends
Security Security
======== ========
I deal with all security related issues publicly and transparently, and it can sometimes take a while before I See the miniaudio [security policy](.github/SECURITY.md).
get a chance to address it. If this is an issue for you, you need to use another library. The fastest way to get
a bug fixed is to submit a pull request, but if this is impractical for you please post a ticket to the public
GitHub issue tracker.
License License
+670 -137
View File
File diff suppressed because it is too large Load Diff
+266 -36
View File
@@ -740,12 +740,6 @@ init
uninit uninit
This is where you should do any cleanup. Do not close the stream here. This is where you should do any cleanup. Do not close the stream here.
ioctl
This function is optional. You can use this to implement custom IO control commands. Return
`FS_INVALID_OPERATION` if the command is not recognized. The format of the `pArg` parameter is
command specific. If the backend does not need to implement this function, it can be left as
`NULL` or return `FS_NOT_IMPLEMENTED`.
remove remove
This function is used to delete a file or directory. This is not recursive. If the path is This function is used to delete a file or directory. This is not recursive. If the path is
a directory, the backend should return an error if it is not empty. Backends do not need to a directory, the backend should return an error if it is not empty. Backends do not need to
@@ -939,6 +933,14 @@ see some random tags and stuff in this file. These are just used for doing a dum
extern "C" { extern "C" {
#endif #endif
/* BEG fs_platform_detection.c */
#if defined(_WIN32)
#define FS_WIN32
#else
#define FS_POSIX
#endif
/* END fs_platform_detection.c */
/* BEG fs_compiler_compat.h */ /* BEG fs_compiler_compat.h */
#include <stddef.h> /* For size_t. */ #include <stddef.h> /* For size_t. */
#include <stdarg.h> /* For va_list. */ #include <stdarg.h> /* For va_list. */
@@ -1060,6 +1062,75 @@ typedef unsigned int fs_bool32;
/* END fs_compiler_compat.h */ /* END fs_compiler_compat.h */
/* BEG fs_thread_basic_types.h */
#if defined(FS_POSIX)
#ifndef FS_USE_PTHREAD
#define FS_USE_PTHREAD
#endif
#ifndef FS_NO_PTHREAD_IN_HEADER
#include <pthread.h>
typedef pthread_t fs_pthread_t;
typedef pthread_mutex_t fs_pthread_mutex_t;
#else
typedef fs_uintptr fs_pthread_t;
typedef union fs_pthread_mutex_t { char __data[40]; fs_uint64 __alignment; } fs_pthread_mutex_t;
#endif
#endif
/* END fs_thread_basic_types.h */
/* BEG fs_thread_mtx.h */
#if defined(FS_WIN32)
typedef struct
{
void* handle; /* HANDLE, CreateMutex(), CreateEvent() */
int type;
} fs_mtx;
#else
/*
We may need to force the use of a manual recursive mutex which will happen when compiling
on very old compilers, or with `-std=c89`.
*/
/* If __STDC_VERSION__ is not defined it means we're compiling in C89 mode. */
#if !defined(FS_USE_MANUAL_RECURSIVE_MUTEX) && !defined(__STDC_VERSION__)
#define FS_USE_MANUAL_RECURSIVE_MUTEX
#endif
/* This is for checking if PTHREAD_MUTEX_RECURSIVE is available. */
#if !defined(FS_USE_MANUAL_RECURSIVE_MUTEX) && (!defined(__USE_UNIX98) && !defined(__USE_XOPEN2K8))
#define FS_USE_MANUAL_RECURSIVE_MUTEX
#endif
#ifdef FS_USE_MANUAL_RECURSIVE_MUTEX
typedef struct
{
fs_pthread_mutex_t mutex; /* The underlying pthread mutex. */
fs_pthread_mutex_t guard; /* Guard for metadata (owner and recursionCount). */
fs_pthread_t owner;
int recursionCount;
int type;
} fs_mtx;
#else
typedef fs_pthread_mutex_t fs_mtx;
#endif
#endif
enum
{
fs_mtx_plain = 0x00000000,
fs_mtx_timed = 0x00000001,
fs_mtx_recursive = 0x00000002
};
FS_API int fs_mtx_init(fs_mtx* mutex, int type);
FS_API void fs_mtx_destroy(fs_mtx* mutex);
FS_API int fs_mtx_lock(fs_mtx* mutex);
FS_API int fs_mtx_unlock(fs_mtx* mutex);
/* END fs_thread_mtx.h */
/* BEG fs_result.h */ /* BEG fs_result.h */
typedef enum typedef enum
{ {
@@ -1125,7 +1196,7 @@ typedef enum
FS_HAS_MORE_OUTPUT = 102 /* Some stream has more output data to be read, but there's not enough room in the output buffer. */ FS_HAS_MORE_OUTPUT = 102 /* Some stream has more output data to be read, but there's not enough room in the output buffer. */
} fs_result; } fs_result;
FS_API const char* fs_result_to_string(fs_result result); FS_API const char* fs_result_description(fs_result result);
/* END fs_result.h */ /* END fs_result.h */
@@ -1160,6 +1231,7 @@ The stream vtable can support both reading and writing, but it doesn't need to s
the same time. If one is not supported, simply leave the relevant `read` or `write` callback as the same time. If one is not supported, simply leave the relevant `read` or `write` callback as
`NULL`, or have them return FS_NOT_IMPLEMENTED. `NULL`, or have them return FS_NOT_IMPLEMENTED.
*/ */
typedef enum fs_seek_origin typedef enum fs_seek_origin
{ {
FS_SEEK_SET = 0, FS_SEEK_SET = 0,
@@ -1229,6 +1301,7 @@ appended to the end of the data.
For flexiblity in case the backend does not support cursor retrieval or positioning, the data will be read For flexiblity in case the backend does not support cursor retrieval or positioning, the data will be read
in fixed sized chunks. in fixed sized chunks.
*/ */
typedef enum fs_format typedef enum fs_format
{ {
FS_FORMAT_TEXT, FS_FORMAT_TEXT,
@@ -1443,6 +1516,42 @@ struct fs_iterator
fs_file_info info; fs_file_info info;
}; };
/*
Configuration structure for fs objects.
Members
-------
pBackend
The backend to use. If NULL, the standard file system backend will be used.
pBackendConfig
A pointer to a backend-specific configuration structure. This is passed directly to the
backend's init function. The documentation for your backend will tell you how to use this. Most
backends will allow you to set this to NULL.
pStream
A stream to use for archive backends. This is required for any file-backed backend, such as ZIP
archives. If the backend does not need a stream, this can be NULL.
pArchiveTypes
An array of archive types to register. This can be NULL if no archive types are to be
registered. Archive types are mapped to file extensions. See `fs_archive_type` for more
information.
archiveTypeCount
The number of archive types in the `pArchiveTypes` array. Set this to 0 if `pArchiveTypes` is
NULL.
onRefCountChanged
A callback that is fired when the reference count of a fs object changes.
pRefCountChangedUserData
User data that is passed to the `onRefCountChanged` callback.
pAllocationCallbacks
Custom allocation callbacks. If NULL, the standard malloc/realloc/free functions will be used.
*/
struct fs_config struct fs_config
{ {
const fs_backend* pBackend; const fs_backend* pBackend;
@@ -1464,7 +1573,6 @@ struct fs_backend
size_t (* alloc_size )(const void* pBackendConfig); size_t (* alloc_size )(const void* pBackendConfig);
fs_result (* init )(fs* pFS, const void* pBackendConfig, fs_stream* pStream); /* Return 0 on success or an errno result code on error. pBackendConfig is a pointer to a backend-specific struct. The documentation for your backend will tell you how to use this. You can usually pass in NULL for this. */ fs_result (* init )(fs* pFS, const void* pBackendConfig, fs_stream* pStream); /* Return 0 on success or an errno result code on error. pBackendConfig is a pointer to a backend-specific struct. The documentation for your backend will tell you how to use this. You can usually pass in NULL for this. */
void (* uninit )(fs* pFS); void (* uninit )(fs* pFS);
fs_result (* ioctl )(fs* pFS, int op, void* pArg); /* Optional. */
fs_result (* remove )(fs* pFS, const char* pFilePath); fs_result (* remove )(fs* pFS, const char* pFilePath);
fs_result (* rename )(fs* pFS, const char* pOldPath, const char* pNewPath); /* Return FS_DIFFERENT_DEVICE if the old and new paths are on different devices and would require a copy. */ fs_result (* rename )(fs* pFS, const char* pOldPath, const char* pNewPath); /* Return FS_DIFFERENT_DEVICE if the old and new paths are on different devices and would require a copy. */
fs_result (* mkdir )(fs* pFS, const char* pPath); /* This is not recursive. Return FS_ALREADY_EXISTS if directory already exists. Return FS_DOES_NOT_EXIST if a parent directory does not exist. */ fs_result (* mkdir )(fs* pFS, const char* pPath); /* This is not recursive. Return FS_ALREADY_EXISTS if directory already exists. Return FS_DOES_NOT_EXIST if a parent directory does not exist. */
@@ -1661,34 +1769,6 @@ fs_init()
FS_API void fs_uninit(fs* pFS); FS_API void fs_uninit(fs* pFS);
/*
Performs a control operation on the file system.
This is backend-specific. Check the documentation for the backend you are using to see what
operations are supported.
Parameters
----------
pFS : (in)
A pointer to the file system object. Must not be NULL.
op : (in)
The operation to perform. This is backend-specific.
pArg : (in, optional)
An optional pointer to an argument struct. This is backend-specific. Can be NULL if the
operation does not require any arguments.
Return Value
------------
Returns FS_SUCCESS on success; any other result code otherwise. May return FS_NOT_IMPLEMENTED if
the operation is not supported by the backend.
*/
FS_API fs_result fs_ioctl(fs* pFS, int op, void* pArg);
/* /*
Removes a file or empty directory. Removes a file or empty directory.
@@ -3291,6 +3371,154 @@ FS_API fs_result fs_file_open_and_read(fs* pFS, const char* pFilePath, fs_format
FS_API fs_result fs_file_open_and_write(fs* pFS, const char* pFilePath, const void* pData, size_t dataSize); FS_API fs_result fs_file_open_and_write(fs* pFS, const char* pFilePath, const void* pData, size_t dataSize);
/*
Serializes a file system subdirectory to a stream.
This function recursively serializes all files and directories within the specified directory
to a binary stream. The serialized data can later be restored using `fs_deserialize()`.
The directory parameter specifies which directory to serialize. This function is built on top of
standard file iteration functions, i.e. `fs_first()`, `fs_next()`. The directory and options all
work the same way as they do for `fs_first()`.
It is possible for serialization to fail partway through, in which case the stream will contain
partial data. It is the caller's responsibility to handle this case appropriately.
When calculating offsets, it will use `fs_stream_tell()` to get the current position in the stream.
Keep this in mind if you are appending this to the end of an existing stream. This may or may not
be useful to you depending on your use case.
The format is designed to be appendable to existing payloads using tools like `cat`. Offsets are
stored relative to the end of the archive to support this use case. The Base Offset field in the
tail specifies where file data begins relative to the end of the stream, and will always be a
negative number. Each file's offset is then relative to this base offset. To seek to a file's data,
you would add the file's offset to the base offset and then seek by that amount relative to the end
of the stream.
Below is the format:
|: MAIN STRUCTURE :|
|------------------------------------------------|
| n | File Data |
|------|-----------------------------------------|
| n | TOC Entries |
|------|-----------------------------------------|
| 8 | 'FSSRLZ1\0' |
| 8 | Base Offset (negative, relative to end) |
| 8 | TOC Offset (relative to Base Offset) |
| 4 | TOC Entry Count |
| 4 | Reserved (set to zero) |
|: TOC ENTRY :|
|------------------------------------------------|
| 4 | File Flags |
| 4 | File Path Length |
| n | File Path |
| 1 | '\0' (Null Terminator) |
| n | Padding to 8-byte alignment |
| 8 | File Size |
| 8 | File Offset (local, relative) |
|: FILE FLAGS :|
|------------------------------------------------|
| 0x1 | Directory |
Notes:
- The signature is located at the end of the stream. To parse, seek to the last 32 bytes, read
the signature, base offset, TOC offset and entry count, then calculate the absolute TOC offset
as Base Offset + TOC Offset and seek to that position, relative to the end, to read the TOC.
The base offset is always negative.
- Directories should be explicitly listed in the TOC, and also have the "Directory" flag set.
- Directories must be listed before any files that are contained within them.
- File Offset and TOC Offset are both local offsets relative to the Base Offset position. Sum
the offsets with the base offset, and then seek relative to the end.
- The Base Offset is relative to the end of the archive (negative value).
- File data is aligned to 8-byte boundaries.
- All multi-byte values are little-endian.
- File paths are stored using UTF-8 encoding.
- To calculate the padding between the null terminator and the next 8-byte aligned offset, round
the length of the file path plus one (for the null terminator) up to the next multiple of 8,
then subtract the length of the file path plus one.
Parameters
----------
pFS : (in)
A pointer to the file system object. Must not be NULL.
pDirectoryPath : (in, optional)
The path to the directory to serialize.
options : (in)
Options for the serialization operation. These are passed in directly to `fs_first()` and
therefore have the same meaning. See `fs_first()` for details. These will also be passed into
`fs_file_open()` when opening files, combined with `FS_READ`.
pOutputStream : (in)
A pointer to the output stream where the serialized data will be written. Must not be NULL.
Return Value
------------
Returns FS_SUCCESS on success; any other result code otherwise.
See Also
--------
fs_deserialize()
fs_first()
*/
FS_API fs_result fs_serialize(fs* pFS, const char* pDirectoryPath, int options, fs_stream* pOutputStream);
/*
Deserializes file system data from a stream.
This function reads serialized file system data from a stream and recreates the files and
directories in the specified subdirectory. The format of the data must match that produced by
`fs_serialize()`.
The subdirectory parameter specifies where to restore the serialized data. If NULL or empty, the
data is restored to the file system root. The path is relative to the file system root.
Existing files with the same name will be overwritten during deserialization. If the output
directory already exists, it will not emptied before restoring the data.
This function will not attempt to clean up any partially restored data if an error occurs during
the process.
Parameters
----------
pFS : (in)
A pointer to the file system object. Must not be NULL.
pSubdirectoryPath : (in, optional)
The path to the subdirectory where the data should be restored. Can be NULL or empty to
restore to the filesystem root. The path should use forward slashes ('/') as separators.
options : (in)
Options for the deserialization operation. These will be passed into `fs_file_open()` when
creating files. See `fs_file_open()` for details.
pInputStream : (in)
A pointer to the input stream containing the serialized data. Must not be NULL.
Return Value
------------
Returns FS_SUCCESS on success; any other result code otherwise.
See Also
--------
fs_serialize()
*/
FS_API fs_result fs_deserialize(fs* pFS, const char* pDirectoryPath, int options, fs_stream* pInputStream);
/* BEG fs_backend_posix.h */ /* BEG fs_backend_posix.h */
extern const fs_backend* FS_BACKEND_POSIX; extern const fs_backend* FS_BACKEND_POSIX;
/* END fs_backend_posix.h */ /* END fs_backend_posix.h */
@@ -3417,6 +3645,7 @@ read and write from different locations from the same fs_memory_stream object, y
seek before doing your read or write. You cannot read and write at the same time across seek before doing your read or write. You cannot read and write at the same time across
multiple threads for the same fs_memory_stream object. multiple threads for the same fs_memory_stream object.
*/ */
typedef struct fs_memory_stream fs_memory_stream; typedef struct fs_memory_stream fs_memory_stream;
struct fs_memory_stream struct fs_memory_stream
@@ -3460,6 +3689,7 @@ FS_API void* fs_sorted_search(const void* pKey, const void* pList, size_t count,
FS_API int fs_strncmp(const char* str1, const char* str2, size_t maxLen); FS_API int fs_strncmp(const char* str1, const char* str2, size_t maxLen);
FS_API int fs_strnicmp(const char* str1, const char* str2, size_t count); FS_API int fs_strnicmp(const char* str1, const char* str2, size_t count);
FS_API int fs_strncat_s(char* dst, size_t dstCap, const char* src, size_t count);
/* END fs_utils.h */ /* END fs_utils.h */
+535 -340
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -33,7 +33,7 @@ c89str load_file(const char* pFilePath)
resultFS = fs_file_open_and_read(NULL, pFilePath, FS_FORMAT_TEXT, (void**)&pFileData, &fileSize); resultFS = fs_file_open_and_read(NULL, pFilePath, FS_FORMAT_TEXT, (void**)&pFileData, &fileSize);
if (resultFS != FS_SUCCESS) { if (resultFS != FS_SUCCESS) {
printf("Failed to open %s: %s.\n", pFilePath, fs_result_to_string(resultFS)); printf("Failed to open %s: %s.\n", pFilePath, fs_result_description(resultFS));
return NULL; return NULL;
} }
@@ -50,7 +50,7 @@ int save_file(const char* pFilePath, c89str src)
resultFS = fs_file_open_and_write(NULL, pFilePath, src, c89str_len(src)); resultFS = fs_file_open_and_write(NULL, pFilePath, src, c89str_len(src));
if (resultFS != FS_SUCCESS) { if (resultFS != FS_SUCCESS) {
printf("Failed to save %s: %s.\n", pFilePath, fs_result_to_string(resultFS)); printf("Failed to save %s: %s.\n", pFilePath, fs_result_description(resultFS));
return (int)resultFS; return (int)resultFS;
} }