mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-11-12 01:30:49 +01:00
Merge pull request #1595 from bnnm/api-misc10
- Allow TXTH subsongs with stream names - cleanup
This commit is contained in:
commit
cdfaf59bfc
@ -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)
|
||||
```
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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))
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user