If you have an issue with any of our projects. feel free to register.

Commit ebfb7e12 authored by Christopher Snowhill's avatar Christopher Snowhill

Updated VGMStream to r1050-3043-g295ffac0

parent 3cdebc84
......@@ -348,6 +348,7 @@ uint32_t ffmpeg_get_channel_layout(ffmpeg_codec_data * data);
void ffmpeg_set_channel_remapping(ffmpeg_codec_data * data, int *channels_remap);
const char* ffmpeg_get_codec_name(ffmpeg_codec_data * data);
void ffmpeg_set_force_seek(ffmpeg_codec_data * data);
const char* ffmpeg_get_metadata_value(ffmpeg_codec_data* data, const char* key);
/* ffmpeg_decoder_utils.c (helper-things) */
......
......@@ -155,6 +155,8 @@ int ffmpeg_make_riff_xma1(uint8_t * buf, size_t buf_size, size_t sample_count, s
put_16bitLE(buf+off+0x12, speakers);
}
/* xmaencode decoding rejects XMA1 without "seek" chunk, though it doesn't seem to use it */
memcpy(buf+riff_size-4-4, "data", 4);
put_32bitLE(buf+riff_size-4, data_size); /* data size */
......
......@@ -941,4 +941,22 @@ void ffmpeg_set_force_seek(ffmpeg_codec_data * data) {
//stream = data->formatCtx->streams[data->streamIndex];
}
const char* ffmpeg_get_metadata_value(ffmpeg_codec_data* data, const char* key) {
AVDictionary* avd;
AVDictionaryEntry* avde;
if (!data || !data->codec)
return NULL;
avd = data->formatCtx->streams[data->streamIndex]->metadata;
if (!avd)
return NULL;
avde = av_dict_get(avd, key, NULL, AV_DICT_IGNORE_SUFFIX);
if (!avde)
return NULL;
return avde->value;
}
#endif
......@@ -11,7 +11,7 @@
* - expand type: IMA style or variations; low or high nibble first
*/
static const int ADPCMTable[89] = {
static const int ADPCMTable[90] = {
7, 8, 9, 10, 11, 12, 13, 14,
16, 17, 19, 21, 23, 25, 28, 31,
34, 37, 41, 45, 50, 55, 60, 66,
......@@ -23,7 +23,9 @@ static const int ADPCMTable[89] = {
3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484,
7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794,
32767
32767,
0 /* garbage value for Ubisoft IMA (see blocked_ubi_sce.c) */
};
static const int IMA_IndexTable[16] = {
......@@ -1054,10 +1056,14 @@ void decode_ubi_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspa
if (has_header) {
first_sample -= 10; //todo fix hack (needed to adjust nibble offset below)
}
if (step_index < 0) step_index=0;
if (step_index > 88) step_index=88;
if (step_index < 0) step_index = 0;
if (step_index > 88) step_index = 88;
} else {
if (step_index < 0) step_index = 0;
if (step_index > 89) step_index = 89;
}
for (i = first_sample; i < first_sample + samples_to_do; i++, sample_count += channelspacing) {
off_t byte_offset = channelspacing == 1 ?
......
......@@ -106,6 +106,7 @@ static const char* extension_list[] = {
"bik2",
//"bin", //common
"bk2",
"blk",
"bmdx",
"bms",
"bnk",
......@@ -137,6 +138,7 @@ static const char* extension_list[] = {
"cxs",
"da",
"dat",
"data",
"dax",
"dbm",
......@@ -237,6 +239,7 @@ static const char* extension_list[] = {
"joe",
"jstm",
"kat",
"kces",
"kcey", //fake extension/header id for .pcm (renamed, to be removed)
"khv", //fake extension/header id for .vas (renamed, to be removed)
......@@ -244,6 +247,7 @@ static const char* extension_list[] = {
"kovs", //fake extension/header id for .kvs
"kns",
"kraw",
"ktsl2asbin",
"ktss", //fake extension/header id for .kns
"kvs",
......@@ -322,6 +326,7 @@ static const char* extension_list[] = {
"mta2",
"mtaf",
"mul",
"mups",
"mus",
"musc",
"musx",
......@@ -1141,7 +1146,7 @@ static const meta_info meta_info_list[] = {
{meta_CSTM, "Nintendo CSTM Header"},
{meta_FSTM, "Nintendo FSTM Header"},
{meta_KT_WIIBGM, "Koei Tecmo WiiBGM Header"},
{meta_KTSS, "Koei Tecmo Nintendo Stream KTSS Header"},
{meta_KTSS, "Koei Tecmo KTSS header"},
{meta_IDSP_NAMCO, "Namco IDSP header"},
{meta_WIIU_BTSND, "Nintendo Wii U Menu Boot Sound"},
{meta_MCA, "Capcom MCA header"},
......@@ -1218,7 +1223,7 @@ static const meta_info meta_info_list[] = {
{meta_UBI_BAO, "Ubisoft BAO header"},
{meta_DSP_SWITCH_AUDIO, "UE4 Switch Audio header"},
{meta_TA_AAC_VITA, "tri-Ace AAC (Vita) header"},
{meta_DSP_SADF, "Procyon Studio SADF header"},
{meta_SADF, "Procyon Studio SADF header"},
{meta_H4M, "Hudson HVQM4 header"},
{meta_ASF, "Argonaut ASF header"},
{meta_XMD, "Konami XMD header"},
......@@ -1292,7 +1297,9 @@ static const meta_info meta_info_list[] = {
{meta_WWISE_FX, "Audiokinetic Wwise FX header"},
{meta_DIVA, "DIVA header"},
{meta_IMUSE, "LucasArts iMUSE header"},
{meta_KTSR, "Koei Tecmo KTSR header"},
{meta_KAT, "Sega KAT header"},
{meta_PCM_SUCCESS, "Success PCM header"},
};
void get_vgmstream_coding_description(VGMSTREAM *vgmstream, char *out, size_t out_size) {
......
......@@ -44,13 +44,13 @@ void block_update_ubi_sce(off_t block_offset, VGMSTREAM* vgmstream) {
vgmstream->ch[i].adpcm_step_index = read_32bitLE(block_offset + header_size * i + 0x04, sf);
vgmstream->ch[i].adpcm_history1_32 = read_32bitLE(block_offset + header_size * i + 0x08, sf);
/* TODO figure out
* First step seems to always be a special value for the decoder, unsure of meaning.
* 0 = too quiet and max = 88 = waveform starts a bit off and clicky. First hist is usually +-1,
* other frames look, fine not sure what are they aiming for.
*/
/* First step is always 0x500, not sure if it's a bug or a feature but the game just takes it as is and
* ends up reading 0 from out-of-bounds memory area which causes a pop at the start. Yikes.
* It gets clampled later so the rest of the sound plays ok.
* We put 89 here as our special index which contains 0 to simulate this.
*/
if (vgmstream->ch[i].adpcm_step_index == 0x500) {
vgmstream->ch[i].adpcm_step_index = 88;
vgmstream->ch[i].adpcm_step_index = 89;
}
}
}
......
......@@ -3,13 +3,15 @@
#include "aix_streamfile.h"
#define MAX_SEGMENTS 50 /* usually segment0=intro, segment1=loop/main, sometimes ~5, rarely ~40 */
/* usually segment0=intro, segment1=loop/main, sometimes ~5, rarely ~40~115
* as pseudo dynamic/multi-song container [Sega Ages 2500 Vol 28 Tetris Collection (PS2)] */
#define MAX_SEGMENTS 120
static VGMSTREAM *build_segmented_vgmstream(STREAMFILE *streamFile, off_t *segment_offsets, size_t *segment_sizes, int32_t *segment_samples, int segment_count, int layer_count);
static VGMSTREAM* build_segmented_vgmstream(STREAMFILE* sf, off_t* segment_offsets, size_t* segment_sizes, int32_t* segment_samples, int segment_count, int layer_count);
/* AIX - N segments with M layers (2ch ADX) inside [SoulCalibur IV (PS3), Dragon Ball Z: Burst Limit (PS3)] */
VGMSTREAM * init_vgmstream_aix(STREAMFILE *sf) {
VGMSTREAM * vgmstream = NULL;
VGMSTREAM* init_vgmstream_aix(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
off_t segment_offsets[MAX_SEGMENTS] = {0};
size_t segment_sizes[MAX_SEGMENTS] = {0};
......@@ -106,8 +108,8 @@ fail:
return NULL;
}
static VGMSTREAM *build_layered_vgmstream(STREAMFILE *streamFile, off_t segment_offset, size_t segment_size, int layer_count) {
VGMSTREAM *vgmstream = NULL;
static VGMSTREAM *build_layered_vgmstream(STREAMFILE* sf, off_t segment_offset, size_t segment_size, int layer_count) {
VGMSTREAM* vgmstream = NULL;
layered_layout_data* data = NULL;
int i;
STREAMFILE* temp_sf = NULL;
......@@ -119,7 +121,7 @@ static VGMSTREAM *build_layered_vgmstream(STREAMFILE *streamFile, off_t segment_
for (i = 0; i < layer_count; i++) {
/* build the layer STREAMFILE */
temp_sf = setup_aix_streamfile(streamFile, segment_offset, segment_size, i, "adx");
temp_sf = setup_aix_streamfile(sf, segment_offset, segment_size, i, "adx");
if (!temp_sf) goto fail;
/* build the sub-VGMSTREAM */
......@@ -149,9 +151,9 @@ fail:
return NULL;
}
static VGMSTREAM *build_segmented_vgmstream(STREAMFILE *streamFile, off_t *segment_offsets, size_t *segment_sizes, int32_t *segment_samples, int segment_count, int layer_count) {
VGMSTREAM *vgmstream = NULL;
segmented_layout_data *data = NULL;
static VGMSTREAM *build_segmented_vgmstream(STREAMFILE* sf, off_t* segment_offsets, size_t* segment_sizes, int32_t* segment_samples, int segment_count, int layer_count) {
VGMSTREAM* vgmstream = NULL;
segmented_layout_data* data = NULL;
int i, loop_flag, loop_start_segment, loop_end_segment;
......@@ -161,7 +163,7 @@ static VGMSTREAM *build_segmented_vgmstream(STREAMFILE *streamFile, off_t *segme
for (i = 0; i < segment_count; i++) {
/* build the layered sub-VGMSTREAM */
data->segments[i] = build_layered_vgmstream(streamFile, segment_offsets[i], segment_sizes[i], layer_count);
data->segments[i] = build_layered_vgmstream(sf, segment_offsets[i], segment_sizes[i], layer_count);
if (!data->segments[i]) goto fail;
data->segments[i]->num_samples = segment_samples[i]; /* just in case */
......@@ -177,7 +179,7 @@ static VGMSTREAM *build_segmented_vgmstream(STREAMFILE *streamFile, off_t *segme
* - 2 segments: intro + loop [SoulCalibur IV (PS3)]
* - 3 segments: intro + loop + end [Dragon Ball Z: Burst Limit (PS3), Metroid: Other M (Wii)]
* - 4/5 segments: intros + loop + ends [Danball Senki (PSP)]
* - 39 segments: no loops but multiple segments for dynamic parts? [Tetris Collection (PS2)] */
* - +39 segments: no loops but multiple segments for dynamic parts? [Tetris Collection (PS2)] */
loop_flag = (segment_count > 0 && segment_count <= 5);
loop_start_segment = (segment_count > 3) ? 2 : 1;
loop_end_segment = (segment_count > 3) ? (segment_count - 2) : 1;
......
......@@ -5,7 +5,11 @@ typedef struct {
const char* key;
} bnsfkey_info;
/* Known keys, extracted from games' exe/files */
/* Known keys, from games' exe (iM@S, near "nus" strings) or files (Tales, config in audio bigfiles).
*
* In memdumps, first 16 chars of key can be found XORed with "Ua#oK3P94vdxX,ft" after AES 'Td'
* mix tables (that end with 8D4697A3 A38D4697 97A38D46 4697A38D), then can be cross referenced
* with other strings (max 24 chars) in the memdump. */
static const bnsfkey_info s14key_list[] = {
/* THE iDOLM@STER 2 (PS3/X360) */
......@@ -17,6 +21,9 @@ static const bnsfkey_info s14key_list[] = {
/* Tales of Berseria (PS3) */
{"SPSLOC13"},
/* THE iDOLM@STER: One For All (PS3) */
{"86c215d7655eefb5c77ae92c"},
};
#endif/*_BNSF_KEYS_H_*/
#endif /*_BNSF_KEYS_H_*/
......@@ -2,7 +2,7 @@
//todo move to utils or something
static void block_callback_default(STREAMFILE *sf, deblock_io_data *data) {
static void block_callback_default(STREAMFILE* sf, deblock_io_data* data) {
data->block_size = data->cfg.chunk_size;
data->skip_size = data->cfg.skip_size;
data->data_size = data->block_size - data->skip_size;
......@@ -10,7 +10,7 @@ static void block_callback_default(STREAMFILE *sf, deblock_io_data *data) {
//;VGM_LOG("DEBLOCK: of=%lx, bs=%lx, ss=%lx, ds=%lx\n", data->physical_offset, data->block_size, data->skip_size, data->data_size);
}
static size_t deblock_io_read(STREAMFILE *sf, uint8_t *dest, off_t offset, size_t length, deblock_io_data* data) {
static size_t deblock_io_read(STREAMFILE* sf, uint8_t* dest, off_t offset, size_t length, deblock_io_data* data) {
size_t total_read = 0;
//;VGM_LOG("DEBLOCK: of=%lx, sz=%x, po=%lx\n", offset, length, data->physical_offset);
......@@ -25,9 +25,7 @@ static size_t deblock_io_read(STREAMFILE *sf, uint8_t *dest, off_t offset, size_
data->skip_size = 0;
data->step_count = data->cfg.step_start;
/*
data->read_count = data->cfg.read_count;
*/
//data->read_count = data->cfg.read_count;
}
/* read blocks */
......@@ -98,6 +96,10 @@ static size_t deblock_io_read(STREAMFILE *sf, uint8_t *dest, off_t offset, size_
to_read = length;
bytes_done = read_streamfile(dest, data->physical_offset + data->skip_size + bytes_consumed, to_read, sf);
if (data->cfg.read_callback) {
data->cfg.read_callback(dest, data, bytes_consumed, bytes_done);
}
total_read += bytes_done;
dest += bytes_done;
offset += bytes_done;
......@@ -112,7 +114,7 @@ static size_t deblock_io_read(STREAMFILE *sf, uint8_t *dest, off_t offset, size_
return total_read;
}
static size_t deblock_io_size(STREAMFILE *streamfile, deblock_io_data* data) {
static size_t deblock_io_size(STREAMFILE* sf, deblock_io_data* data) {
uint8_t buf[0x04];
if (data->logical_size)
......@@ -124,7 +126,7 @@ static size_t deblock_io_size(STREAMFILE *streamfile, deblock_io_data* data) {
}
/* force a fake read at max offset, to get max logical_offset (will be reset next read) */
deblock_io_read(streamfile, buf, 0x7FFFFFFF, 1, data);
deblock_io_read(sf, buf, 0x7FFFFFFF, 1, data);
data->logical_size = data->logical_offset;
//todo tests:
......@@ -141,8 +143,8 @@ static size_t deblock_io_size(STREAMFILE *streamfile, deblock_io_data* data) {
* decoder can't easily use blocked layout, or some other weird feature. It "filters" data so
* reader only sees clean data without blocks. Must pass setup config and a callback that sets
* sizes of a single block. */
STREAMFILE* open_io_deblock_streamfile_f(STREAMFILE *sf, deblock_config_t *cfg) {
STREAMFILE *new_sf = NULL;
STREAMFILE* open_io_deblock_streamfile_f(STREAMFILE* sf, deblock_config_t *cfg) {
STREAMFILE* new_sf = NULL;
deblock_io_data io_data = {0};
/* prepare data */
......
......@@ -34,7 +34,9 @@ struct deblock_config_t {
size_t interleave_last_count;
/* callback that setups deblock_io_data state, normally block_size and data_size */
void (*block_callback)(STREAMFILE *sf, deblock_io_data *data);
void (*block_callback)(STREAMFILE* sf, deblock_io_data* data);
/* callback that alters block, with the current position into the block (0=beginning) */
void (*read_callback)(uint8_t* dst, deblock_io_data* data, size_t block_pos, size_t read_size);
} ;
struct deblock_io_data {
......@@ -56,6 +58,6 @@ struct deblock_io_data {
off_t physical_end;
};
STREAMFILE* open_io_deblock_streamfile_f(STREAMFILE *sf, deblock_config_t *cfg);
STREAMFILE* open_io_deblock_streamfile_f(STREAMFILE* sf, deblock_config_t* cfg);
#endif /* _DEBLOCK_STREAMFILE_H_ */
......@@ -503,6 +503,9 @@ fail:
static STREAMFILE *open_mapfile_pair(STREAMFILE* sf, int track, int num_tracks) {
static const char *const mapfile_pairs[][2] = {
/* standard cases, replace map part with mus part (from the end to preserve prefixes) */
{"game.mpf", "Game_Stream.mus"}, /* Skate */
{"ipod.mpf", "Ipod_Stream.mus"},
{"world.mpf", "World_Stream.mus"},
{"FreSkate.mpf", "track.mus,ram.mus"}, /* Skate It */
{"nsf_sing.mpf", "track_main.mus"}, /* Need for Speed: Nitro */
{"nsf_wii.mpf", "Track.mus"}, /* Need for Speed: Nitro */
......
......@@ -264,7 +264,6 @@ fail:
/* EA BNK with variable header - from EA games SFXs; also created by sx.exe */
VGMSTREAM * init_vgmstream_ea_bnk(STREAMFILE* sf) {
off_t offset;
int target_stream = sf->stream_index;
/* check extension */
......@@ -276,15 +275,8 @@ VGMSTREAM * init_vgmstream_ea_bnk(STREAMFILE* sf) {
if (!check_extensions(sf,"bnk,sdt,mus,abk,ast"))
goto fail;
/* check header (doesn't use EA blocks, otherwise very similar to SCHl) */
if (read_32bitBE(0x100,sf) == EA_BNK_HEADER_LE)
offset = 0x100; /* Harry Potter and the Goblet of Fire (PS2) .mus have weird extra 0x100 bytes */
else
offset = 0x00;
if (target_stream == 0) target_stream = 1;
return parse_bnk_header(sf, offset, target_stream - 1, 0);
return parse_bnk_header(sf, 0x00, target_stream - 1, 0);
fail:
return NULL;
......
......@@ -3,34 +3,28 @@
#ifdef VGM_USE_FFMPEG
static int read_pos_file(uint8_t * buf, size_t bufsize, STREAMFILE *streamFile);
/**
* Generic init FFmpeg and vgmstream for any file supported by FFmpeg.
* Called by vgmstream when trying to identify the file type (if the player allows it).
*/
VGMSTREAM * init_vgmstream_ffmpeg(STREAMFILE *streamFile) {
return init_vgmstream_ffmpeg_offset( streamFile, 0, streamFile->get_size(streamFile) );
}
static int read_pos_file(uint8_t* buf, size_t bufsize, STREAMFILE* sf);
static int find_ogg_loops(ffmpeg_codec_data* data, int32_t* p_loop_start, int32_t* p_loop_end);
VGMSTREAM * init_vgmstream_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size) {
VGMSTREAM *vgmstream = NULL;
ffmpeg_codec_data *data = NULL;
/* parses any file supported by FFmpeg and not handled elsewhere (mainly: MP4/AAC, MP3, MPC, FLAC) */
VGMSTREAM* init_vgmstream_ffmpeg(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
ffmpeg_codec_data* data = NULL;
int loop_flag = 0;
int32_t loop_start = 0, loop_end = 0, num_samples = 0;
int total_subsongs, target_subsong = streamFile->stream_index;
int total_subsongs, target_subsong = sf->stream_index;
/* no checks */
//if (!check_extensions(streamFile, "..."))
//if (!check_extensions(sf, "..."))
// goto fail;
/* don't try to open headers and other mini files */
if (get_streamfile_size(streamFile) <= 0x1000)
if (get_streamfile_size(sf) <= 0x1000)
goto fail;
/* init ffmpeg */
data = init_ffmpeg_offset(streamFile, start, size);
data = init_ffmpeg_offset(sf, 0, get_streamfile_size(sf));
if (!data) return NULL;
total_subsongs = data->streamCount;
......@@ -41,38 +35,43 @@ VGMSTREAM * init_vgmstream_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start,
{
uint8_t posbuf[4+4+4];
if ( read_pos_file(posbuf, 4+4+4, streamFile) ) {
loop_start = get_32bitLE(posbuf+0);
loop_end = get_32bitLE(posbuf+4);
if (read_pos_file(posbuf, 4+4+4, sf)) {
loop_start = get_s32le(posbuf+0);
loop_end = get_s32le(posbuf+4);
loop_flag = 1; /* incorrect looping will be validated outside */
/* FFmpeg can't always determine totalSamples correctly so optionally load it (can be 0/NULL)
* won't crash and will output silence if no loop points and bigger than actual stream's samples */
num_samples = get_32bitLE(posbuf+8);
num_samples = get_s32le(posbuf+8);
}
}
/* try to read Ogg loop tags (abridged) */
if (loop_flag == 0 && read_u32be(0x00, sf) == 0x4F676753) { /* "OggS" */
loop_flag = find_ogg_loops(data, &loop_start, &loop_end);
}
/* hack for AAC files (will return 0 samples if not an actual file) */
if (!num_samples && check_extensions(streamFile, "aac,laac")) {
num_samples = aac_get_samples(streamFile, 0x00, get_streamfile_size(streamFile));
if (!num_samples && check_extensions(sf, "aac,laac")) {
num_samples = aac_get_samples(sf, 0x00, get_streamfile_size(sf));
}
#ifdef VGM_USE_MPEG
/* hack for MP3 files (will return 0 samples if not an actual file)
* .mus: Marc Ecko's Getting Up (PC) */
if (!num_samples && check_extensions(streamFile, "mp3,lmp3,mus")) {
num_samples = mpeg_get_samples(streamFile, 0x00, get_streamfile_size(streamFile));
if (!num_samples && check_extensions(sf, "mp3,lmp3,mus")) {
num_samples = mpeg_get_samples(sf, 0x00, get_streamfile_size(sf));
}
#endif
/* hack for MPC, that seeks/resets incorrectly due to seek table shenanigans */
if (read_32bitBE(0x00, streamFile) == 0x4D502B07 || /* "MP+\7" (Musepack V7) */
read_32bitBE(0x00, streamFile) == 0x4D50434B) { /* "MPCK" (Musepack V8) */
if (read_u32be(0x00, sf) == 0x4D502B07 || /* "MP+\7" (Musepack V7) */
read_u32be(0x00, sf) == 0x4D50434B) { /* "MPCK" (Musepack V8) */
ffmpeg_set_force_seek(data);
}
/* default but often inaccurate when calculated using bitrate (wrong for VBR) */
if (!num_samples) {
num_samples = data->totalSamples;
num_samples = data->totalSamples; /* may be 0 if FFmpeg can't precalculate it */
}
......@@ -87,15 +86,8 @@ VGMSTREAM * init_vgmstream_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start,
vgmstream->layout_type = layout_none;
vgmstream->num_samples = num_samples;
if (loop_flag) {
vgmstream->loop_start_sample = loop_start;
vgmstream->loop_end_sample = loop_end;
}
/* this may happen for some streams if FFmpeg can't determine it (ex. AAC) */
if (vgmstream->num_samples <= 0)
goto fail;
vgmstream->loop_start_sample = loop_start;
vgmstream->loop_end_sample = loop_end;
vgmstream->channel_layout = ffmpeg_get_channel_layout(vgmstream->codec_data);
return vgmstream;
......@@ -111,46 +103,94 @@ fail:
}
/**
* open file containing looping data and copy to buffer
*
* returns true if found and copied
*/
int read_pos_file(uint8_t * buf, size_t bufsize, STREAMFILE *streamFile) {
/* open file containing looping data and copy to buffer, returns true if found and copied */
int read_pos_file(uint8_t* buf, size_t bufsize, STREAMFILE* sf) {
char posname[PATH_LIMIT];
char filename[PATH_LIMIT];
/*size_t bytes_read;*/
STREAMFILE * streamFilePos= NULL;
STREAMFILE* sf_pos = NULL;
streamFile->get_name(streamFile,filename,sizeof(filename));
get_streamfile_name(sf,filename,sizeof(filename));
if (strlen(filename)+4 > sizeof(posname)) goto fail;
if (strlen(filename)+4 > sizeof(posname))
goto fail;
/* try to open a posfile using variations: "(name.ext).pos" */
{
strcpy(posname, filename);
strcat(posname, ".pos");
streamFilePos = streamFile->open(streamFile,posname,STREAMFILE_DEFAULT_BUFFER_SIZE);
if (streamFilePos) goto found;
sf_pos = open_streamfile(sf, posname);;
if (sf_pos) goto found;
goto fail;
}
found:
//if (get_streamfile_size(streamFilePos) != bufsize) goto fail;
//if (get_streamfile_size(sf_pos) != bufsize) goto fail;
/* allow pos files to be of different sizes in case of new features, just fill all we can */
memset(buf, 0, bufsize);
read_streamfile(buf, 0, bufsize, streamFilePos);
read_streamfile(buf, 0, bufsize, sf_pos);
close_streamfile(streamFilePos);
close_streamfile(sf_pos);
return 1;
fail:
if (streamFilePos) close_streamfile(streamFilePos);
close_streamfile(sf_pos);
return 0;
}
/* loop tag handling could be unified with ogg_vorbis.c, but that one has a extra features too */
static int find_ogg_loops(ffmpeg_codec_data* data, int32_t* p_loop_start, int32_t* p_loop_end) {
char* endptr;
const char* value;
int loop_flag = 0;
int32_t loop_start = -1, loop_end = -1;
// Try to detect the loop flags based on current file metadata
value = ffmpeg_get_metadata_value(data, "LoopStart");
if (value != NULL) {
loop_start = strtol(value, &endptr, 10);
loop_flag = 1;
}
value = ffmpeg_get_metadata_value(data, "LoopEnd");
if (value != NULL) {
loop_end = strtol(value, &endptr, 10);
loop_flag = 1;
}
if (loop_flag) {
if (loop_end <= 0) {
// Detected a loop, but loop_end is still undefined or wrong. Try to calculate it.
value = ffmpeg_get_metadata_value(data, "LoopLength");
if (value != NULL) {
int loop_length = strtol(value, &endptr, 10);
if (loop_start != -1) loop_end = loop_start + loop_length;
}
}
if (loop_end <= 0) {
// Looks a calculation was not possible, or tag value is wrongly set. Use the end of track as end value
loop_end = data->totalSamples;
}
if (loop_start <= 0) {
// Weird edge case: loopend is defined and there's a loop, but loopstart was never defined. Reset to sane value
loop_start = 0;
}
} else {
// Every other attempt to detect loop information failed, reset start/end flags to sane values
loop_start = 0;
loop_end = 0;
}