Fix .dic crash

This commit is contained in:
bnnm 2024-01-14 20:36:30 +01:00
parent 1f5bbeccdb
commit 18689e314c
3 changed files with 467 additions and 459 deletions

View File

@ -1,454 +1,457 @@
#include <ctype.h> #include <ctype.h>
#include "../vgmstream.h" #include "../vgmstream.h"
#include "../coding/coding.h" #include "../coding/coding.h"
#include "mixing.h" #include "mixing.h"
#include "../util/channel_mappings.h" #include "../util/channel_mappings.h"
#include "../util/sf_utils.h" #include "../util/sf_utils.h"
/*******************************************************************************/ /*******************************************************************************/
/* TEXT */ /* TEXT */
/*******************************************************************************/ /*******************************************************************************/
static void describe_get_time(int32_t samples, int sample_rate, double* p_time_mm, double* p_time_ss) { static void describe_get_time(int32_t samples, int sample_rate, double* p_time_mm, double* p_time_ss) {
double seconds = (double)samples / sample_rate; double seconds = (double)samples / sample_rate;
*p_time_mm = (int)(seconds / 60.0); *p_time_mm = (int)(seconds / 60.0);
*p_time_ss = seconds - *p_time_mm * 60.0f; *p_time_ss = seconds - *p_time_mm * 60.0f;
if (*p_time_ss >= 59.999) /* avoid round up to 60.0 when printing to %06.3f */ if (*p_time_ss >= 59.999) /* avoid round up to 60.0 when printing to %06.3f */
*p_time_ss = 59.999; *p_time_ss = 59.999;
} }
/* Write a description of the stream into array pointed by desc, which must be length bytes long. /* Write a description of the stream into array pointed by desc, which must be length bytes long.
* Will always be null-terminated if length > 0 */ * Will always be null-terminated if length > 0 */
void describe_vgmstream(VGMSTREAM* vgmstream, char* desc, int length) { void describe_vgmstream(VGMSTREAM* vgmstream, char* desc, int length) {
#define TEMPSIZE (256+32) #define TEMPSIZE (256+32)
char temp[TEMPSIZE]; char temp[TEMPSIZE];
double time_mm, time_ss; double time_mm, time_ss;
desc[0] = '\0'; desc[0] = '\0';
if (!vgmstream) { if (!vgmstream) {
snprintf(temp,TEMPSIZE, "NULL VGMSTREAM"); snprintf(temp,TEMPSIZE, "NULL VGMSTREAM");
concatn(length,desc,temp); concatn(length,desc,temp);
return; return;
} }
snprintf(temp,TEMPSIZE, "sample rate: %d Hz\n", vgmstream->sample_rate); snprintf(temp,TEMPSIZE, "sample rate: %d Hz\n", vgmstream->sample_rate);
concatn(length,desc,temp); concatn(length,desc,temp);
snprintf(temp,TEMPSIZE, "channels: %d\n", vgmstream->channels); snprintf(temp,TEMPSIZE, "channels: %d\n", vgmstream->channels);
concatn(length,desc,temp); concatn(length,desc,temp);
{ {
int output_channels = 0; int output_channels = 0;
mixing_info(vgmstream, NULL, &output_channels); mixing_info(vgmstream, NULL, &output_channels);
if (output_channels != vgmstream->channels) { if (output_channels != vgmstream->channels) {
snprintf(temp,TEMPSIZE, "input channels: %d\n", vgmstream->channels); /* repeated but mainly for plugins */ snprintf(temp,TEMPSIZE, "input channels: %d\n", vgmstream->channels); /* repeated but mainly for plugins */
concatn(length,desc,temp); concatn(length,desc,temp);
snprintf(temp,TEMPSIZE, "output channels: %d\n", output_channels); snprintf(temp,TEMPSIZE, "output channels: %d\n", output_channels);
concatn(length,desc,temp); concatn(length,desc,temp);
} }
} }
if (vgmstream->channel_layout) { if (vgmstream->channel_layout) {
int cl = vgmstream->channel_layout; int cl = vgmstream->channel_layout;
/* not "channel layout: " to avoid mixups with "layout: " */ /* not "channel layout: " to avoid mixups with "layout: " */
snprintf(temp,TEMPSIZE, "channel mask: 0x%x /", vgmstream->channel_layout); snprintf(temp,TEMPSIZE, "channel mask: 0x%x /", vgmstream->channel_layout);
concatn(length,desc,temp); concatn(length,desc,temp);
if (cl & speaker_FL) concatn(length,desc," FL"); if (cl & speaker_FL) concatn(length,desc," FL");
if (cl & speaker_FR) concatn(length,desc," FR"); if (cl & speaker_FR) concatn(length,desc," FR");
if (cl & speaker_FC) concatn(length,desc," FC"); if (cl & speaker_FC) concatn(length,desc," FC");
if (cl & speaker_LFE) concatn(length,desc," LFE"); if (cl & speaker_LFE) concatn(length,desc," LFE");
if (cl & speaker_BL) concatn(length,desc," BL"); if (cl & speaker_BL) concatn(length,desc," BL");
if (cl & speaker_BR) concatn(length,desc," BR"); if (cl & speaker_BR) concatn(length,desc," BR");
if (cl & speaker_FLC) concatn(length,desc," FLC"); if (cl & speaker_FLC) concatn(length,desc," FLC");
if (cl & speaker_FRC) concatn(length,desc," FRC"); if (cl & speaker_FRC) concatn(length,desc," FRC");
if (cl & speaker_BC) concatn(length,desc," BC"); if (cl & speaker_BC) concatn(length,desc," BC");
if (cl & speaker_SL) concatn(length,desc," SL"); if (cl & speaker_SL) concatn(length,desc," SL");
if (cl & speaker_SR) concatn(length,desc," SR"); if (cl & speaker_SR) concatn(length,desc," SR");
if (cl & speaker_TC) concatn(length,desc," TC"); if (cl & speaker_TC) concatn(length,desc," TC");
if (cl & speaker_TFL) concatn(length,desc," TFL"); if (cl & speaker_TFL) concatn(length,desc," TFL");
if (cl & speaker_TFC) concatn(length,desc," TFC"); if (cl & speaker_TFC) concatn(length,desc," TFC");
if (cl & speaker_TFR) concatn(length,desc," TFR"); if (cl & speaker_TFR) concatn(length,desc," TFR");
if (cl & speaker_TBL) concatn(length,desc," TBL"); if (cl & speaker_TBL) concatn(length,desc," TBL");
if (cl & speaker_TBC) concatn(length,desc," TBC"); if (cl & speaker_TBC) concatn(length,desc," TBC");
if (cl & speaker_TBR) concatn(length,desc," TBR"); if (cl & speaker_TBR) concatn(length,desc," TBR");
concatn(length,desc,"\n"); concatn(length,desc,"\n");
} }
/* times mod sounds avoid round up to 60.0 */ /* times mod sounds avoid round up to 60.0 */
if (vgmstream->loop_start_sample >= 0 && vgmstream->loop_end_sample > vgmstream->loop_start_sample) { if (vgmstream->loop_start_sample >= 0 && vgmstream->loop_end_sample > vgmstream->loop_start_sample) {
if (!vgmstream->loop_flag) { if (!vgmstream->loop_flag) {
concatn(length,desc,"looping: disabled\n"); concatn(length,desc,"looping: disabled\n");
} }
describe_get_time(vgmstream->loop_start_sample, vgmstream->sample_rate, &time_mm, &time_ss); describe_get_time(vgmstream->loop_start_sample, vgmstream->sample_rate, &time_mm, &time_ss);
snprintf(temp,TEMPSIZE, "loop start: %d samples (%1.0f:%06.3f seconds)\n", vgmstream->loop_start_sample, time_mm, time_ss); snprintf(temp,TEMPSIZE, "loop start: %d samples (%1.0f:%06.3f seconds)\n", vgmstream->loop_start_sample, time_mm, time_ss);
concatn(length,desc,temp); concatn(length,desc,temp);
describe_get_time(vgmstream->loop_end_sample, vgmstream->sample_rate, &time_mm, &time_ss); describe_get_time(vgmstream->loop_end_sample, vgmstream->sample_rate, &time_mm, &time_ss);
snprintf(temp,TEMPSIZE, "loop end: %d samples (%1.0f:%06.3f seconds)\n", vgmstream->loop_end_sample, time_mm, time_ss); snprintf(temp,TEMPSIZE, "loop end: %d samples (%1.0f:%06.3f seconds)\n", vgmstream->loop_end_sample, time_mm, time_ss);
concatn(length,desc,temp); concatn(length,desc,temp);
} }
describe_get_time(vgmstream->num_samples, vgmstream->sample_rate, &time_mm, &time_ss); describe_get_time(vgmstream->num_samples, vgmstream->sample_rate, &time_mm, &time_ss);
snprintf(temp,TEMPSIZE, "stream total samples: %d (%1.0f:%06.3f seconds)\n", vgmstream->num_samples, time_mm, time_ss); snprintf(temp,TEMPSIZE, "stream total samples: %d (%1.0f:%06.3f seconds)\n", vgmstream->num_samples, time_mm, time_ss);
concatn(length,desc,temp); concatn(length,desc,temp);
snprintf(temp,TEMPSIZE, "encoding: "); snprintf(temp,TEMPSIZE, "encoding: ");
concatn(length,desc,temp); concatn(length,desc,temp);
get_vgmstream_coding_description(vgmstream, temp, TEMPSIZE); get_vgmstream_coding_description(vgmstream, temp, TEMPSIZE);
concatn(length,desc,temp); concatn(length,desc,temp);
concatn(length,desc,"\n"); concatn(length,desc,"\n");
snprintf(temp,TEMPSIZE, "layout: "); snprintf(temp,TEMPSIZE, "layout: ");
concatn(length,desc,temp); concatn(length,desc,temp);
get_vgmstream_layout_description(vgmstream, temp, TEMPSIZE); get_vgmstream_layout_description(vgmstream, temp, TEMPSIZE);
concatn(length, desc, temp); concatn(length, desc, temp);
concatn(length,desc,"\n"); concatn(length,desc,"\n");
if (vgmstream->layout_type == layout_interleave && vgmstream->channels > 1) { if (vgmstream->layout_type == layout_interleave && vgmstream->channels > 1) {
snprintf(temp,TEMPSIZE, "interleave: %#x bytes\n", (int32_t)vgmstream->interleave_block_size); snprintf(temp,TEMPSIZE, "interleave: %#x bytes\n", (int32_t)vgmstream->interleave_block_size);
concatn(length,desc,temp); concatn(length,desc,temp);
if (vgmstream->interleave_first_block_size && vgmstream->interleave_first_block_size != vgmstream->interleave_block_size) { if (vgmstream->interleave_first_block_size && vgmstream->interleave_first_block_size != vgmstream->interleave_block_size) {
snprintf(temp,TEMPSIZE, "interleave first block: %#x bytes\n", (int32_t)vgmstream->interleave_first_block_size); snprintf(temp,TEMPSIZE, "interleave first block: %#x bytes\n", (int32_t)vgmstream->interleave_first_block_size);
concatn(length,desc,temp); concatn(length,desc,temp);
} }
if (vgmstream->interleave_last_block_size && vgmstream->interleave_last_block_size != vgmstream->interleave_block_size) { if (vgmstream->interleave_last_block_size && vgmstream->interleave_last_block_size != vgmstream->interleave_block_size) {
snprintf(temp,TEMPSIZE, "interleave last block: %#x bytes\n", (int32_t)vgmstream->interleave_last_block_size); snprintf(temp,TEMPSIZE, "interleave last block: %#x bytes\n", (int32_t)vgmstream->interleave_last_block_size);
concatn(length,desc,temp); concatn(length,desc,temp);
} }
} }
/* codecs with configurable frame size */ /* codecs with configurable frame size */
if (vgmstream->frame_size > 0 || vgmstream->interleave_block_size > 0) { if (vgmstream->frame_size > 0 || vgmstream->interleave_block_size > 0) {
int32_t frame_size = vgmstream->frame_size > 0 ? vgmstream->frame_size : vgmstream->interleave_block_size; int32_t frame_size = vgmstream->frame_size > 0 ? vgmstream->frame_size : vgmstream->interleave_block_size;
switch (vgmstream->coding_type) { switch (vgmstream->coding_type) {
case coding_MSADPCM: case coding_MSADPCM:
case coding_MSADPCM_int: case coding_MSADPCM_int:
case coding_MSADPCM_ck: case coding_MSADPCM_ck:
case coding_MS_IMA: case coding_MS_IMA:
case coding_MC3: case coding_MC3:
case coding_WWISE_IMA: case coding_WWISE_IMA:
case coding_REF_IMA: case coding_REF_IMA:
case coding_PSX_cfg: case coding_PSX_cfg:
snprintf(temp,TEMPSIZE, "frame size: %#x bytes\n", frame_size); snprintf(temp,TEMPSIZE, "frame size: %#x bytes\n", frame_size);
concatn(length,desc,temp); concatn(length,desc,temp);
break; break;
default: default:
break; break;
} }
} }
snprintf(temp,TEMPSIZE, "metadata from: "); snprintf(temp,TEMPSIZE, "metadata from: ");
concatn(length,desc,temp); concatn(length,desc,temp);
get_vgmstream_meta_description(vgmstream, temp, TEMPSIZE); get_vgmstream_meta_description(vgmstream, temp, TEMPSIZE);
concatn(length,desc,temp); concatn(length,desc,temp);
concatn(length,desc,"\n"); concatn(length,desc,"\n");
snprintf(temp,TEMPSIZE, "bitrate: %d kbps\n", get_vgmstream_average_bitrate(vgmstream) / 1000); snprintf(temp,TEMPSIZE, "bitrate: %d kbps\n", get_vgmstream_average_bitrate(vgmstream) / 1000);
concatn(length,desc,temp); concatn(length,desc,temp);
/* only interesting if more than one */ /* only interesting if more than one */
if (vgmstream->num_streams > 1) { if (vgmstream->num_streams > 1) {
snprintf(temp,TEMPSIZE, "stream count: %d\n", vgmstream->num_streams); snprintf(temp,TEMPSIZE, "stream count: %d\n", vgmstream->num_streams);
concatn(length,desc,temp); concatn(length,desc,temp);
} }
if (vgmstream->num_streams > 1) { if (vgmstream->num_streams > 1) {
snprintf(temp,TEMPSIZE, "stream index: %d\n", vgmstream->stream_index == 0 ? 1 : vgmstream->stream_index); snprintf(temp,TEMPSIZE, "stream index: %d\n", vgmstream->stream_index == 0 ? 1 : vgmstream->stream_index);
concatn(length,desc,temp); concatn(length,desc,temp);
} }
if (vgmstream->stream_name[0] != '\0') { if (vgmstream->stream_name[0] != '\0') {
snprintf(temp,TEMPSIZE, "stream name: %s\n", vgmstream->stream_name); snprintf(temp,TEMPSIZE, "stream name: %s\n", vgmstream->stream_name);
concatn(length,desc,temp); concatn(length,desc,temp);
} }
if (vgmstream->config_enabled) { if (vgmstream->config_enabled) {
int32_t samples = vgmstream->pstate.play_duration; int32_t samples = vgmstream->pstate.play_duration;
describe_get_time(samples, vgmstream->sample_rate, &time_mm, &time_ss); describe_get_time(samples, vgmstream->sample_rate, &time_mm, &time_ss);
snprintf(temp,TEMPSIZE, "play duration: %d samples (%1.0f:%06.3f seconds)\n", samples, time_mm, time_ss); snprintf(temp,TEMPSIZE, "play duration: %d samples (%1.0f:%06.3f seconds)\n", samples, time_mm, time_ss);
concatn(length,desc,temp); concatn(length,desc,temp);
} }
} }
void describe_vgmstream_info(VGMSTREAM* vgmstream, vgmstream_info* info) { void describe_vgmstream_info(VGMSTREAM* vgmstream, vgmstream_info* info) {
if (!info) { if (!info) {
return; return;
} }
memset(info, 0, sizeof(*info)); memset(info, 0, sizeof(*info));
if (!vgmstream) { if (!vgmstream) {
return; return;
} }
info->sample_rate = vgmstream->sample_rate; info->sample_rate = vgmstream->sample_rate;
info->channels = vgmstream->channels; info->channels = vgmstream->channels;
{ {
int output_channels = 0; int output_channels = 0;
mixing_info(vgmstream, NULL, &output_channels); mixing_info(vgmstream, NULL, &output_channels);
if (output_channels != vgmstream->channels) { if (output_channels != vgmstream->channels) {
info->mixing_info.input_channels = vgmstream->channels; info->mixing_info.input_channels = vgmstream->channels;
info->mixing_info.output_channels = output_channels; info->mixing_info.output_channels = output_channels;
} }
} }
info->channel_layout = vgmstream->channel_layout; info->channel_layout = vgmstream->channel_layout;
if (vgmstream->loop_start_sample >= 0 && vgmstream->loop_end_sample > vgmstream->loop_start_sample) { if (vgmstream->loop_start_sample >= 0 && vgmstream->loop_end_sample > vgmstream->loop_start_sample) {
info->loop_info.start = vgmstream->loop_start_sample; info->loop_info.start = vgmstream->loop_start_sample;
info->loop_info.end = vgmstream->loop_end_sample; info->loop_info.end = vgmstream->loop_end_sample;
} }
info->num_samples = vgmstream->num_samples; info->num_samples = vgmstream->num_samples;
get_vgmstream_coding_description(vgmstream, info->encoding, sizeof(info->encoding)); get_vgmstream_coding_description(vgmstream, info->encoding, sizeof(info->encoding));
get_vgmstream_layout_description(vgmstream, info->layout, sizeof(info->layout)); get_vgmstream_layout_description(vgmstream, info->layout, sizeof(info->layout));
if (vgmstream->layout_type == layout_interleave && vgmstream->channels > 1) { if (vgmstream->layout_type == layout_interleave && vgmstream->channels > 1) {
info->interleave_info.value = vgmstream->interleave_block_size; info->interleave_info.value = vgmstream->interleave_block_size;
if (vgmstream->interleave_first_block_size && vgmstream->interleave_first_block_size != vgmstream->interleave_block_size) { if (vgmstream->interleave_first_block_size && vgmstream->interleave_first_block_size != vgmstream->interleave_block_size) {
info->interleave_info.first_block = vgmstream->interleave_first_block_size; info->interleave_info.first_block = vgmstream->interleave_first_block_size;
} }
if (vgmstream->interleave_last_block_size && vgmstream->interleave_last_block_size != vgmstream->interleave_block_size) { if (vgmstream->interleave_last_block_size && vgmstream->interleave_last_block_size != vgmstream->interleave_block_size) {
info->interleave_info.last_block = vgmstream->interleave_last_block_size; info->interleave_info.last_block = vgmstream->interleave_last_block_size;
} }
} }
/* codecs with configurable frame size */ /* codecs with configurable frame size */
if (vgmstream->frame_size > 0 || vgmstream->interleave_block_size > 0) { if (vgmstream->frame_size > 0 || vgmstream->interleave_block_size > 0) {
int32_t frame_size = vgmstream->frame_size > 0 ? vgmstream->frame_size : vgmstream->interleave_block_size; int32_t frame_size = vgmstream->frame_size > 0 ? vgmstream->frame_size : vgmstream->interleave_block_size;
switch (vgmstream->coding_type) { switch (vgmstream->coding_type) {
case coding_MSADPCM: case coding_MSADPCM:
case coding_MSADPCM_int: case coding_MSADPCM_int:
case coding_MSADPCM_ck: case coding_MSADPCM_ck:
case coding_MS_IMA: case coding_MS_IMA:
case coding_MC3: case coding_MC3:
case coding_WWISE_IMA: case coding_WWISE_IMA:
case coding_REF_IMA: case coding_REF_IMA:
case coding_PSX_cfg: case coding_PSX_cfg:
info->frame_size = frame_size; info->frame_size = frame_size;
break; break;
default: default:
break; break;
} }
} }
get_vgmstream_meta_description(vgmstream, info->metadata, sizeof(info->metadata)); get_vgmstream_meta_description(vgmstream, info->metadata, sizeof(info->metadata));
info->bitrate = get_vgmstream_average_bitrate(vgmstream); info->bitrate = get_vgmstream_average_bitrate(vgmstream);
/* only interesting if more than one */ /* only interesting if more than one */
if (vgmstream->num_streams > 1) { if (vgmstream->num_streams > 1) {
info->stream_info.total = vgmstream->num_streams; info->stream_info.total = vgmstream->num_streams;
} }
else { else {
info->stream_info.total = 1; info->stream_info.total = 1;
} }
if (vgmstream->num_streams > 1) { if (vgmstream->num_streams > 1) {
info->stream_info.current = vgmstream->stream_index == 0 ? 1 : vgmstream->stream_index; info->stream_info.current = vgmstream->stream_index == 0 ? 1 : vgmstream->stream_index;
} }
if (vgmstream->stream_name[0] != '\0') { if (vgmstream->stream_name[0] != '\0') {
snprintf(info->stream_info.name, sizeof(info->stream_info.name), "%s", vgmstream->stream_name); snprintf(info->stream_info.name, sizeof(info->stream_info.name), "%s", vgmstream->stream_name);
} }
} }
/*******************************************************************************/ /*******************************************************************************/
/* BITRATE */ /* BITRATE */
/*******************************************************************************/ /*******************************************************************************/
#define BITRATE_FILES_MAX 128 /* arbitrary max, but +100 segments have been observed */ #define BITRATE_FILES_MAX 128 /* arbitrary max, but +100 segments have been observed */
typedef struct { typedef struct {
uint32_t hash[BITRATE_FILES_MAX]; /* already used streamfiles */ uint32_t hash[BITRATE_FILES_MAX]; /* already used streamfiles */
int subsong[BITRATE_FILES_MAX]; /* subsongs of those streamfiles (could be incorporated to the hash?) */ int subsong[BITRATE_FILES_MAX]; /* subsongs of those streamfiles (could be incorporated to the hash?) */
int count; int count;
int count_max; int count_max;
} bitrate_info_t; } bitrate_info_t;
static uint32_t hash_sf(STREAMFILE* sf) { static uint32_t hash_sf(STREAMFILE* sf) {
int i; int i;
char path[PATH_LIMIT]; char path[PATH_LIMIT];
uint32_t hash = 2166136261; uint32_t hash = 2166136261;
get_streamfile_name(sf, path, sizeof(path)); get_streamfile_name(sf, path, sizeof(path));
/* our favorite garbo hash a.k.a FNV-1 32b */ /* our favorite garbo hash a.k.a FNV-1 32b */
i = 0; i = 0;
while (path[i] != '\0') { while (path[i] != '\0') {
char c = tolower(path[i]); char c = tolower(path[i]);
hash = (hash * 16777619) ^ (uint8_t)c; hash = (hash * 16777619) ^ (uint8_t)c;
i++; i++;
} }
return hash; return hash;
} }
/* average bitrate helper to get STREAMFILE for a channel, since some codecs may use their own */ /* average bitrate helper to get STREAMFILE for a channel, since some codecs may use their own */
static STREAMFILE* get_vgmstream_average_bitrate_channel_streamfile(VGMSTREAM* vgmstream, int channel) { static STREAMFILE* get_vgmstream_average_bitrate_channel_streamfile(VGMSTREAM* vgmstream, int channel) {
if (vgmstream->coding_type == coding_NWA) { if (vgmstream->coding_type == coding_NWA) {
return nwa_get_streamfile(vgmstream->codec_data); return nwa_get_streamfile(vgmstream->codec_data);
} }
if (vgmstream->coding_type == coding_ACM) { if (vgmstream->coding_type == coding_ACM) {
return acm_get_streamfile(vgmstream->codec_data); return acm_get_streamfile(vgmstream->codec_data);
} }
if (vgmstream->coding_type == coding_COMPRESSWAVE) { if (vgmstream->coding_type == coding_COMPRESSWAVE) {
return compresswave_get_streamfile(vgmstream->codec_data); return compresswave_get_streamfile(vgmstream->codec_data);
} }
#ifdef VGM_USE_VORBIS #ifdef VGM_USE_VORBIS
if (vgmstream->coding_type == coding_OGG_VORBIS) { if (vgmstream->coding_type == coding_OGG_VORBIS) {
return ogg_vorbis_get_streamfile(vgmstream->codec_data); return ogg_vorbis_get_streamfile(vgmstream->codec_data);
} }
#endif #endif
if (vgmstream->coding_type == coding_CRI_HCA) { if (vgmstream->coding_type == coding_CRI_HCA) {
return hca_get_streamfile(vgmstream->codec_data); return hca_get_streamfile(vgmstream->codec_data);
} }
#ifdef VGM_USE_FFMPEG #ifdef VGM_USE_FFMPEG
if (vgmstream->coding_type == coding_FFmpeg) { if (vgmstream->coding_type == coding_FFmpeg) {
return ffmpeg_get_streamfile(vgmstream->codec_data); return ffmpeg_get_streamfile(vgmstream->codec_data);
} }
#endif #endif
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) #if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
if (vgmstream->coding_type == coding_MP4_AAC) { if (vgmstream->coding_type == coding_MP4_AAC) {
mp4_aac_codec_data *data = vgmstream->codec_data; mp4_aac_codec_data *data = vgmstream->codec_data;
return data ? data->if_file.streamfile : NULL; return data ? data->if_file.streamfile : NULL;
} }
#endif #endif
return vgmstream->ch[channel].streamfile; return vgmstream->ch[channel].streamfile;
} }
static int get_vgmstream_file_bitrate_from_size(size_t size, int sample_rate, int32_t length_samples) { static int get_vgmstream_file_bitrate_from_size(size_t size, int sample_rate, int32_t length_samples) {
if (sample_rate == 0 || length_samples == 0) return 0; if (sample_rate == 0 || length_samples == 0) return 0;
if (length_samples < 100) return 0; /* ignore stupid bitrates caused by some segments */ if (length_samples < 100) return 0; /* ignore stupid bitrates caused by some segments */
return (int)((int64_t)size * 8 * sample_rate / length_samples); return (int)((int64_t)size * 8 * sample_rate / length_samples);
} }
static int get_vgmstream_file_bitrate_from_streamfile(STREAMFILE* sf, int sample_rate, int32_t length_samples) { static int get_vgmstream_file_bitrate_from_streamfile(STREAMFILE* sf, int sample_rate, int32_t length_samples) {
if (sf == NULL) return 0; if (sf == NULL) return 0;
return get_vgmstream_file_bitrate_from_size(get_streamfile_size(sf), sample_rate, length_samples); return get_vgmstream_file_bitrate_from_size(get_streamfile_size(sf), sample_rate, length_samples);
} }
static int get_vgmstream_file_bitrate_main(VGMSTREAM* vgmstream, bitrate_info_t* br, int* p_uniques) { static int get_vgmstream_file_bitrate_main(VGMSTREAM* vgmstream, bitrate_info_t* br, int* p_uniques) {
int i, ch; int i, ch;
int bitrate = 0; int bitrate = 0;
/* Recursively get bitrate and fill the list of streamfiles if needed (to filter), /* Recursively get bitrate and fill the list of streamfiles if needed (to filter),
* since layouts can include further vgmstreams that may also share streamfiles. * since layouts can include further vgmstreams that may also share streamfiles.
* *
* Because of how data, layers and segments can be combined it's possible to * Because of how data, layers and segments can be combined it's possible to
* fool this in various ways; metas should report stream_size in complex cases * fool this in various ways; metas should report stream_size in complex cases
* to get accurate bitrates (particularly for subsongs). An edge case is when * to get accurate bitrates (particularly for subsongs). An edge case is when
* segments use only a few samples from a full file (like Wwise transitions), bitrates * segments use only a few samples from a full file (like Wwise transitions), bitrates
* become a bit high since its hard to detect only part of the file is needed. */ * become a bit high since its hard to detect only part of the file is needed. */
if (vgmstream->stream_size != 0) { if (vgmstream->stream_size != 0) {
/* format may report full size for custom layouts that otherwise get odd values */ /* format may report full size for custom layouts that otherwise get odd values */
bitrate += get_vgmstream_file_bitrate_from_size(vgmstream->stream_size, vgmstream->sample_rate, vgmstream->num_samples); bitrate += get_vgmstream_file_bitrate_from_size(vgmstream->stream_size, vgmstream->sample_rate, vgmstream->num_samples);
if (p_uniques) if (p_uniques)
(*p_uniques)++; (*p_uniques)++;
} }
else if (vgmstream->layout_type == layout_segmented) { else if (vgmstream->layout_type == layout_segmented) {
int uniques = 0; int uniques = 0;
segmented_layout_data *data = (segmented_layout_data *) vgmstream->layout_data; segmented_layout_data *data = (segmented_layout_data *) vgmstream->layout_data;
for (i = 0; i < data->segment_count; i++) { for (i = 0; i < data->segment_count; i++) {
bitrate += get_vgmstream_file_bitrate_main(data->segments[i], br, &uniques); bitrate += get_vgmstream_file_bitrate_main(data->segments[i], br, &uniques);
} }
if (uniques) if (uniques)
bitrate /= uniques; /* average */ bitrate /= uniques; /* average */
} }
else if (vgmstream->layout_type == layout_layered) { else if (vgmstream->layout_type == layout_layered) {
layered_layout_data *data = vgmstream->layout_data; layered_layout_data *data = vgmstream->layout_data;
for (i = 0; i < data->layer_count; i++) { for (i = 0; i < data->layer_count; i++) {
bitrate += get_vgmstream_file_bitrate_main(data->layers[i], br, NULL); bitrate += get_vgmstream_file_bitrate_main(data->layers[i], br, NULL);
} }
} }
else { else {
/* Add channel bitrate if streamfile hasn't been used before, so bitrate doesn't count repeats /* Add channel bitrate if streamfile hasn't been used before, so bitrate doesn't count repeats
* (like same STREAMFILE reopened per channel, also considering SFs may be wrapped). */ * (like same STREAMFILE reopened per channel, also considering SFs may be wrapped). */
for (ch = 0; ch < vgmstream->channels; ch++) { for (ch = 0; ch < vgmstream->channels; ch++) {
uint32_t hash_cur; uint32_t hash_cur;
int subsong_cur; int subsong_cur;
STREAMFILE* sf_cur; STREAMFILE* sf_cur;
int is_unique = 1; /* default to "no other SFs exist" */ int is_unique = 1; /* default to "no other SFs exist" */
/* compares paths (hashes for faster compares) + subsongs (same file + different subsong = "different" file) */ /* compares paths (hashes for faster compares) + subsongs (same file + different subsong = "different" file) */
sf_cur = get_vgmstream_average_bitrate_channel_streamfile(vgmstream, ch); sf_cur = get_vgmstream_average_bitrate_channel_streamfile(vgmstream, ch);
if (!sf_cur) continue; if (!sf_cur) continue;
hash_cur = hash_sf(sf_cur); hash_cur = hash_sf(sf_cur);
subsong_cur = vgmstream->stream_index; subsong_cur = vgmstream->stream_index;
for (i = 0; i < br->count; i++) { for (i = 0; i < br->count; i++) {
uint32_t hash_cmp = br->hash[i]; uint32_t hash_cmp = br->hash[i];
int subsong_cmp = br->subsong[i]; int subsong_cmp = br->subsong[i];
if (hash_cur == hash_cmp && subsong_cur == subsong_cmp) { if (hash_cur == hash_cmp && subsong_cur == subsong_cmp) {
is_unique = 0; is_unique = 0;
break; break;
} }
} }
if (is_unique) { if (is_unique) {
size_t file_bitrate; size_t file_bitrate;
if (br->count >= br->count_max) goto fail; if (br->count >= br->count_max) goto fail;
if (vgmstream->stream_size) { if (vgmstream->stream_size) {
/* stream_size applies to both channels but should add once and detect repeats (for current subsong) */ /* stream_size applies to both channels but should add once and detect repeats (for current subsong) */
file_bitrate = get_vgmstream_file_bitrate_from_size(vgmstream->stream_size, vgmstream->sample_rate, vgmstream->num_samples); file_bitrate = get_vgmstream_file_bitrate_from_size(vgmstream->stream_size, vgmstream->sample_rate, vgmstream->num_samples);
} }
else { else {
file_bitrate = get_vgmstream_file_bitrate_from_streamfile(sf_cur, vgmstream->sample_rate, vgmstream->num_samples); file_bitrate = get_vgmstream_file_bitrate_from_streamfile(sf_cur, vgmstream->sample_rate, vgmstream->num_samples);
} }
/* possible in cases like using silence codec */ /* possible in cases like using silence codec */
if (!file_bitrate) if (!file_bitrate)
break; break;
br->hash[br->count] = hash_cur; br->hash[br->count] = hash_cur;
br->subsong[br->count] = subsong_cur; br->subsong[br->count] = subsong_cur;
br->count++; br->count++;
if (p_uniques) if (p_uniques)
(*p_uniques)++; (*p_uniques)++;
bitrate += file_bitrate; bitrate += file_bitrate;
break; break;
} }
} }
} }
return bitrate; return bitrate;
fail: fail:
return 0; return 0;
} }
/* Return the average bitrate in bps of all unique data contained within this stream. /* Return the average bitrate in bps of all unique data contained within this stream.
* This is the bitrate of the *file*, as opposed to the bitrate of the *codec*, meaning * This is the bitrate of the *file*, as opposed to the bitrate of the *codec*, meaning
* it counts extra data like block headers and padding. While this can be surprising * it counts extra data like block headers and padding. While this can be surprising
* sometimes (as it's often higher than common codec bitrates) it isn't wrong per se. */ * sometimes (as it's often higher than common codec bitrates) it isn't wrong per se. */
int get_vgmstream_average_bitrate(VGMSTREAM* vgmstream) { int get_vgmstream_average_bitrate(VGMSTREAM* vgmstream) {
bitrate_info_t br = {0}; bitrate_info_t br = {0};
br.count_max = BITRATE_FILES_MAX; br.count_max = BITRATE_FILES_MAX;
return get_vgmstream_file_bitrate_main(vgmstream, &br, NULL); if (vgmstream->coding_type == coding_SILENCE)
} return 0;
return get_vgmstream_file_bitrate_main(vgmstream, &br, NULL);
}

View File

@ -38,6 +38,8 @@ ogg_vorbis_codec_data* init_ogg_vorbis(STREAMFILE* sf, off_t start, off_t size,
ov_callbacks callbacks = {0}; ov_callbacks callbacks = {0};
//todo clean up //todo clean up
if (!sf)
return NULL;
callbacks.read_func = ov_read_func; callbacks.read_func = ov_read_func;
callbacks.seek_func = ov_seek_func; callbacks.seek_func = ov_seek_func;

View File

@ -84,7 +84,7 @@ VGMSTREAM* init_vgmstream_ego_dic(STREAMFILE* sf) {
if (sb == NULL) { if (sb == NULL) {
vgm_logi("DIC1: external file '%s' not found (put together)\n", resource_name); vgm_logi("DIC1: external file '%s' not found (put together)\n", resource_name);
/* allow missing as silence since some game use huge .dic that is a bit hard to get */ /* allow missing as silence since some game use huge .dic that is a bit hard to get */
//goto fail; codec = 0xFFFFFFFF; //goto fail;
} }
} }
@ -100,6 +100,13 @@ VGMSTREAM* init_vgmstream_ego_dic(STREAMFILE* sf) {
switch(codec) { switch(codec) {
case 0xFFFFFFFF: //fake
vgmstream->coding_type = coding_SILENCE;
vgmstream->layout_type = layout_none;
vgmstream->num_samples = sample_rate;
break;
case 0x57495000: //WIP\0 case 0x57495000: //WIP\0
vgmstream->coding_type = coding_PCM16LE; vgmstream->coding_type = coding_PCM16LE;
vgmstream->layout_type = layout_interleave; vgmstream->layout_type = layout_interleave;
@ -143,10 +150,6 @@ VGMSTREAM* init_vgmstream_ego_dic(STREAMFILE* sf) {
vgmstream->loop_start_sample = 0; vgmstream->loop_start_sample = 0;
vgmstream->loop_end_sample = vgmstream->num_samples; vgmstream->loop_end_sample = vgmstream->num_samples;
if (!sb) {
vgmstream->coding_type = coding_SILENCE;
vgmstream->layout_type = layout_none;
}
if (!vgmstream_open_stream(vgmstream, sb, stream_offset)) if (!vgmstream_open_stream(vgmstream, sb, stream_offset))
goto fail; goto fail;