api: tweaks

This commit is contained in:
bnnm 2025-02-02 12:35:31 +01:00
parent be705d3b90
commit 388fce7c9f
12 changed files with 300 additions and 114 deletions

View File

@ -1,5 +1,4 @@
#include "../src/libvgmstream.h"
#if LIBVGMSTREAM_ENABLE
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
@ -59,7 +58,7 @@ static int api_example(const char* infile) {
libvgmstream_options_t opt = {
.libsf = get_streamfile(infile)
};
err = libvgmstream_open_song(lib, &opt);
err = libvgmstream_open_stream(lib, &opt);
// external SF is not needed after _open
libstreamfile_close(opt.libsf);
@ -137,7 +136,7 @@ static int api_example(const char* infile) {
printf("\n");
// close current streamfile before opening new ones, optional
//libvgmstream_close_song(lib);
//libvgmstream_close_stream(lib);
// process done
libvgmstream_free(lib);
@ -403,4 +402,3 @@ int main(int argc, char** argv) {
return 0;
}
#endif

View File

@ -1,6 +1,5 @@
#include "api_internal.h"
#include "mixing.h"
#if LIBVGMSTREAM_ENABLE
#define INTERNAL_BUF_SAMPLES 1024
@ -104,5 +103,3 @@ int api_get_sample_size(libvgmstream_sample_t sample_type) {
return 0x02;
}
}
#endif

View File

@ -1,7 +1,6 @@
#include "api_internal.h"
#include "sbuf.h"
#include "mixing.h"
#if LIBVGMSTREAM_ENABLE
static void load_vgmstream(libvgmstream_priv_t* priv, libvgmstream_options_t* opt) {
@ -106,14 +105,14 @@ static void update_format_info(libvgmstream_priv_t* priv) {
}
}
LIBVGMSTREAM_API int libvgmstream_open_song(libvgmstream_t* lib, libvgmstream_options_t* opt) {
LIBVGMSTREAM_API int libvgmstream_open_stream(libvgmstream_t* lib, libvgmstream_options_t* opt) {
if (!lib ||!lib->priv)
return LIBVGMSTREAM_ERROR_GENERIC;
if (!opt || !opt->libsf || opt->subsong_index < 0)
return LIBVGMSTREAM_ERROR_GENERIC;
// close loaded song if any + reset
libvgmstream_close_song(lib);
libvgmstream_close_stream(lib);
libvgmstream_priv_t* priv = lib->priv;
@ -131,7 +130,7 @@ LIBVGMSTREAM_API int libvgmstream_open_song(libvgmstream_t* lib, libvgmstream_op
}
LIBVGMSTREAM_API void libvgmstream_close_song(libvgmstream_t* lib) {
LIBVGMSTREAM_API void libvgmstream_close_stream(libvgmstream_t* lib) {
if (!lib || !lib->priv)
return;
@ -142,5 +141,3 @@ LIBVGMSTREAM_API void libvgmstream_close_song(libvgmstream_t* lib) {
libvgmstream_priv_reset(priv, true);
}
#endif

View File

@ -2,8 +2,6 @@
#include "mixing.h"
#include "render.h"
#if LIBVGMSTREAM_ENABLE
static bool reset_buf(libvgmstream_priv_t* priv) {
// state reset
@ -155,5 +153,3 @@ LIBVGMSTREAM_API void libvgmstream_reset(libvgmstream_t* lib) {
}
libvgmstream_priv_reset(priv, false);
}
#endif

View File

@ -1,5 +1,4 @@
#include "api_internal.h"
#if LIBVGMSTREAM_ENABLE
static int get_internal_log_level(libvgmstream_loglevel_t level) {
@ -60,7 +59,7 @@ LIBVGMSTREAM_API bool libvgmstream_is_valid(const char* filename, libvgmstream_v
vgmstream_ctx_valid_cfg icfg = {
.is_extension = cfg->is_extension,
.skip_standard = cfg->skip_default,
.skip_standard = cfg->skip_standard,
.reject_extensionless = cfg->reject_extensionless,
.accept_unknown = cfg->accept_unknown,
.accept_common = cfg->accept_common
@ -91,5 +90,3 @@ LIBVGMSTREAM_API int libvgmstream_get_title(libvgmstream_t* lib, libvgmstream_ti
LIBVGMSTREAM_API bool libvgmstream_is_virtual_filename(const char* filename) {
return vgmstream_is_virtual_filename(filename);
}
#endif

View File

@ -5,7 +5,6 @@
#include "../vgmstream.h"
#include "plugins.h"
#if LIBVGMSTREAM_ENABLE
#define LIBVGMSTREAM_OK 0
#define LIBVGMSTREAM_ERROR_GENERIC -1
@ -63,4 +62,3 @@ int api_get_sample_size(libvgmstream_sample_t sample_type);
STREAMFILE* open_api_streamfile(libstreamfile_t* libsf);
#endif
#endif

View File

@ -1,5 +1,4 @@
#include "api_internal.h"
#if LIBVGMSTREAM_ENABLE
static libstreamfile_t* libstreamfile_from_streamfile(STREAMFILE* sf);
@ -8,75 +7,75 @@ static libstreamfile_t* libstreamfile_from_streamfile(STREAMFILE* sf);
typedef struct {
int64_t offset;
int64_t size;
STREAMFILE* inner_sf;
STREAMFILE* sf;
char name[PATH_LIMIT];
} libsf_data_t;
} libsf_priv_t;
static int libsf_read(void* user_data, uint8_t* dst, int dst_size) {
libsf_data_t* data = user_data;
if (!data || !dst)
libsf_priv_t* priv = user_data;
if (!priv || !dst)
return 0;
int bytes = data->inner_sf->read(data->inner_sf, dst, data->offset, dst_size);
data->offset += bytes;
int bytes = priv->sf->read(priv->sf, dst, priv->offset, dst_size);
priv->offset += bytes;
return bytes;
}
static int64_t libsf_seek(void* user_data, int64_t offset, int whence) {
libsf_data_t* data = user_data;
if (!data)
libsf_priv_t* priv = user_data;
if (!priv)
return -1;
switch (whence) {
case LIBSTREAMFILE_SEEK_SET: /* absolute */
break;
case LIBSTREAMFILE_SEEK_CUR: /* relative to current */
offset += data->offset;
offset += priv->offset;
break;
case LIBSTREAMFILE_SEEK_END: /* relative to file end (should be negative) */
offset += data->size;
offset += priv->size;
break;
default:
break;
}
/* clamp offset like fseek */
if (offset > data->size)
offset = data->size;
if (offset > priv->size)
offset = priv->size;
else if (offset < 0)
offset = 0;
/* main seek */
data->offset = offset;
priv->offset = offset;
return 0;
}
static int64_t libsf_get_size(void* user_data) {
libsf_data_t* data = user_data;
if (!data)
libsf_priv_t* priv = user_data;
if (!priv)
return 0;
return data->size;
return priv->size;
}
static const char* libsf_get_name(void* user_data) {
libsf_data_t* data = user_data;
if (!data)
libsf_priv_t* priv = user_data;
if (!priv)
return NULL;
if (data->name[0] == '\0') {
data->inner_sf->get_name(data->inner_sf, data->name, sizeof(data->name));
if (priv->name[0] == '\0') {
priv->sf->get_name(priv->sf, priv->name, sizeof(priv->name));
}
return data->name;
return priv->name;
}
static libstreamfile_t* libsf_open(void* user_data, const char* filename) {
libsf_data_t* data = user_data;
if (!data || !data->inner_sf || !filename)
libsf_priv_t* priv = user_data;
if (!priv || !priv->sf || !filename)
return NULL;
STREAMFILE* sf = data->inner_sf->open(data->inner_sf, filename, 0);
STREAMFILE* sf = priv->sf->open(priv->sf, filename, 0);
if (!sf)
return NULL;
@ -93,11 +92,11 @@ static void libsf_close(libstreamfile_t* libsf) {
if (!libsf)
return;
libsf_data_t* data = libsf->user_data;
if (data && data->inner_sf) {
data->inner_sf->close(data->inner_sf);
libsf_priv_t* priv = libsf->user_data;
if (priv && priv->sf) {
priv->sf->close(priv->sf);
}
free(data);
free(priv);
free(libsf);
}
@ -106,7 +105,7 @@ static libstreamfile_t* libstreamfile_from_streamfile(STREAMFILE* sf) {
return NULL;
libstreamfile_t* libsf = NULL;
libsf_data_t* data = NULL;
libsf_priv_t* priv = NULL;
libsf = calloc(1, sizeof(libstreamfile_t));
if (!libsf) goto fail;
@ -118,12 +117,12 @@ static libstreamfile_t* libstreamfile_from_streamfile(STREAMFILE* sf) {
libsf->open = libsf_open;
libsf->close = libsf_close;
libsf->user_data = calloc(1, sizeof(libsf_data_t));
libsf->user_data = calloc(1, sizeof(libsf_priv_t));
if (!libsf->user_data) goto fail;
data = libsf->user_data;
data->inner_sf = sf;
data->size = get_streamfile_size(sf);
priv = libsf->user_data;
priv->sf = sf;
priv->size = get_streamfile_size(sf);
return libsf;
fail:
@ -145,4 +144,18 @@ LIBVGMSTREAM_API libstreamfile_t* libstreamfile_open_from_stdio(const char* file
return libsf;
}
#endif
LIBVGMSTREAM_API libstreamfile_t* libstreamfile_open_from_file(void* file_, const char* filename) {
FILE* file = file_;
STREAMFILE* sf = open_stdio_streamfile_by_file(file, filename);
if (!sf)
return NULL;
libstreamfile_t* libsf = libstreamfile_from_streamfile(sf);
if (!libsf) {
close_streamfile(sf);
return NULL;
}
return libsf;
}

195
src/base/api_libsf_cache.c Normal file
View File

@ -0,0 +1,195 @@
#include "api_internal.h"
/* libstreamfile_t for external use, that caches some external libsf */
/* value can be adjusted freely but 8k is a good enough compromise. */
#define CACHE_DEFAULT_BUFFER_SIZE 0x8000
typedef struct {
libstreamfile_t* libsf;
int64_t offset; /* last read offset (info) */
int64_t buf_offset; /* current buffer data start */
uint8_t* buf; /* data buffer */
size_t buf_size; /* max buffer size */
size_t valid_size; /* current buffer size */
size_t file_size; /* buffered file size */
char name[PATH_LIMIT];
} cache_priv_t;
static int cache_read(void* user_data, uint8_t* dst, int dst_size) {
cache_priv_t* priv = user_data;
size_t read_total = 0;
if (!dst || dst_size <= 0)
return 0;
/* is the part of the requested length in the buffer? */
if (priv->offset >= priv->buf_offset && priv->offset < priv->buf_offset + priv->valid_size) {
size_t buf_limit;
int buf_into = (int)(priv->offset - priv->buf_offset);
buf_limit = priv->valid_size - buf_into;
if (buf_limit > dst_size)
buf_limit = dst_size;
memcpy(dst, priv->buf + buf_into, buf_limit);
read_total += buf_limit;
dst_size -= buf_limit;
priv->offset += buf_limit;
dst += buf_limit;
}
/* read the rest of the requested length */
while (dst_size > 0) {
size_t buf_limit;
/* ignore requests at EOF */
if (priv->offset >= priv->file_size) {
//offset = priv->file_size; /* seems fseek doesn't clamp offset */
//VGM_ASSERT_ONCE(offset > sf->file_size, "STDIO: reading over file_size 0x%x @ 0x%lx + 0x%x\n", sf->file_size, offset, length);
break;
}
/* position to new offset */
priv->libsf->seek(priv, priv->offset, 0);
/* fill the buffer (offset now is beyond buf_offset) */
priv->buf_offset = priv->offset;
priv->valid_size = priv->libsf->read(priv, priv->buf, priv->buf_size);
/* decide how much must be read this time */
if (dst_size > priv->buf_size)
buf_limit = priv->buf_size;
else
buf_limit = dst_size;
/* give up on partial reads (EOF) */
if (priv->valid_size < buf_limit) {
memcpy(dst, priv->buf, priv->valid_size);
priv->offset += priv->valid_size;
read_total += priv->valid_size;
break;
}
/* use the new buffer */
memcpy(dst, priv->buf, buf_limit);
priv->offset += buf_limit;
read_total += buf_limit;
dst_size -= buf_limit;
dst += buf_limit;
}
return read_total;
}
static int64_t cache_seek(void* user_data, int64_t offset, int whence) {
cache_priv_t* priv = user_data;
switch (whence) {
case LIBSTREAMFILE_SEEK_SET: /* absolute */
break;
case LIBSTREAMFILE_SEEK_CUR: /* relative to current */
offset += priv->offset;
break;
case LIBSTREAMFILE_SEEK_END: /* relative to file end (should be negative) */
offset += priv->file_size;
break;
default:
break;
}
/* clamp offset like fseek */
if (offset > priv->file_size)
offset = priv->file_size;
else if (offset < 0)
offset = 0;
/* main seek */
priv->offset = offset;
return 0;
}
static int64_t cache_get_size(void* user_data) {
cache_priv_t* priv = user_data;
return priv->file_size;
}
static const char* cache_get_name(void* user_data) {
cache_priv_t* priv = user_data;
return priv->name;
}
static libstreamfile_t* cache_open(void* user_data, const char* filename) {
cache_priv_t* priv = user_data;
if (!priv || !priv->libsf || !filename)
return NULL;
libstreamfile_t* inner_libsf = priv->libsf->open(priv->libsf->user_data, filename);
if (!inner_libsf)
return NULL;
libstreamfile_t* libsf = libstreamfile_open_buffered(inner_libsf, priv->buf_size);
if (!libsf) {
libstreamfile_close(inner_libsf);
return NULL;
}
return libsf;
}
static void cache_close(libstreamfile_t* libsf) {
if (!libsf)
return;
cache_priv_t* priv = libsf->user_data;
if (priv && priv->libsf) {
libstreamfile_close(priv->libsf);
}
if (priv) {
free(priv->buf);
}
free(priv);
free(libsf);
}
LIBVGMSTREAM_API libstreamfile_t* libstreamfile_open_buffered(libstreamfile_t* ext_libsf, int buf_size) {
if (!ext_libsf)
return NULL;
if (!buf_size)
buf_size = CACHE_DEFAULT_BUFFER_SIZE;
libstreamfile_t* libsf = NULL;
cache_priv_t* priv = NULL;
libsf = calloc(1, sizeof(libstreamfile_t));
if (!libsf) goto fail;
libsf->read = cache_read;
libsf->seek = cache_seek;
libsf->get_size = cache_get_size;
libsf->get_name = cache_get_name;
libsf->open = cache_open;
libsf->close = cache_close;
libsf->user_data = calloc(1, sizeof(cache_priv_t));
if (!libsf->user_data) goto fail;
priv = libsf->user_data;
priv->libsf = ext_libsf;
priv->buf_size = buf_size;
priv->buf = calloc(buf_size, sizeof(uint8_t));
if (!priv->buf) goto fail;
priv->file_size = priv->libsf->get_size(priv->libsf->user_data);
snprintf(priv->name, sizeof(priv->name), "%s", priv->libsf->get_name(priv->libsf->user_data));
priv->name[sizeof(priv->name) - 1] = '\0';
return libsf;
fail:
cache_close(libsf);
return NULL;
}

View File

@ -1,5 +1,4 @@
#include "api_internal.h"
#if LIBVGMSTREAM_ENABLE
typedef struct {
VGMSTREAM_TAGS* vtags;
@ -65,5 +64,3 @@ LIBVGMSTREAM_API void libvgmstream_tags_free(libvgmstream_tags_t* tags) {
free(tags->priv);
free(tags);
}
#endif

View File

@ -1,5 +1,4 @@
#include "api_internal.h"
#if LIBVGMSTREAM_ENABLE
/* STREAMFILE for internal use, that bridges calls to external libstreamfile_t */
@ -90,5 +89,3 @@ static STREAMFILE* open_api_streamfile_internal(libstreamfile_t* libsf, bool ext
STREAMFILE* open_api_streamfile(libstreamfile_t* libsf) {
return open_api_streamfile_internal(libsf, true);
}
#endif

View File

@ -1,18 +1,16 @@
#ifndef _LIBVGMSTREAM_H_
#define _LIBVGMSTREAM_H_
#define LIBVGMSTREAM_ENABLE 1
#if LIBVGMSTREAM_ENABLE
/* libvgmstream: vgmstream's public API
*
* Basic usage (also see api_example.c):
* - libvgmstream_init(...) // create context
* - libvgmstream_setup(...) // setup config (if needed)
* - libvgmstream_open_song(...) // open format
* - libvgmstream_render(...) // main decode
* - output samples + repeat libvgmstream_render until stream is done
* - libvgmstream_free(...) // cleanup
* - libvgmstream_init(...) // create context
* - libvgmstream_setup(...) // setup config (if needed)
* - libvgmstream_open_stream(...) // open format
* - libvgmstream_render(...) // main decode
* - output samples + repeat libvgmstream_render until 'done' is set
* - libvgmstream_free(...) // cleanup
*
* By default vgmstream behaves like a decoder (returns samples until stream end), but you can configure
* it to loop N times or even downmix. In other words, it also behaves a bit like a player.
@ -56,9 +54,9 @@
* - only refers to the API itself, changes related to formats/etc don't alter this
* - vgmstream's features are mostly stable, but this API may be tweaked from time to time
*/
#define LIBVGMSTREAM_API_VERSION_MAJOR 1 // breaking API/ABI changes
#define LIBVGMSTREAM_API_VERSION_MINOR 0 // compatible API/ABI changes
#define LIBVGMSTREAM_API_VERSION_PATCH 0 // fixes
#define LIBVGMSTREAM_API_VERSION_MAJOR 0x01 // breaking API/ABI changes
#define LIBVGMSTREAM_API_VERSION_MINOR 0x00 // compatible API/ABI changes
#define LIBVGMSTREAM_API_VERSION_PATCH 0x00 // fixes
/* Current API version, for dynamic checks. returns hex value: 0xMMmmpppp = MM-major, mm-minor, pppp-patch
* - use when loading vgmstream as a dynamic library to ensure API/ABI compatibility
@ -75,10 +73,10 @@ LIBVGMSTREAM_API uint32_t libvgmstream_get_version(void);
/* interleaved samples: buf[0]=ch0, buf[1]=ch1, buf[2]=ch0, buf[3]=ch0, ... */
typedef enum {
LIBVGMSTREAM_SAMPLE_PCM16 = 0x01,
LIBVGMSTREAM_SAMPLE_PCM24 = 0x02,
LIBVGMSTREAM_SAMPLE_PCM32 = 0x03,
LIBVGMSTREAM_SAMPLE_FLOAT = 0x04,
LIBVGMSTREAM_SAMPLE_PCM16 = 1,
LIBVGMSTREAM_SAMPLE_PCM24 = 2,
LIBVGMSTREAM_SAMPLE_PCM32 = 3,
LIBVGMSTREAM_SAMPLE_FLOAT = 4,
} libvgmstream_sample_t;
/* current song info, may be copied around (values are info-only) */
@ -86,12 +84,11 @@ typedef struct {
/* main (always set) */
int channels; // output channels
int sample_rate; // output sample rate
libvgmstream_sample_t sample_type; // output buffer's sample type
int sample_size; // derived from sample_type (pcm16=0x02, float=0x04, etc)
/* extra info (may be 0 if not known or not relevant) */
uint32_t channel_layout; // standard WAVE bitflags
uint32_t channel_layout; // standard WAVE bitflags, 0 if unset or non-standard
int subsong_index; // 0 = none, N = loaded subsong N (1=first)
int subsong_count; // 0 = format has no concept of subsongs, N = has N subsongs
@ -110,6 +107,7 @@ typedef struct {
bool loop_flag; // if file loops
// ** false + defined loops means looping was forcefully disabled
// ** true + undefined loops means the file loops in a way not representable by loop points
//bool rough_samples; // signal cases where loop points or sample count can't exactly reflect actual behavior
bool play_forever; // if file loops forever based on current config (meaning _play never stops)
int64_t play_samples; // totals after all calculations (after applying loop/fade/etc config)
@ -117,32 +115,29 @@ typedef struct {
// ** if play_forever is set this is still provided for reference based on non-forever config
int stream_bitrate; // average bitrate of the subsong (slightly bloated vs codec_bitrate; incorrect in rare cases)
//int codec_bitrate; // average bitrate of the codec data
// ** not possible / slow to calculate in most cases
//int codec_bitrate; // average bitrate of the codec data [not possible / slow to calculate in most cases]
/* descriptions */
char codec_name[128]; //
char layout_name[128]; //
char meta_name[128]; // (not internal "tag" metadata)
char stream_name[256]; // some internal name or representation, not always useful
// ** these are a bit big for a struct, but the typical use case of vgsmtream is opening a file > immediately
char codec_name[128]; // represention of main decoder name
char layout_name[128]; // represention of how data is laid out
char meta_name[128]; // represention of format's name (not internal "tag" metadata)
char stream_name[256]; // stream's internal name or representation (not an internal filename not always useful)
// ** these are a bit big for a struct, but the typical use case of vgmstream is opening a file > immediately
// query description and since libvgmstream returns its own copy it shouldn't be too much of a problem
// ** (may be separated later)
/* misc */
//bool rough_samples; // signal cases where loop points or sample count can't exactly reflect actual behavior
int format_id; // when reopening subfiles or similar formats without checking other all possible formats
// ** this value WILL change without warning between vgmstream versions/commits
// ** this value WILL change without warning between vgmstream versions/commits, but usually only add
} libvgmstream_format_t;
/* current decoder state */
typedef struct {
void* buf; // current decoded buf (valid after _decode until next call; may change between calls)
int buf_samples; // current buffer samples (0 is possible in some cases)
int buf_bytes; // current buffer bytes (channels * sample_size * samples)
bool done; // when stream is done based on config
bool done; // when stream is done, based on config
// ** note that with play_forever this flag is never set
} libvgmstream_decoder_t;
@ -183,7 +178,7 @@ typedef struct {
double fade_time; // fade period after target loops
double fade_delay; // fade delay after target loops
int auto_downmix_channels; // downmixing if vgmstream's channels are higher than value
int auto_downmix_channels; // downmix if vgmstream's channels are higher than value
// ** for players that can only handle N channels
// ** this type of downmixing is very simplistic and not recommended
@ -218,11 +213,11 @@ typedef struct {
* - returns < 0 on error (file not recognised, invalid subsong index, etc)
* - will close currently loaded song if needed
*/
LIBVGMSTREAM_API int libvgmstream_open_song(libvgmstream_t* lib, libvgmstream_options_t* open_options);
LIBVGMSTREAM_API int libvgmstream_open_stream(libvgmstream_t* lib, libvgmstream_options_t* open_options);
/* Closes current song; may still use libvgmstream to open other songs
*/
LIBVGMSTREAM_API void libvgmstream_close_song(libvgmstream_t* lib);
LIBVGMSTREAM_API void libvgmstream_close_stream(libvgmstream_t* lib);
/* Decodes next batch of samples
@ -289,14 +284,14 @@ LIBVGMSTREAM_API const char** libvgmstream_get_common_extensions(size_t* size);
typedef struct {
bool is_extension; /* set if filename is just an extension */
bool skip_default; /* set if shouldn't check default formats */
bool reject_extensionless; /* set if player can't play extensionless files */
bool accept_unknown; /* set to allow any extension (for txth) */
bool accept_common; /* set to allow known-but-common extension (when player has plugin priority) */
bool is_extension; /* set if filename is just an extension (otherwise may be seen as 'extensionless') */
bool skip_standard; /* disable extension check vs default formats */
bool reject_extensionless; /* enable if player can't play extensionless files */
bool accept_unknown; /* enable to allow any extension even if not known by vgmstream (for .txth) */
bool accept_common; /* enable to allow known-but-common extension (when player has plugin priority) */
} libvgmstream_valid_t;
/* Returns if vgmstream can parse a filename by extension, to reject some files earlier
/* Returns if vgmstream can parse a filename by extension, to reject some files earlier.
* - doesn't check file contents (that's only done on _open)
* - config may be NULL
* - mainly for plugins that want to fail early; libvgmstream doesn't use this
@ -363,4 +358,3 @@ LIBVGMSTREAM_API void libvgmstream_tags_free(libvgmstream_tags_t* tags);
#endif
#endif

View File

@ -1,13 +1,15 @@
#ifndef _LIBVGMSTREAM_STREAMFILE_H_
#define _LIBVGMSTREAM_STREAMFILE_H_
#include "libvgmstream.h"
#if LIBVGMSTREAM_ENABLE
/* vgmstream's IO API, defined as a "streamfile" (SF).
/* vgmstream's IO API, defined as a "streamfile" ('SF').
*
* vgmstream roughly assumes there is an underlying filesystem (as usual in games): seeking + reading from arbitrary offsets,
* opening companion files, filename tests, etc. If your case is too different you may still create a partial streamfile: returning
* a fake filename, only handling "open" that reopens itself (same filename), etc. Simpler formats will probably work just fine.
* vgmstream mostly assumes there is an underlying filesystem (as usual in games), plus given video game formats are
* often ill-defined it needs extra ops to handle edge cases: seeking + reading from arbitrary offsets, opening companion
* files, filename/size tests, etc.
*
* If your case is too different you may still create a partial streamfile: returning a fake filename, only handling "open"
* that reopens itself (same filename), etc. Simpler formats should work fine.
*/
@ -19,7 +21,7 @@ enum {
//LIBSTREAMFILE_SEEK_GET_SIZE = 5,
};
// maybe "libvgmstream_streamfile_t" but it was getting unwieldly
// should be "libvgmstream_streamfile_t" but it was getting unwieldly
typedef struct libstreamfile_t {
//uint32_t flags; // info flags for vgmstream
void* user_data; // any internal structure
@ -30,11 +32,11 @@ typedef struct libstreamfile_t {
int (*read)(void* user_data, uint8_t* dst, int dst_size);
/* seek to offset
* - note that vgmstream needs to seek + read fairly often (to be optimized later)
* - note that vgmstream needs to seek + read fairly often (to be optimized someday)
*/
int64_t (*seek)(void* user_data, int64_t offset, int whence);
/* get max offset (typically for checks or calculations)
/* get max offset (typically for checks or sample calculations)
*/
int64_t (*get_size)(void* user_data);
@ -47,7 +49,7 @@ typedef struct libstreamfile_t {
*/
struct libstreamfile_t* (*open)(void* user_data, const char* filename);
/* free current SF (needed for copied streamfiles) */
/* free current SF */
void (*close)(struct libstreamfile_t* libsf);
} libstreamfile_t;
@ -60,8 +62,13 @@ static inline void libstreamfile_close(libstreamfile_t* libsf) {
libsf->close(libsf);
}
/* base libstreamfile using STDIO (cached) */
LIBVGMSTREAM_API libstreamfile_t* libstreamfile_open_from_stdio(const char* filename);
#endif
/* base libstreamfile using a FILE (cached); the filename is needed as metadata */
LIBVGMSTREAM_API libstreamfile_t* libstreamfile_open_from_file(void* file, const char* filename);
/* cached streamfile (recommended to wrap your external libsf since vgmstream needs to seek a lot) */
LIBVGMSTREAM_API libstreamfile_t* libstreamfile_open_buffered(libstreamfile_t* ext_libsf, int buf_size);
#endif