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] #### NUMBER OF CHANNELS [REQUIRED]
How many audio channels the file has, typically 2 (stereo).
``` ```
channels = (value) channels = (value)
``` ```
#### MUSIC FREQUENCY [REQUIRED] #### AUDIO FREQUENCY [REQUIRED]
Number of samples per second, typically 48000/44100/32000/24000/22050/11025.
``` ```
sample_rate = (value) sample_rate = (value)
``` ```

View File

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

View File

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

View File

@ -432,11 +432,16 @@ fail:
/* FFmpeg internals (roughly) for reference: /* FFmpeg internals (roughly) for reference:
* *
* // metadata info first extracted
* AVFormatContext // base info extracted from input file * AVFormatContext // base info extracted from input file
* AVStream // substreams * AVStream // substreams
* AVCodecParameters // codec id, channels, format, ... * 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 avformat to get all possible format info (needs file or custom IO)
* - open avcodec based on target stream + codec info from avformat * - open avcodec based on target stream + codec info from avformat
@ -444,8 +449,13 @@ fail:
* - read next frame into packet via avformat * - read next frame into packet via avformat
* - decode packet via avcodec * - decode packet via avcodec
* - handle samples * - 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) { static int init_ffmpeg_config(ffmpeg_codec_data* data, int target_subsong, int reset) {
int errcode = 0; 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; samples_to_do = sdst->samples - sdst->filled;
if (samples_to_do <= 0) { /* when decoding more than num_samples */ 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; 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) */ /* BINK 1/2 - RAD Game Tools movies (audio/video format) */
VGMSTREAM* init_vgmstream_bik(STREAMFILE* sf) { VGMSTREAM* init_vgmstream_bik(STREAMFILE* sf) {
VGMSTREAM * vgmstream = NULL; 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;
/* checks */ /* checks */
/* bink1/2 header, followed by version-char (audio is the same) */ /* bink1/2 header, followed by version-char (audio is the same) */
if ((read_u32be(0x00,sf) & 0xffffff00) != get_id32be("BIK\0") && if ((read_u32be(0x00,sf) & 0xffffff00) != get_id32be("BIK\0") &&
(read_u32be(0x00,sf) & 0xffffff00) != get_id32be("KB2\0")) (read_u32be(0x00,sf) & 0xffffff00) != get_id32be("KB2\0"))
goto fail; return NULL;
/* .bik/bk2: standard /* .bik/bk2: standard
* .bik2: older? * .bik2: older?
@ -25,7 +22,13 @@ VGMSTREAM* init_vgmstream_bik(STREAMFILE* sf) {
* .vid: Etrange Libellules games [Alice in Wonderland (PC)] * .vid: Etrange Libellules games [Alice in Wonderland (PC)]
* .bika: fake extension for demuxed audio */ * .bika: fake extension for demuxed audio */
if (!check_extensions(sf,"bik,bk2,bik2,ps3,xmv,xen,vid,bika")) 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 */ /* find target stream info and samples */
if (!bink_get_info(sf, target_subsong, &total_subsongs, &stream_size, &channels, &sample_rate, &num_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; vgmstream->num_streams = total_subsongs;
if (name_offset) 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); close_streamfile(temp_sf);

View File

@ -197,7 +197,7 @@ VGMSTREAM* init_vgmstream_sqex_scd(STREAMFILE* sf) {
/* actual Ogg init */ /* actual Ogg init */
ogg_vgmstream = init_vgmstream_ogg_vorbis_config(sf, start_offset, &ovmi); ogg_vgmstream = init_vgmstream_ogg_vorbis_config(sf, start_offset, &ovmi);
if (ogg_vgmstream && name_offset) 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; return ogg_vgmstream;
} }
#endif #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) { 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); int buf_size = sizeof(sead->readable_name);
char descriptor[255], name[255]; char descriptor[256], name[256];
if (sead->filename_size > 255 || sndname_size > 255)
goto fail;
if (buf[0] == '\0') { /* init */ if (buf[0] == '\0') { /* init */
read_string(descriptor,sead->filename_size+1, sead->filename_offset, sf); read_string_sz(descriptor, sizeof(descriptor), sead->filename_size, sead->filename_offset, sf);
read_string(name, sndname_size+1, sndname_offset, sf); read_string_sz(name, sizeof(name), sndname_size, sndname_offset, sf);
snprintf(buf,buf_size, "%s/%s", descriptor, name); snprintf(buf,buf_size, "%s/%s", descriptor, name);
} }
else { /* add */ 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, "; ");
sead_cat(buf, buf_size, name); 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) { 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); 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) read_string_sz(descriptor, sizeof(descriptor), sead->filename_size, sead->filename_offset, sf);
goto fail; //read_string_sz(filename, sizeof(filename), sead->muscname_size, sead->muscname_offset, sf); /* same as filename, not too interesting */
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 */
if (sead->sectname_offset) 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) 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 else
strcpy(name, "?"); strcpy(name, "?");
if (sead->modename_offset > 0) 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 */ /* default mode in most files */
if (sead->modename_offset == 0 || strcmp(mode, "Mode") == 0 || strcmp(mode, "Mode0") == 0) if (sead->modename_offset == 0 || strcmp(mode, "Mode") == 0 || strcmp(mode, "Mode0") == 0)
snprintf(buf,buf_size, "%s/%s", descriptor, name); snprintf(buf,buf_size, "%s/%s", descriptor, name);
else else
snprintf(buf,buf_size, "%s/%s/%s", descriptor, name, mode); 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) { 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->num_streams = txth.subsong_count;
vgmstream->stream_size = txth.data_size; vgmstream->stream_size = txth.data_size;
if (txth.name_offset_set) { if (txth.name_offset_set) {
size_t name_size = txth.name_size ? txth.name_size + 1 : STREAM_NAME_SIZE; read_string_sz(vgmstream->stream_name, STREAM_NAME_SIZE, txth.name_size, txth.name_offset, txth.sf_head);
read_string(vgmstream->stream_name,name_size, txth.name_offset,txth.sf_head);
} }
/* codec specific (taken from GENH with minimal changes) */ /* 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? //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); close_streamfile(sf_sub);
return vgmstream; return vgmstream;

View File

@ -120,29 +120,41 @@ size_t read_bom(STREAMFILE* sf) {
return 0x00; return 0x00;
} }
size_t read_string(char* buf, size_t buf_size, off_t offset, STREAMFILE* sf) { size_t read_string_sz(char* buf, size_t buf_size, size_t string_size, off_t offset, STREAMFILE* sf) {
size_t pos;
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); uint8_t byte = read_u8(offset + pos, sf);
char c = (char)byte; if (buf) buf[pos] = (char)byte;
if (buf) buf[pos] = c;
if (c == '\0') // done
if (byte == '\0')
return pos; 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'; 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) if (byte < 0x20 || byte > 0xF0)
goto fail; break;
} }
fail: // error or wrong max_size
if (buf) buf[0] = '\0'; if (buf) buf[0] = '\0';
return 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 read_string_utf16(char* buf, size_t buf_size, off_t offset, STREAMFILE* sf, int big_endian) {
size_t pos, offpos; size_t pos, offpos;
read_u16_t read_u16 = big_endian ? read_u16be : read_u16le; 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 */ /* skip BOM if needed */
size_t read_bom(STREAMFILE* sf); 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); 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) */ /* 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); size_t read_string_utf16(char* buf, size_t buf_size, off_t offset, STREAMFILE* sf, int big_endian);