Merge pull request #1595 from bnnm/api-misc10

- Allow TXTH subsongs with stream names
- cleanup
This commit is contained in:
bnnm 2024-09-08 21:14:22 +02:00 committed by GitHub
commit cdfaf59bfc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 77 additions and 55 deletions

View File

@ -303,11 +303,13 @@ id_check = (value)
```
#### NUMBER OF CHANNELS [REQUIRED]
How many audio channels the file has, typically 2 (stereo).
```
channels = (value)
```
#### MUSIC FREQUENCY [REQUIRED]
#### AUDIO FREQUENCY [REQUIRED]
Number of samples per second, typically 48000/44100/32000/24000/22050/11025.
```
sample_rate = (value)
```

View File

@ -71,9 +71,9 @@ static const char* libsf_get_name(void* user_data) {
return data->name;
}
struct libstreamfile_t* libsf_open(void* user_data, const char* filename) {
static libstreamfile_t* libsf_open(void* user_data, const char* filename) {
libsf_data_t* data = user_data;
if (!data || !data->inner_sf)
if (!data || !data->inner_sf || !filename)
return NULL;
STREAMFILE* sf = data->inner_sf->open(data->inner_sf, filename, 0);
@ -89,7 +89,7 @@ struct libstreamfile_t* libsf_open(void* user_data, const char* filename) {
return libsf;
}
static void libsf_close(struct libstreamfile_t* libsf) {
static void libsf_close(libstreamfile_t* libsf) {
if (!libsf)
return;

View File

@ -31,8 +31,11 @@ static void seek_force_decode(VGMSTREAM* vgmstream, int samples) {
to_do = buf_samples;
sbuf_tmp.samples = to_do;
render_layout(&sbuf_tmp, vgmstream);
/* no mixing */
samples -= to_do;
sbuf_tmp.filled = 0; // discard buf
}
}

View File

@ -432,11 +432,16 @@ fail:
/* FFmpeg internals (roughly) for reference:
*
* // metadata info first extracted
* AVFormatContext // base info extracted from input file
* AVStream // substreams
* AVCodecParameters // codec id, channels, format, ...
*
* AVCodecContext // sample rate and general info
* // codec info passed to 'decode' functions
* AVCodecContext // codec's sample rate, priv data and general info
* AVPacket // encoded data (1 or N frames) passed to decoder
* AVFrame // decoded data (audio samples + channels + etc) received from decoder
* // (bufs are internally managed)
*
* - open avformat to get all possible format info (needs file or custom IO)
* - open avcodec based on target stream + codec info from avformat
@ -444,8 +449,13 @@ fail:
* - read next frame into packet via avformat
* - decode packet via avcodec
* - handle samples
*/
*
* In FFmpeg, each "avformat" defines an struct with format info/config and read_probe (detection) + read_header
* (setup format/codec params) + read_packet (demux single frame) functions. Meanwhile "avcodec" defines an struct
* with config and decode_init/close (setup, may use first block's data to init itself or some extradata from
* avformat) + decode_frame (loads AVFrame from AVPacket) + decode_flush (reset state).
* Codec/demuxer contexts aren't alloc'd manually and instead they declare priv data size.
*/
static int init_ffmpeg_config(ffmpeg_codec_data* data, int target_subsong, int reset) {
int errcode = 0;

View File

@ -35,7 +35,7 @@ void render_vgmstream_layered(sbuf_t* sdst, VGMSTREAM* vgmstream) {
samples_to_do = sdst->samples - sdst->filled;
if (samples_to_do <= 0) { /* when decoding more than num_samples */
VGM_LOG_ONCE("LAYERED: wrong samples_to_do\n");
VGM_LOG_ONCE("LAYERED: wrong %i samples_to_do (%i filled vs %i samples)\n", samples_to_do, sdst->filled, sdst->samples);
goto decode_fail;
}

View File

@ -6,16 +6,13 @@ static bool bink_get_info(STREAMFILE* sf, int target_subsong, int* p_total_subso
/* BINK 1/2 - RAD Game Tools movies (audio/video format) */
VGMSTREAM* init_vgmstream_bik(STREAMFILE* sf) {
VGMSTREAM * vgmstream = NULL;
int channels = 0, loop_flag = 0, sample_rate = 0, num_samples = 0;
int total_subsongs = 0, target_subsong = sf->stream_index;
size_t stream_size;
VGMSTREAM* vgmstream = NULL;
/* checks */
/* bink1/2 header, followed by version-char (audio is the same) */
if ((read_u32be(0x00,sf) & 0xffffff00) != get_id32be("BIK\0") &&
(read_u32be(0x00,sf) & 0xffffff00) != get_id32be("KB2\0"))
goto fail;
return NULL;
/* .bik/bk2: standard
* .bik2: older?
@ -25,7 +22,13 @@ VGMSTREAM* init_vgmstream_bik(STREAMFILE* sf) {
* .vid: Etrange Libellules games [Alice in Wonderland (PC)]
* .bika: fake extension for demuxed audio */
if (!check_extensions(sf,"bik,bk2,bik2,ps3,xmv,xen,vid,bika"))
goto fail;
return NULL;
/* this typically handles regular or demuxed videos, but .bik with a 4x4 video made for audio do exist [Viva Piñata (DS)] */
int channels = 0, loop_flag = 0, sample_rate = 0, num_samples = 0;
int total_subsongs = 0, target_subsong = sf->stream_index;
size_t stream_size;
/* find target stream info and samples */
if (!bink_get_info(sf, target_subsong, &total_subsongs, &stream_size, &channels, &sample_rate, &num_samples))

View File

@ -243,7 +243,7 @@ VGMSTREAM* init_vgmstream_nus3bank(STREAMFILE* sf) {
vgmstream->num_streams = total_subsongs;
if (name_offset)
read_string(vgmstream->stream_name, name_size, name_offset, sf);
read_string_sz(vgmstream->stream_name, STREAM_NAME_SIZE, name_size, name_offset, sf);
close_streamfile(temp_sf);

View File

@ -197,7 +197,7 @@ VGMSTREAM* init_vgmstream_sqex_scd(STREAMFILE* sf) {
/* actual Ogg init */
ogg_vgmstream = init_vgmstream_ogg_vorbis_config(sf, start_offset, &ovmi);
if (ogg_vgmstream && name_offset)
read_string(ogg_vgmstream->stream_name, PATH_LIMIT, name_offset, sf);
read_string(ogg_vgmstream->stream_name, STREAM_NAME_SIZE, name_offset, sf);
return ogg_vgmstream;
}
#endif

View File

@ -339,58 +339,45 @@ static void sead_cat(char* dst, int dst_max, const char* src) {
}
static void build_readable_sab_name(sead_header_t* sead, STREAMFILE* sf, uint32_t sndname_offset, uint32_t sndname_size) {
char * buf = sead->readable_name;
char* buf = sead->readable_name;
int buf_size = sizeof(sead->readable_name);
char descriptor[255], name[255];
if (sead->filename_size > 255 || sndname_size > 255)
goto fail;
char descriptor[256], name[256];
if (buf[0] == '\0') { /* init */
read_string(descriptor,sead->filename_size+1, sead->filename_offset, sf);
read_string(name, sndname_size+1, sndname_offset, sf);
read_string_sz(descriptor, sizeof(descriptor), sead->filename_size, sead->filename_offset, sf);
read_string_sz(name, sizeof(name), sndname_size, sndname_offset, sf);
snprintf(buf,buf_size, "%s/%s", descriptor, name);
}
else { /* add */
read_string(name, sndname_size+1, sndname_offset, sf);
read_string_sz(name, sizeof(name), sndname_size, sndname_offset, sf);
sead_cat(buf, buf_size, "; ");
sead_cat(buf, buf_size, name);
}
return;
fail:
VGM_LOG("SEAD: bad sab name found\n");
}
static void build_readable_mab_name(sead_header_t* sead, STREAMFILE* sf) {
char * buf = sead->readable_name;
char* buf = sead->readable_name;
int buf_size = sizeof(sead->readable_name);
char descriptor[255], name[255], mode[255];
char descriptor[256], name[256], mode[256];
if (sead->filename_size > 255 || sead->muscname_size > 255 || sead->sectname_size > 255 || sead->modename_size > 255)
goto fail;
read_string(descriptor,sead->filename_size+1,sead->filename_offset, sf);
//read_string(filename,sead->muscname_size+1,sead->muscname_offset, sf); /* same as filename, not too interesting */
read_string_sz(descriptor, sizeof(descriptor), sead->filename_size, sead->filename_offset, sf);
//read_string_sz(filename, sizeof(filename), sead->muscname_size, sead->muscname_offset, sf); /* same as filename, not too interesting */
if (sead->sectname_offset)
read_string(name,sead->sectname_size+1,sead->sectname_offset, sf);
read_string_sz(name, sizeof(name), sead->sectname_size,sead->sectname_offset, sf);
else if (sead->instname_offset)
read_string(name,sead->instname_size+1,sead->instname_offset, sf);
read_string_sz(name, sizeof(name), sead->instname_size, sead->instname_offset, sf);
else
strcpy(name, "?");
if (sead->modename_offset > 0)
read_string(mode,sead->modename_size+1,sead->modename_offset, sf);
read_string_sz(mode, sizeof(mode), sead->modename_size,sead->modename_offset, sf);
/* default mode in most files */
if (sead->modename_offset == 0 || strcmp(mode, "Mode") == 0 || strcmp(mode, "Mode0") == 0)
snprintf(buf,buf_size, "%s/%s", descriptor, name);
else
snprintf(buf,buf_size, "%s/%s/%s", descriptor, name, mode);
return;
fail:
VGM_LOG("SEAD: bad mab name found\n");
}
static void parse_sead_mab_name(sead_header_t* sead, STREAMFILE* sf) {

View File

@ -330,8 +330,7 @@ VGMSTREAM* init_vgmstream_txth(STREAMFILE* sf) {
vgmstream->num_streams = txth.subsong_count;
vgmstream->stream_size = txth.data_size;
if (txth.name_offset_set) {
size_t name_size = txth.name_size ? txth.name_size + 1 : STREAM_NAME_SIZE;
read_string(vgmstream->stream_name,name_size, txth.name_offset,txth.sf_head);
read_string_sz(vgmstream->stream_name, STREAM_NAME_SIZE, txth.name_size, txth.name_offset, txth.sf_head);
}
/* codec specific (taken from GENH with minimal changes) */
@ -780,6 +779,9 @@ static VGMSTREAM* init_subfile(txth_header* txth) {
}
//todo: other combos with subsongs + subfile?
if (txth->name_offset_set) {
read_string_sz(vgmstream->stream_name, STREAM_NAME_SIZE, txth->name_size, txth->name_offset, txth->sf_head);
}
close_streamfile(sf_sub);
return vgmstream;

View File

@ -120,29 +120,41 @@ size_t read_bom(STREAMFILE* sf) {
return 0x00;
}
size_t read_string(char* buf, size_t buf_size, off_t offset, STREAMFILE* sf) {
size_t pos;
size_t read_string_sz(char* buf, size_t buf_size, size_t string_size, off_t offset, STREAMFILE* sf) {
for (pos = 0; pos < buf_size; pos++) {
// read up to buf, or stop before if size is set; in either case will stop at 0x00
size_t max_size = buf_size;
if (string_size > 0 && string_size < max_size)
max_size = string_size + 1;
for (size_t pos = 0; pos < max_size; pos++) {
uint8_t byte = read_u8(offset + pos, sf);
char c = (char)byte;
if (buf) buf[pos] = c;
if (c == '\0')
if (buf) buf[pos] = (char)byte;
// done
if (byte == '\0')
return pos;
if (pos+1 == buf_size) { /* null at maxsize and don't validate (expected to be garbage) */
// null at maxsize and don't validate (expected to be garbage)
if (pos + 1 == max_size) {
if (buf) buf[pos] = '\0';
return buf_size;
return max_size;
}
/* UTF-8 only goes to 0x7F, but allow a bunch of Windows-1252 codes that some games use */
// UTF-8 only goes to 0x7F, but allow a bunch of Windows-1252 codes that some games use
if (byte < 0x20 || byte > 0xF0)
goto fail;
break;
}
fail:
// error or wrong max_size
if (buf) buf[0] = '\0';
return 0;
}
size_t read_string(char* buf, size_t buf_size, off_t offset, STREAMFILE* sf) {
return read_string_sz(buf, buf_size, 0, offset, sf);
}
size_t read_string_utf16(char* buf, size_t buf_size, off_t offset, STREAMFILE* sf, int big_endian) {
size_t pos, offpos;
read_u16_t read_u16 = big_endian ? read_u16be : read_u16le;

View File

@ -11,7 +11,10 @@ size_t read_line(char* buf, int buf_size, off_t offset, STREAMFILE* sf, int* p_l
/* skip BOM if needed */
size_t read_bom(STREAMFILE* sf);
/* reads a c-string (ANSI only), up to bufsize or NULL, returning size. buf is optional (works as get_string_size). */
/* Reads a C-string (ANSI only), up to buf_size, string_size (no need to include null char), or NULL (whichever is happens first).
* Returning size and buf is optional (works as get_string_size without it). Will always null-terminate the string. */
size_t read_string_sz(char* buf, size_t buf_size, size_t string_size, off_t offset, STREAMFILE* sf);
/* same but without known string_size */
size_t read_string(char* buf, size_t buf_size, off_t offset, STREAMFILE* sf);
/* reads a UTF16 string... but actually only as ANSI (discards the upper byte) */
size_t read_string_utf16(char* buf, size_t buf_size, off_t offset, STREAMFILE* sf, int big_endian);