Updated VGMStream to r1050-3828-g54f06ba6
parent
91039aea9c
commit
0a7be8ab41
|
@ -595,6 +595,7 @@ ffmpeg_codec_data* init_ffmpeg_switch_opus_config(STREAMFILE* sf, off_t start_of
|
|||
ffmpeg_codec_data* init_ffmpeg_switch_opus(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate);
|
||||
ffmpeg_codec_data* init_ffmpeg_ue4_opus(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate);
|
||||
ffmpeg_codec_data* init_ffmpeg_ea_opus(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate);
|
||||
ffmpeg_codec_data* init_ffmpeg_ea_opusm(STREAMFILE* sf, off_t data_offset, size_t data_size, opus_config* cfg);
|
||||
ffmpeg_codec_data* init_ffmpeg_x_opus(STREAMFILE* sf, off_t table_offset, int table_count, off_t data_offset, size_t data_size, int channels, int skip);
|
||||
ffmpeg_codec_data* init_ffmpeg_fsb_opus(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate);
|
||||
ffmpeg_codec_data* init_ffmpeg_wwise_opus(STREAMFILE* sf, off_t data_offset, size_t data_size, opus_config* cfg);
|
||||
|
|
|
@ -329,8 +329,15 @@ ffmpeg_codec_data* init_ffmpeg_header_offset_subsong(STREAMFILE* sf, uint8_t* he
|
|||
|
||||
/* expose start samples to be skipped (encoder delay, usually added by MDCT-based encoders like AAC/MP3/ATRAC3/XMA/etc)
|
||||
* get after init_seek because some demuxers like AAC only fill skip_samples for the first packet */
|
||||
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 64, 100)
|
||||
if (stream->start_skip_samples) /* samples to skip in the first packet */
|
||||
data->skipSamples = stream->start_skip_samples;
|
||||
else if (stream->skip_samples) /* samples to skip in any packet (first in this case), used sometimes instead (ex. AAC) */
|
||||
data->skipSamples = stream->skip_samples;
|
||||
#else
|
||||
if (stream->start_time)
|
||||
data->skipSamples = av_rescale_q(stream->start_time, stream->time_base, tb);
|
||||
#endif
|
||||
|
||||
/* check ways to skip encoder delay/padding, for debugging purposes (some may be old/unused/encoder only/etc) */
|
||||
VGM_ASSERT(data->codecCtx->delay > 0, "FFMPEG: delay %i\n", (int)data->codecCtx->delay);//delay: OPUS
|
||||
|
@ -338,7 +345,12 @@ ffmpeg_codec_data* init_ffmpeg_header_offset_subsong(STREAMFILE* sf, uint8_t* he
|
|||
VGM_ASSERT(stream->codecpar->initial_padding > 0, "FFMPEG: initial_padding %i\n", (int)stream->codecpar->initial_padding);//delay: OPUS
|
||||
VGM_ASSERT(stream->codecpar->trailing_padding > 0, "FFMPEG: trailing_padding %i\n", (int)stream->codecpar->trailing_padding);
|
||||
VGM_ASSERT(stream->codecpar->seek_preroll > 0, "FFMPEG: seek_preroll %i\n", (int)stream->codecpar->seek_preroll);//seek delay: OPUS
|
||||
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 64, 100)
|
||||
VGM_ASSERT(stream->skip_samples > 0, "FFMPEG: skip_samples %i\n", (int)stream->skip_samples); //delay: MP4
|
||||
VGM_ASSERT(stream->start_skip_samples > 0, "FFMPEG: start_skip_samples %i\n", (int)stream->start_skip_samples); //delay: MP3
|
||||
#else
|
||||
VGM_ASSERT(stream->start_time > 0, "FFMPEG: start_time %i\n", (int)stream->start_time); //delay
|
||||
#endif
|
||||
VGM_ASSERT(stream->first_discard_sample > 0, "FFMPEG: first_discard_sample %i\n", (int)stream->first_discard_sample); //padding: MP3
|
||||
VGM_ASSERT(stream->last_discard_sample > 0, "FFMPEG: last_discard_sample %i\n", (int)stream->last_discard_sample); //padding: MP3
|
||||
/* also negative timestamp for formats like OGG/OPUS */
|
||||
|
@ -792,7 +804,12 @@ void seek_ffmpeg(ffmpeg_codec_data* data, int32_t num_sample) {
|
|||
if (data->skip_samples_set) {
|
||||
AVStream *stream = data->formatCtx->streams[data->streamIndex];
|
||||
/* sometimes (ex. AAC) after seeking to the first packet skip_samples is restored, but we want our value */
|
||||
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 64, 100)
|
||||
stream->skip_samples = 0;
|
||||
stream->start_skip_samples = 0;
|
||||
#else
|
||||
stream->start_time = 0;
|
||||
#endif
|
||||
|
||||
data->samples_discard += data->skipSamples;
|
||||
}
|
||||
|
@ -876,7 +893,12 @@ void ffmpeg_set_skip_samples(ffmpeg_codec_data* data, int skip_samples) {
|
|||
|
||||
/* overwrite FFmpeg's skip samples */
|
||||
stream = data->formatCtx->streams[data->streamIndex];
|
||||
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 64, 100)
|
||||
stream->start_skip_samples = 0; /* used for the first packet *if* pts=0 */
|
||||
stream->skip_samples = 0; /* skip_samples can be used for any packet */
|
||||
#else
|
||||
stream->start_time = 0;
|
||||
#endif
|
||||
|
||||
/* set skip samples with our internal discard */
|
||||
data->skip_samples_set = 1;
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
* https://github.com/hcs64/ww2ogg
|
||||
*/
|
||||
|
||||
typedef enum { OPUS_SWITCH, OPUS_UE4_v1, OPUS_UE4_v2, OPUS_EA, OPUS_X, OPUS_FSB, OPUS_WWISE, OPUS_FIXED } opus_type_t;
|
||||
typedef enum { OPUS_SWITCH, OPUS_UE4_v1, OPUS_UE4_v2, OPUS_EA, OPUS_EA_M, OPUS_X, OPUS_FSB, OPUS_WWISE, OPUS_FIXED } opus_type_t;
|
||||
|
||||
static size_t make_oggs_first(uint8_t *buf, int buf_size, opus_config *cfg);
|
||||
static size_t make_oggs_page(uint8_t *buf, int buf_size, size_t data_size, int page_sequence, int granule);
|
||||
|
@ -128,6 +128,17 @@ static size_t opus_io_read(STREAMFILE* sf, uint8_t *dest, off_t offset, size_t l
|
|||
data_size = read_u16be(data->physical_offset, sf);
|
||||
skip_size = 0x02;
|
||||
break;
|
||||
case OPUS_EA_M: {
|
||||
uint8_t flag = read_u8(data->physical_offset + 0x00, sf);
|
||||
if (flag == 0x48) { /* should start on 0x44 though */
|
||||
data->physical_offset += read_u16be(data->physical_offset + 0x02, sf);
|
||||
flag = read_u8(data->physical_offset + 0x00, sf);
|
||||
}
|
||||
data_size = read_u16be(data->physical_offset + 0x02, sf);
|
||||
skip_size = (flag == 0x45) ? data_size : 0x08;
|
||||
data_size -= skip_size;
|
||||
break;
|
||||
}
|
||||
case OPUS_X:
|
||||
case OPUS_WWISE:
|
||||
data_size = get_table_frame_size(data, data->sequence - 2);
|
||||
|
@ -195,7 +206,7 @@ static size_t opus_io_read(STREAMFILE* sf, uint8_t *dest, off_t offset, size_t l
|
|||
|
||||
|
||||
static size_t opus_io_size(STREAMFILE* sf, opus_io_data* data) {
|
||||
off_t physical_offset, max_physical_offset;
|
||||
off_t offset, max_offset;
|
||||
size_t logical_size = 0;
|
||||
int packet = 0;
|
||||
|
||||
|
@ -207,32 +218,43 @@ static size_t opus_io_size(STREAMFILE* sf, opus_io_data* data) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
physical_offset = data->stream_offset;
|
||||
max_physical_offset = data->stream_offset + data->stream_size;
|
||||
offset = data->stream_offset;
|
||||
max_offset = data->stream_offset + data->stream_size;
|
||||
logical_size = data->head_size;
|
||||
|
||||
/* get size of the logical stream */
|
||||
while (physical_offset < max_physical_offset) {
|
||||
while (offset < max_offset) {
|
||||
size_t data_size, skip_size, oggs_size;
|
||||
|
||||
switch(data->type) {
|
||||
case OPUS_SWITCH:
|
||||
data_size = read_u32be(physical_offset, sf);
|
||||
data_size = read_u32be(offset, sf);
|
||||
skip_size = 0x08;
|
||||
break;
|
||||
case OPUS_UE4_v1:
|
||||
case OPUS_FSB:
|
||||
data_size = read_u16le(physical_offset, sf);
|
||||
data_size = read_u16le(offset, sf);
|
||||
skip_size = 0x02;
|
||||
break;
|
||||
case OPUS_UE4_v2:
|
||||
data_size = read_u16le(physical_offset, sf);
|
||||
data_size = read_u16le(offset, sf);
|
||||
skip_size = 0x02 + 0x02;
|
||||
break;
|
||||
case OPUS_EA:
|
||||
data_size = read_u16be(physical_offset, sf);
|
||||
data_size = read_u16be(offset, sf);
|
||||
skip_size = 0x02;
|
||||
break;
|
||||
case OPUS_EA_M: {
|
||||
uint8_t flag = read_u8(offset + 0x00, sf);
|
||||
if (flag == 0x48) {
|
||||
offset += read_u16be(offset + 0x02, sf);
|
||||
flag = read_u8(offset + 0x00, sf);
|
||||
}
|
||||
data_size = read_u16be(offset + 0x02, sf);
|
||||
skip_size = (flag == 0x45) ? data_size : 0x08;
|
||||
data_size -= skip_size;
|
||||
break;
|
||||
}
|
||||
case OPUS_X:
|
||||
case OPUS_WWISE:
|
||||
data_size = get_table_frame_size(data, packet);
|
||||
|
@ -247,24 +269,24 @@ static size_t opus_io_size(STREAMFILE* sf, opus_io_data* data) {
|
|||
}
|
||||
|
||||
/* FSB pads data after end (total size without frame headers is given but not too useful here) */
|
||||
if (data->type == OPUS_FSB && data_size == 0) {
|
||||
if ((data->type == OPUS_FSB || data->type == OPUS_EA_M) && data_size == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (data_size == 0) {
|
||||
VGM_LOG("OPUS: data_size is 0 at %x\n", (uint32_t)physical_offset);
|
||||
VGM_LOG("OPUS: data_size is 0 at %x\n", (uint32_t)offset);
|
||||
return 0; /* bad rip? or could 'break' and truck along */
|
||||
}
|
||||
|
||||
oggs_size = 0x1b + (int)(data_size / 0xFF + 1); /* OggS page: base size + lacing values */
|
||||
|
||||
physical_offset += data_size + skip_size;
|
||||
offset += data_size + skip_size;
|
||||
logical_size += oggs_size + data_size;
|
||||
packet++;
|
||||
}
|
||||
|
||||
/* logical size can be bigger though */
|
||||
if (physical_offset > get_streamfile_size(sf)) {
|
||||
if (offset > get_streamfile_size(sf)) {
|
||||
VGM_LOG("OPUS: wrong size\n");
|
||||
return 0;
|
||||
}
|
||||
|
@ -485,6 +507,11 @@ static size_t make_opus_header(uint8_t* buf, int buf_size, opus_config *cfg) {
|
|||
header_size += 0x01+0x01+cfg->channels;
|
||||
}
|
||||
|
||||
if (cfg->skip < 0) {
|
||||
VGM_LOG("OPUS: wrong skip %i\n", cfg->skip);
|
||||
cfg->skip = 0; /* ??? */
|
||||
}
|
||||
|
||||
if (header_size > buf_size) {
|
||||
VGM_LOG("OPUS: buffer can't hold header\n");
|
||||
goto fail;
|
||||
|
@ -623,6 +650,12 @@ static size_t custom_opus_get_samples(off_t offset, size_t stream_size, STREAMFI
|
|||
data_size = read_u16be(offset, sf);
|
||||
skip_size = 0x02;
|
||||
break;
|
||||
#if 0
|
||||
case OPUS_EA_M:
|
||||
/* num_samples should exist on header */
|
||||
...
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if 0 //needs data*, num_samples should exist on header
|
||||
case OPUS_X:
|
||||
|
@ -673,6 +706,15 @@ static size_t custom_opus_get_encoder_delay(off_t offset, STREAMFILE* sf, opus_t
|
|||
case OPUS_EA:
|
||||
skip_size = 0x02;
|
||||
break;
|
||||
case OPUS_EA_M: {
|
||||
uint8_t flag = read_u8(offset + 0x00, sf);
|
||||
if (flag == 0x48) {
|
||||
offset += read_u16be(offset + 0x02, sf);
|
||||
flag = read_u8(offset + 0x00, sf);
|
||||
}
|
||||
skip_size = read_u16be(offset + 0x02, sf);
|
||||
break;
|
||||
}
|
||||
case OPUS_X:
|
||||
case OPUS_WWISE:
|
||||
skip_size = 0x00;
|
||||
|
@ -768,6 +810,9 @@ ffmpeg_codec_data* init_ffmpeg_ue4_opus(STREAMFILE* sf, off_t start_offset, size
|
|||
ffmpeg_codec_data* init_ffmpeg_ea_opus(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate) {
|
||||
return init_ffmpeg_custom_opus(sf, start_offset, data_size, channels, skip, sample_rate, OPUS_EA);
|
||||
}
|
||||
ffmpeg_codec_data* init_ffmpeg_ea_opusm(STREAMFILE* sf, off_t data_offset, size_t data_size, opus_config* cfg) {
|
||||
return init_ffmpeg_custom_opus_config(sf, data_offset, data_size, cfg, OPUS_EA_M);
|
||||
}
|
||||
ffmpeg_codec_data* init_ffmpeg_x_opus(STREAMFILE* sf, off_t table_offset, int table_count, off_t data_offset, size_t data_size, int channels, int skip) {
|
||||
return init_ffmpeg_custom_table_opus(sf, table_offset, table_count, data_offset, data_size, channels, skip, 0, OPUS_X);
|
||||
}
|
||||
|
|
|
@ -22,6 +22,9 @@
|
|||
#define EAAC_CODEC_EATRAX 0x0a
|
||||
#define EAAC_CODEC_EAMP3 0x0b
|
||||
#define EAAC_CODEC_EAOPUS 0x0c
|
||||
#define EAAC_CODEC_EAATRAC9 0x0d
|
||||
#define EAAC_CODEC_EAOPUSM 0x0e
|
||||
#define EAAC_CODEC_EAOPUSMU 0x0f
|
||||
|
||||
#define EAAC_TYPE_RAM 0x00
|
||||
#define EAAC_TYPE_STREAM 0x01
|
||||
|
@ -34,12 +37,12 @@
|
|||
#define EAAC_BLOCKID1_DATA 0x44 /* 'D' */
|
||||
#define EAAC_BLOCKID1_END 0x45 /* 'E' */
|
||||
|
||||
static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE* sf_head, STREAMFILE* sf_data, off_t header_offset, off_t start_offset, meta_t meta_type, int standalone);
|
||||
static VGMSTREAM *parse_s10a_header(STREAMFILE* sf, off_t offset, uint16_t target_index, off_t ast_offset);
|
||||
static VGMSTREAM* init_vgmstream_eaaudiocore_header(STREAMFILE* sf_head, STREAMFILE* sf_data, off_t header_offset, off_t start_offset, meta_t meta_type, int standalone);
|
||||
static VGMSTREAM* parse_s10a_header(STREAMFILE* sf, off_t offset, uint16_t target_index, off_t ast_offset);
|
||||
|
||||
|
||||
/* .SNR+SNS - from EA latest games (~2005-2010), v0 header */
|
||||
VGMSTREAM * init_vgmstream_ea_snr_sns(STREAMFILE* sf) {
|
||||
VGMSTREAM* init_vgmstream_ea_snr_sns(STREAMFILE* sf) {
|
||||
/* check extension, case insensitive */
|
||||
if (!check_extensions(sf,"snr"))
|
||||
goto fail;
|
||||
|
@ -51,7 +54,7 @@ fail:
|
|||
}
|
||||
|
||||
/* .SPS - from EA latest games (~2010~present), v1 header */
|
||||
VGMSTREAM * init_vgmstream_ea_sps(STREAMFILE* sf) {
|
||||
VGMSTREAM* init_vgmstream_ea_sps(STREAMFILE* sf) {
|
||||
/* check extension, case insensitive */
|
||||
if (!check_extensions(sf,"sps"))
|
||||
goto fail;
|
||||
|
@ -63,8 +66,8 @@ fail:
|
|||
}
|
||||
|
||||
/* .SNU - from EA Redwood Shores/Visceral games (Dead Space, Dante's Inferno, The Godfather 2), v0 header */
|
||||
VGMSTREAM * init_vgmstream_ea_snu(STREAMFILE *sf) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
VGMSTREAM* init_vgmstream_ea_snu(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t start_offset, header_offset;
|
||||
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
|
||||
|
||||
|
@ -102,7 +105,7 @@ fail:
|
|||
}
|
||||
|
||||
/* EA ABK - ABK header seems to be same as in the old games but the sound table is different and it contains SNR/SNS sounds instead */
|
||||
VGMSTREAM * init_vgmstream_ea_abk_eaac(STREAMFILE* sf) {
|
||||
VGMSTREAM* init_vgmstream_ea_abk_eaac(STREAMFILE* sf) {
|
||||
int is_dupe, total_sounds = 0, target_stream = sf->stream_index;
|
||||
off_t bnk_offset, modules_table, module_data, player_offset, samples_table, entry_offset, ast_offset;
|
||||
off_t cfg_num_players_off, cfg_module_data_off, cfg_module_entry_size, cfg_samples_table_off;
|
||||
|
@ -110,7 +113,7 @@ VGMSTREAM * init_vgmstream_ea_abk_eaac(STREAMFILE* sf) {
|
|||
uint16_t num_modules, bnk_index, bnk_target_index;
|
||||
uint8_t num_players;
|
||||
off_t sample_tables[0x400];
|
||||
VGMSTREAM *vgmstream;
|
||||
VGMSTREAM* vgmstream;
|
||||
int32_t(*read_32bit)(off_t, STREAMFILE*);
|
||||
int16_t(*read_16bit)(off_t, STREAMFILE*);
|
||||
|
||||
|
@ -226,11 +229,11 @@ fail:
|
|||
}
|
||||
|
||||
/* EA S10A header - seen inside new ABK files. Putting it here in case it's encountered stand-alone. */
|
||||
static VGMSTREAM * parse_s10a_header(STREAMFILE* sf, off_t offset, uint16_t target_index, off_t sns_offset) {
|
||||
static VGMSTREAM* parse_s10a_header(STREAMFILE* sf, off_t offset, uint16_t target_index, off_t sns_offset) {
|
||||
uint32_t num_sounds;
|
||||
off_t snr_offset;
|
||||
STREAMFILE *astFile = NULL;
|
||||
VGMSTREAM *vgmstream;
|
||||
VGMSTREAM* vgmstream;
|
||||
|
||||
/* header is always big endian */
|
||||
/* 0x00: header magic */
|
||||
|
@ -279,12 +282,12 @@ fail:
|
|||
}
|
||||
|
||||
/* EA SBR/SBS - used in older 7th gen games for storing SFX */
|
||||
VGMSTREAM * init_vgmstream_ea_sbr(STREAMFILE* sf) {
|
||||
VGMSTREAM* init_vgmstream_ea_sbr(STREAMFILE* sf) {
|
||||
uint32_t i, num_sounds, type_desc;
|
||||
uint16_t num_metas, meta_type;
|
||||
off_t table_offset, types_offset, entry_offset, metas_offset, data_offset, snr_offset, sns_offset;
|
||||
STREAMFILE *sbsFile = NULL;
|
||||
VGMSTREAM *vgmstream = NULL;
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
int target_stream = sf->stream_index;
|
||||
|
||||
if (!check_extensions(sf, "sbr"))
|
||||
|
@ -372,14 +375,14 @@ fail:
|
|||
}
|
||||
|
||||
/* EA HDR/STH/DAT - seen in older 7th gen games, used for storing speech */
|
||||
VGMSTREAM * init_vgmstream_ea_hdr_sth_dat(STREAMFILE* sf) {
|
||||
VGMSTREAM* init_vgmstream_ea_hdr_sth_dat(STREAMFILE* sf) {
|
||||
int target_stream = sf->stream_index;
|
||||
uint32_t snr_offset, sns_offset, block_size;
|
||||
uint16_t sth_offset, sth_offset2;
|
||||
uint8_t userdata_size, total_sounds, block_id;
|
||||
size_t dat_size;
|
||||
STREAMFILE *sf_dat = NULL, *sf_sth = NULL;
|
||||
VGMSTREAM *vgmstream;
|
||||
VGMSTREAM* vgmstream;
|
||||
uint32_t(*read_u32)(off_t, STREAMFILE*);
|
||||
|
||||
/* 0x00: ID */
|
||||
|
@ -606,13 +609,13 @@ static STREAMFILE *open_mapfile_pair(STREAMFILE* sf, int track, int num_tracks)
|
|||
}
|
||||
|
||||
/* EA MPF/MUS combo - used in older 7th gen games for storing interactive music */
|
||||
VGMSTREAM * init_vgmstream_ea_mpf_mus_eaac(STREAMFILE* sf) {
|
||||
VGMSTREAM* init_vgmstream_ea_mpf_mus_eaac(STREAMFILE* sf) {
|
||||
uint32_t num_tracks, track_start, track_checksum = 0, mus_sounds, mus_stream = 0;
|
||||
uint32_t tracks_table, samples_table, eof_offset, table_offset, entry_offset, snr_offset, sns_offset;
|
||||
uint16_t num_subbanks;
|
||||
uint8_t version, sub_version;
|
||||
STREAMFILE *musFile = NULL;
|
||||
VGMSTREAM *vgmstream = NULL;
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
int i;
|
||||
int target_stream = sf->stream_index, total_streams, is_ram = 0;
|
||||
uint32_t(*read_u32)(off_t, STREAMFILE *);
|
||||
|
@ -749,10 +752,10 @@ fail:
|
|||
}
|
||||
|
||||
/* EA TMX - used for engine sounds in NFS games (2007-2011) */
|
||||
VGMSTREAM * init_vgmstream_ea_tmx(STREAMFILE* sf) {
|
||||
VGMSTREAM* init_vgmstream_ea_tmx(STREAMFILE* sf) {
|
||||
uint32_t num_sounds, sound_type, table_offset, data_offset, entry_offset, sound_offset;
|
||||
VGMSTREAM *vgmstream = NULL;
|
||||
STREAMFILE *temp_sf = NULL;
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
STREAMFILE* temp_sf = NULL;
|
||||
int target_stream = sf->stream_index;
|
||||
uint32_t(*read_u32)(off_t, STREAMFILE *);
|
||||
|
||||
|
@ -805,7 +808,7 @@ fail:
|
|||
}
|
||||
|
||||
/* EA Harmony Sample Bank - used in 8th gen EA Sports games */
|
||||
VGMSTREAM * init_vgmstream_ea_sbr_harmony(STREAMFILE *sf) {
|
||||
VGMSTREAM* init_vgmstream_ea_sbr_harmony(STREAMFILE* sf) {
|
||||
uint64_t set_sounds, base_offset, sound_offset;
|
||||
uint32_t chunk_id, data_offset, table_offset, dset_offset, sound_table_offset;
|
||||
uint16_t num_dsets;
|
||||
|
@ -813,7 +816,7 @@ VGMSTREAM * init_vgmstream_ea_sbr_harmony(STREAMFILE *sf) {
|
|||
uint32_t i, j;
|
||||
char sound_name[STREAM_NAME_SIZE];
|
||||
STREAMFILE *sf_sbs = NULL, *sf_data = NULL;
|
||||
VGMSTREAM *vgmstream = NULL;
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
int target_stream = sf->stream_index, total_sounds, local_target, is_streamed = 0;
|
||||
uint64_t(*read_u64)(off_t, STREAMFILE *);
|
||||
uint32_t(*read_u32)(off_t, STREAMFILE*);
|
||||
|
@ -1031,8 +1034,8 @@ static size_t calculate_eaac_size(STREAMFILE* sf, eaac_header *ea, uint32_t num_
|
|||
* Audio "assets" come in separate RAM headers (.SNR/SPH) and raw blocked streams (.SNS/SPS),
|
||||
* or together in pseudoformats (.SNU, .SBR+.SBS banks, .AEMS, .MUS, etc).
|
||||
* Some .SNR include stream data, while .SPS have headers so .SPH is optional. */
|
||||
static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE* sf_head, STREAMFILE* sf_data, off_t header_offset, off_t start_offset, meta_t meta_type, int standalone) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
static VGMSTREAM* init_vgmstream_eaaudiocore_header(STREAMFILE* sf_head, STREAMFILE* sf_data, off_t header_offset, off_t start_offset, meta_t meta_type, int standalone) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
STREAMFILE *temp_sf = NULL, *sf = NULL, *snsFile = NULL;
|
||||
uint32_t header1, header2, header_block_size = 0, header_size;
|
||||
uint8_t header_block_id;
|
||||
|
@ -1294,6 +1297,23 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE* sf_head, STREAM
|
|||
/* DSP coefs are read in the blocks */
|
||||
break;
|
||||
|
||||
#ifdef VGM_USE_SPEEX
|
||||
case EAAC_CODEC_EASPEEX: { /* "Esp0": EASpeex (libspeex variant, base versions vary: 1.0.5, 1.2beta3) [FIFA 14 (PS4), FIFA 2020 (Switch)] */
|
||||
/* EASpeex looks normal but simplify with custom IO to avoid worrying about blocks.
|
||||
* First block samples count frames' samples subtracting encoder delay. */
|
||||
|
||||
vgmstream->codec_data = init_speex_ea(eaac.channels);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_SPEEX;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
temp_sf = setup_eaac_audio_streamfile(sf, eaac.version, eaac.codec, eaac.streamed,0,0, 0x00);
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_ATRAC9
|
||||
case EAAC_CODEC_EATRAX: { /* EATrax (unknown FourCC) [Need for Speed: Most Wanted (Vita)] */
|
||||
atrac9_config cfg = {0};
|
||||
|
@ -1320,7 +1340,7 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE* sf_head, STREAM
|
|||
|
||||
|
||||
#ifdef VGM_USE_MPEG
|
||||
case EAAC_CODEC_EAMP3: { /* "EM30"?: EAMP3 [Need for Speed 2015 (PS4)] */
|
||||
case EAAC_CODEC_EAMP3: { /* "EM30": EA-MP3 [Need for Speed 2015 (PS4)] */
|
||||
mpeg_custom_config cfg = {0};
|
||||
|
||||
temp_sf = setup_eaac_audio_streamfile(sf, eaac.version, eaac.codec, eaac.streamed,0,0, 0x00);
|
||||
|
@ -1335,7 +1355,7 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE* sf_head, STREAM
|
|||
#endif
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
case EAAC_CODEC_EAOPUS: { /* "Eop0"? : EAOpus [FIFA 17 (PC), FIFA 19 (Switch)]*/
|
||||
case EAAC_CODEC_EAOPUS: { /* "Eop0": EAOpus [FIFA 17 (PC), FIFA 19 (Switch)]*/
|
||||
vgmstream->layout_data = build_layered_eaaudiocore(sf, &eaac, 0x00);
|
||||
if (!vgmstream->layout_data) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
|
@ -1343,24 +1363,57 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE* sf_head, STREAM
|
|||
break;
|
||||
}
|
||||
#endif
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
//case EAAC_CODEC_EAOPUSMU: /* "MSU0": Multi-Stream Opus Uncoupled (not seen) */
|
||||
case EAAC_CODEC_EAOPUSM: { /* "MSO0": Multi-Stream Opus */
|
||||
off_t offset = 0x00; // eaac.stream_offset;
|
||||
off_t data_size = get_streamfile_size(sf);
|
||||
opus_config cfg = {0};
|
||||
|
||||
#ifdef VGM_USE_SPEEX
|
||||
case EAAC_CODEC_EASPEEX: { /* "Esp0"?: EASpeex (libspeex variant, base versions vary: 1.0.5, 1.2beta3) [FIFA 14 (PS4), FIFA 2020 (Switch)] */
|
||||
/* EASpeex looks normal but simplify with custom IO to avoid worrying about blocks.
|
||||
* First block samples count frames' samples subtracting encoder delay. */
|
||||
cfg.channels = eaac.channels;
|
||||
{
|
||||
uint32_t block_size = read_u32be(offset + 0x00, sf) & 0x00FFFFFF;
|
||||
uint32_t curr_samples = read_u32be(offset + 0x04, sf);
|
||||
uint32_t next_samples = read_u32be(offset + block_size + 0x04, sf);
|
||||
|
||||
vgmstream->codec_data = init_speex_ea(eaac.channels);
|
||||
cfg.skip = next_samples - curr_samples;
|
||||
/* maybe should check if next block exists, but files of single packet? */
|
||||
}
|
||||
|
||||
/* find coupled OPUS streams (internal streams using 2ch) */
|
||||
if (eaac.codec == EAAC_CODEC_EAOPUSMU) {
|
||||
cfg.coupled_count = 0;
|
||||
}
|
||||
else {
|
||||
switch(eaac.channels) {
|
||||
//case 8: cfg.coupled_count = 3; break; /* 2ch+2ch+2ch+1ch+1ch, 5 streams */
|
||||
//case 6: /* 2ch+2ch+1ch+1ch, 4 streams */
|
||||
case 4: cfg.coupled_count = 2; break; /* 2ch+2ch, 2 streams */
|
||||
//case 3: /* 2ch+1ch, 2 streams */
|
||||
case 2: cfg.coupled_count = 1; break; /* 2ch, 1 stream */
|
||||
//case 1: cfg.coupled_count = 0; break; /* 1ch, 1 stream */
|
||||
default: goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* total number internal OPUS streams (should be >0) */
|
||||
cfg.stream_count = cfg.channels - cfg.coupled_count;
|
||||
|
||||
/* We *don't* remove EA blocks b/c in Multi Opus 1 block = 1 Opus packet
|
||||
* Regular EAOPUS uses layers to fake multichannel, this is normal multichannel Opus.
|
||||
* This can be used for stereo too, so probably replaces EAOPUS. */
|
||||
//temp_sf = setup_eaac_audio_streamfile(sf_data, eaac->version, eaac->codec, eaac->streamed,0,0, 0x00);
|
||||
//if (!temp_sf) goto fail;
|
||||
|
||||
vgmstream->codec_data = init_ffmpeg_ea_opusm(sf, offset, data_size, &cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_SPEEX;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
temp_sf = setup_eaac_audio_streamfile(sf, eaac.version, eaac.codec, eaac.streamed,0,0, 0x00);
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
case EAAC_CODEC_EAATRAC9: /* "AT90" (possibly ATRAC9 with a saner layout than EATRAX) */
|
||||
default:
|
||||
VGM_LOG("EA EAAC: unknown codec 0x%02x\n", eaac.codec);
|
||||
goto fail;
|
||||
|
@ -1696,7 +1749,7 @@ static layered_layout_data* build_layered_eaaudiocore(STREAMFILE *sf_data, eaac_
|
|||
goto fail;
|
||||
#endif
|
||||
|
||||
if ( !vgmstream_open_stream(data->layers[i], temp_sf, 0x00) ) {
|
||||
if (!vgmstream_open_stream(data->layers[i], temp_sf, 0x00)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
|
|
@ -360,12 +360,12 @@ VGMSTREAM* init_vgmstream_riff(STREAMFILE* sf) {
|
|||
}
|
||||
|
||||
/* check header */
|
||||
if (read_32bitBE(0x00,sf) != 0x52494646) /* "RIFF" */
|
||||
if (!is_id32be(0x00,sf,"RIFF"))
|
||||
goto fail;
|
||||
if (read_32bitBE(0x08,sf) != 0x57415645) /* "WAVE" */
|
||||
if (!is_id32be(0x08,sf, "WAVE"))
|
||||
goto fail;
|
||||
|
||||
riff_size = read_32bitLE(0x04,sf);
|
||||
riff_size = read_u32le(0x04,sf);
|
||||
file_size = get_streamfile_size(sf);
|
||||
|
||||
/* some games have wonky sizes, selectively fix to catch bad rips and new mutations */
|
||||
|
@ -417,6 +417,9 @@ VGMSTREAM* init_vgmstream_riff(STREAMFILE* sf) {
|
|||
else if (codec == 0xFFFE && riff_size + 0x08 + 0x30 == file_size)
|
||||
riff_size += 0x30; /* [E.X. Troopers (PS3)] (adds "ver /eBIT/tIME/mrkr" empty chunks but RIFF size wasn't updated) */
|
||||
|
||||
else if (codec == 0xFFFE && riff_size + 0x08 + 0x38 == file_size)
|
||||
riff_size += 0x38; /* [Sengoku Basara 4 (PS3)] (adds "ver /eBIT/tIME/mrkr" chunks but RIFF size wasn't updated) */
|
||||
|
||||
else if (codec == 0x0002 && riff_size + 0x08 + 0x1c == file_size)
|
||||
riff_size += 0x1c; /* [Mega Man X Legacy Collection (PC)] (adds "ver /tIME/ver " chunks but RIFF size wasn't updated) */
|
||||
}
|
||||
|
@ -982,16 +985,16 @@ VGMSTREAM* init_vgmstream_rifx(STREAMFILE* sf) {
|
|||
|
||||
|
||||
/* check extension, case insensitive */
|
||||
if ( !check_extensions(sf, "wav,lwav") )
|
||||
if (!check_extensions(sf, "wav,lwav"))
|
||||
goto fail;
|
||||
|
||||
/* check header */
|
||||
if (read_32bitBE(0x00,sf) != 0x52494658) /* "RIFX" */
|
||||
if (!is_id32be(0x00,sf, "RIFX"))
|
||||
goto fail;
|
||||
if (read_32bitBE(0x08,sf) != 0x57415645) /* "WAVE" */
|
||||
if (!is_id32be(0x08,sf, "WAVE"))
|
||||
goto fail;
|
||||
|
||||
riff_size = read_32bitBE(0x04,sf);
|
||||
riff_size = read_u32be(0x04,sf);
|
||||
file_size = get_streamfile_size(sf);
|
||||
|
||||
/* check for truncated RIFF */
|
||||
|
|
|
@ -58,6 +58,8 @@ typedef struct {
|
|||
|
||||
uint32_t interleave;
|
||||
uint32_t interleave_last;
|
||||
uint32_t interleave_first;
|
||||
uint32_t interleave_first_skip;
|
||||
uint32_t channels;
|
||||
uint32_t sample_rate;
|
||||
|
||||
|
@ -330,6 +332,8 @@ VGMSTREAM* init_vgmstream_txth(STREAMFILE* sf) {
|
|||
/* high nibble or low nibble first */
|
||||
vgmstream->codec_config = txth.codec_mode;
|
||||
}
|
||||
|
||||
vgmstream->allow_dual_stereo = 1; /* AICA and PSX */
|
||||
break;
|
||||
|
||||
case coding_PCFX:
|
||||
|
@ -369,6 +373,8 @@ VGMSTREAM* init_vgmstream_txth(STREAMFILE* sf) {
|
|||
|
||||
vgmstream->interleave_block_size = txth.interleave;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
vgmstream->allow_dual_stereo = 1; //???
|
||||
break;
|
||||
|
||||
case coding_MSADPCM:
|
||||
|
@ -456,6 +462,7 @@ VGMSTREAM* init_vgmstream_txth(STREAMFILE* sf) {
|
|||
}
|
||||
}
|
||||
|
||||
vgmstream->allow_dual_stereo = 1;
|
||||
break;
|
||||
|
||||
#ifdef VGM_USE_MPEG
|
||||
|
@ -568,9 +575,19 @@ VGMSTREAM* init_vgmstream_txth(STREAMFILE* sf) {
|
|||
}
|
||||
#endif
|
||||
|
||||
if (vgmstream->interleave_block_size) {
|
||||
if (txth.interleave_first_skip && !txth.interleave_first)
|
||||
txth.interleave_first = txth.interleave;
|
||||
if (txth.interleave_first > txth.interleave_first_skip)
|
||||
txth.interleave_first -= txth.interleave_first_skip;
|
||||
vgmstream->interleave_first_block_size = txth.interleave_first;
|
||||
vgmstream->interleave_first_skip = txth.interleave_first_skip;
|
||||
txth.start_offset += txth.interleave_first_skip;
|
||||
}
|
||||
|
||||
|
||||
vgmstream->coding_type = coding;
|
||||
vgmstream->meta_type = meta_TXTH;
|
||||
vgmstream->allow_dual_stereo = 1;
|
||||
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, txth.sf_body, txth.start_offset))
|
||||
|
@ -648,6 +665,10 @@ static VGMSTREAM* init_subfile(txth_header* txth) {
|
|||
txth->interleave = vgmstream->interleave_block_size;
|
||||
if (!txth->interleave_last)
|
||||
txth->interleave_last = vgmstream->interleave_last_block_size;
|
||||
if (!txth->interleave_first)
|
||||
txth->interleave_first = vgmstream->interleave_first_block_size;
|
||||
if (!txth->interleave_first_skip)
|
||||
txth->interleave_first_skip = vgmstream->interleave_first_skip;
|
||||
//if (!txth->loop_flag) //?
|
||||
// txth->loop_flag = vgmstream->loop_flag;
|
||||
/* sometimes headers set loop start but getting loop_end before subfile init is hard */
|
||||
|
@ -965,6 +986,19 @@ static int parse_keyval(STREAMFILE* sf_, txth_header* txth, const char* key, cha
|
|||
if (!parse_num(txth->sf_head,txth,val, &txth->interleave_last)) goto fail;
|
||||
}
|
||||
}
|
||||
else if (is_string(key,"interleave_first")) {
|
||||
if (!parse_num(txth->sf_head,txth,val, &txth->interleave_first)) goto fail;
|
||||
}
|
||||
else if (is_string(key,"interleave_first_skip")) {
|
||||
if (!parse_num(txth->sf_head,txth,val, &txth->interleave_first_skip)) goto fail;
|
||||
|
||||
/* apply */
|
||||
if (!txth->data_size_set) {
|
||||
int skip = txth->interleave_first_skip * txth->channels;
|
||||
if (txth->data_size && txth->data_size > skip)
|
||||
txth->data_size -= skip;
|
||||
}
|
||||
}
|
||||
|
||||
/* BASE CONFIG */
|
||||
else if (is_string(key,"channels")) {
|
||||
|
@ -978,7 +1012,6 @@ static int parse_keyval(STREAMFILE* sf_, txth_header* txth, const char* key, cha
|
|||
else if (is_string(key,"start_offset")) {
|
||||
if (!parse_num(txth->sf_head,txth,val, &txth->start_offset)) goto fail;
|
||||
|
||||
|
||||
/* apply */
|
||||
if (!txth->data_size_set) {
|
||||
|
||||
|
@ -1805,6 +1838,8 @@ static int parse_num(STREAMFILE* sf, txth_header* txth, const char* val, uint32_
|
|||
else { /* known field */
|
||||
if ((n = is_string_field(val,"interleave"))) value = txth->interleave;
|
||||
else if ((n = is_string_field(val,"interleave_last"))) value = txth->interleave_last;
|
||||
else if ((n = is_string_field(val,"interleave_first"))) value = txth->interleave_first;
|
||||
else if ((n = is_string_field(val,"interleave_first_skip")))value = txth->interleave_first_skip;
|
||||
else if ((n = is_string_field(val,"channels"))) value = txth->channels;
|
||||
else if ((n = is_string_field(val,"sample_rate"))) value = txth->sample_rate;
|
||||
else if ((n = is_string_field(val,"start_offset"))) value = txth->start_offset;
|
||||
|
|
|
@ -29,7 +29,8 @@ typedef struct {
|
|||
off_t audio_stream_size;
|
||||
off_t audio_stream_offset;
|
||||
off_t audio_stream_type;
|
||||
off_t audio_subblock_flag;
|
||||
off_t audio_software_flag;
|
||||
off_t audio_hwmodule_flag;
|
||||
off_t audio_streamed_flag;
|
||||
off_t audio_cd_streamed_flag;
|
||||
off_t audio_loop_flag;
|
||||
|
@ -48,7 +49,8 @@ typedef struct {
|
|||
int audio_streamed_and;
|
||||
int audio_cd_streamed_and;
|
||||
int audio_loop_and;
|
||||
int audio_subblock_and;
|
||||
int audio_software_and;
|
||||
int audio_hwmodule_and;
|
||||
int audio_loc_and;
|
||||
int audio_stereo_and;
|
||||
int audio_ram_streamed_and;
|
||||
|
@ -1846,15 +1848,22 @@ static int parse_type_audio(ubi_sb_header* sb, off_t offset, STREAMFILE* sf) {
|
|||
sb->is_external = (int)!(read_32bit(offset + sb->cfg.audio_internal_flag, sf));
|
||||
}
|
||||
|
||||
/* apparently, there may also be other subblocks based on various flags but they were not seen so far */
|
||||
if (sb->cfg.audio_subblock_flag && sb->cfg.audio_subblock_and) {
|
||||
/* flag probably means "software decoded" */
|
||||
int subblock_flag = read_32bit(offset + sb->cfg.audio_subblock_flag, sf) & sb->cfg.audio_subblock_and;
|
||||
sb->subblock_id = (!subblock_flag) ? 0 : 1;
|
||||
if (sb->cfg.audio_software_flag && sb->cfg.audio_software_and) {
|
||||
/* software decoded and hardware decoded sounds are stored in separate subblocks */
|
||||
int software_flag = read_32bit(offset + sb->cfg.audio_software_flag, sf) & sb->cfg.audio_software_and;
|
||||
sb->subblock_id = (!software_flag) ? 0 : 1;
|
||||
|
||||
if (sb->platform == UBI_PS2) {
|
||||
/* flag appears to mean "load into IOP memory instead of SPU" */
|
||||
int hwmodule_flag = read_32bit(offset + sb->cfg.audio_hwmodule_flag, sf) & sb->cfg.audio_hwmodule_and;
|
||||
sb->subblock_id = (!software_flag) ? ((!hwmodule_flag) ? 0 : 3) : 1;
|
||||
}
|
||||
|
||||
/* PC can have subblock 2 based on two fields near the end but it wasn't seen so far */
|
||||
|
||||
/* stream_type field is not used if the flag is not set (it even contains garbage in some versions)
|
||||
* except for PS3 which has two hardware codecs (PSX and AT3) */
|
||||
if (!subblock_flag && sb->platform != UBI_PS3)
|
||||
if (!software_flag && sb->platform != UBI_PS3)
|
||||
sb->stream_type = 0x00;
|
||||
} else {
|
||||
sb->subblock_id = (sb->stream_type == 0x01) ? 0 : 1;
|
||||
|
@ -2180,7 +2189,7 @@ static int set_hardware_codec_for_platform(ubi_sb_header *sb) {
|
|||
/* find actual codec from type (as different games' stream_type can overlap) */
|
||||
static int parse_stream_codec(ubi_sb_header* sb) {
|
||||
|
||||
if (sb->type == UBI_SEQUENCE)
|
||||
if (sb->type != UBI_AUDIO && sb->type != UBI_LAYER)
|
||||
return 1;
|
||||
|
||||
if (sb->is_dat) {
|
||||
|
@ -2337,7 +2346,7 @@ static int parse_offsets(ubi_sb_header* sb, STREAMFILE* sf) {
|
|||
int32_t (*read_32bit)(off_t,STREAMFILE*) = sb->big_endian ? read_32bitBE : read_32bitLE;
|
||||
uint32_t i, j, k;
|
||||
|
||||
if (sb->type == UBI_SEQUENCE)
|
||||
if (sb->type != UBI_AUDIO && sb->type != UBI_LAYER)
|
||||
return 1;
|
||||
|
||||
if (sb->is_bnm)
|
||||
|
@ -2446,6 +2455,11 @@ static int parse_offsets(ubi_sb_header* sb, STREAMFILE* sf) {
|
|||
break;
|
||||
sb->stream_offset += read_32bit(offset + 0x04, sf);
|
||||
}
|
||||
|
||||
if (i == sb->section3_num) {
|
||||
VGM_LOG("UBI SB: Failed to find subblock %d\n", sb->subblock_id);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
@ -2628,22 +2642,22 @@ static void config_sb_entry(ubi_sb_header* sb, size_t section1_size_entry, size_
|
|||
sb->cfg.section2_entry_size = section2_size_entry;
|
||||
sb->cfg.section3_entry_size = 0x08;
|
||||
}
|
||||
static void config_sb_audio_fs(ubi_sb_header* sb, off_t streamed_flag, off_t subblock_flag, off_t loop_flag) {
|
||||
static void config_sb_audio_fs(ubi_sb_header* sb, off_t streamed_flag, off_t software_flag, off_t loop_flag) {
|
||||
/* audio header with standard flags */
|
||||
sb->cfg.audio_streamed_flag = streamed_flag;
|
||||
sb->cfg.audio_subblock_flag = subblock_flag;
|
||||
sb->cfg.audio_software_flag = software_flag;
|
||||
sb->cfg.audio_loop_flag = loop_flag;
|
||||
sb->cfg.audio_streamed_and = 1;
|
||||
sb->cfg.audio_subblock_and = 1;
|
||||
sb->cfg.audio_software_and = 1;
|
||||
sb->cfg.audio_loop_and = 1;
|
||||
}
|
||||
static void config_sb_audio_fb(ubi_sb_header* sb, off_t flag_bits, int streamed_and, int subblock_and, int loop_and) {
|
||||
static void config_sb_audio_fb(ubi_sb_header* sb, off_t flag_bits, int streamed_and, int software_and, int loop_and) {
|
||||
/* audio header with bit flags */
|
||||
sb->cfg.audio_streamed_flag = flag_bits;
|
||||
sb->cfg.audio_subblock_flag = flag_bits;
|
||||
sb->cfg.audio_software_flag = flag_bits;
|
||||
sb->cfg.audio_loop_flag = flag_bits;
|
||||
sb->cfg.audio_streamed_and = streamed_and;
|
||||
sb->cfg.audio_subblock_and = subblock_and;
|
||||
sb->cfg.audio_software_and = software_and;
|
||||
sb->cfg.audio_loop_and = loop_and;
|
||||
}
|
||||
static void config_sb_audio_hs(ubi_sb_header* sb, off_t channels, off_t sample_rate, off_t num_samples, off_t num_samples2, off_t stream_name, off_t stream_type) {
|
||||
|
@ -2664,29 +2678,40 @@ static void config_sb_audio_he(ubi_sb_header* sb, off_t channels, off_t sample_r
|
|||
sb->cfg.audio_extra_name = extra_name;
|
||||
sb->cfg.audio_stream_type = stream_type;
|
||||
}
|
||||
static void config_sb_audio_fb_ps2(ubi_sb_header* sb, off_t flag_bits, int streamed_and, int software_and, int loop_and, int hwmodule_and) {
|
||||
/* audio header with bit flags */
|
||||
sb->cfg.audio_streamed_flag = flag_bits;
|
||||
sb->cfg.audio_software_flag = flag_bits;
|
||||
sb->cfg.audio_loop_flag = flag_bits;
|
||||
sb->cfg.audio_hwmodule_flag = flag_bits;
|
||||
sb->cfg.audio_streamed_and = streamed_and;
|
||||
sb->cfg.audio_software_and = software_and;
|
||||
sb->cfg.audio_loop_and = loop_and;
|
||||
sb->cfg.audio_hwmodule_and = hwmodule_and;
|
||||
}
|
||||
static void config_sb_audio_ps2_bnm(ubi_sb_header *sb, off_t flag_bits, int streamed_and, int cd_streamed_and, int loop_and, off_t channels, off_t sample_rate) {
|
||||
/* bit flags, channels and sample rate */
|
||||
sb->cfg.audio_streamed_flag = flag_bits;
|
||||
sb->cfg.audio_cd_streamed_flag = flag_bits;
|
||||
sb->cfg.audio_loop_flag = flag_bits;
|
||||
sb->cfg.audio_streamed_and = streamed_and;
|
||||
sb->cfg.audio_cd_streamed_and = cd_streamed_and;
|
||||
sb->cfg.audio_loop_and = loop_and;
|
||||
sb->cfg.audio_channels = channels;
|
||||
sb->cfg.audio_sample_rate = sample_rate;
|
||||
sb->cfg.audio_streamed_flag = flag_bits;
|
||||
sb->cfg.audio_cd_streamed_flag = flag_bits;
|
||||
sb->cfg.audio_loop_flag = flag_bits;
|
||||
sb->cfg.audio_streamed_and = streamed_and;
|
||||
sb->cfg.audio_cd_streamed_and = cd_streamed_and;
|
||||
sb->cfg.audio_loop_and = loop_and;
|
||||
sb->cfg.audio_channels = channels;
|
||||
sb->cfg.audio_sample_rate = sample_rate;
|
||||
}
|
||||
static void config_sb_audio_ps2_old(ubi_sb_header *sb, off_t flag_bits, int streamed_and, int loop_and, int loc_and, int stereo_and, off_t pitch, off_t sample_rate) {
|
||||
/* bit flags, sample rate only */
|
||||
sb->cfg.audio_streamed_flag = flag_bits;
|
||||
sb->cfg.audio_loop_flag = flag_bits;
|
||||
sb->cfg.audio_loc_flag = flag_bits;
|
||||
sb->cfg.audio_stereo_flag = flag_bits;
|
||||
sb->cfg.audio_streamed_and = streamed_and;
|
||||
sb->cfg.audio_loop_and = loop_and;
|
||||
sb->cfg.audio_loc_and = loc_and;
|
||||
sb->cfg.audio_stereo_and = stereo_and;
|
||||
sb->cfg.audio_pitch = pitch;
|
||||
sb->cfg.audio_sample_rate = sample_rate;
|
||||
sb->cfg.audio_streamed_flag = flag_bits;
|
||||
sb->cfg.audio_loop_flag = flag_bits;
|
||||
sb->cfg.audio_loc_flag = flag_bits;
|
||||
sb->cfg.audio_stereo_flag = flag_bits;
|
||||
sb->cfg.audio_streamed_and = streamed_and;
|
||||
sb->cfg.audio_loop_and = loop_and;
|
||||
sb->cfg.audio_loc_and = loc_and;
|
||||
sb->cfg.audio_stereo_and = stereo_and;
|
||||
sb->cfg.audio_pitch = pitch;
|
||||
sb->cfg.audio_sample_rate = sample_rate;
|
||||
}
|
||||
static void config_sb_sequence(ubi_sb_header* sb, off_t sequence_count, off_t entry_size) {
|
||||
/* sequence header and chain table */
|
||||
|
@ -3335,7 +3360,7 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) {
|
|||
(sb->version == 0x0012000c && sb->platform == UBI_PS2)) {
|
||||
config_sb_entry(sb, 0x48, 0x6c);
|
||||
|
||||
config_sb_audio_fb(sb, 0x18, (1 << 2), (1 << 3), (1 << 4));
|
||||
config_sb_audio_fb_ps2(sb, 0x18, (1 << 2), (1 << 3), (1 << 4), (1 << 5));
|
||||
config_sb_audio_hs(sb, 0x20, 0x24, 0x30, 0x38, 0x40, 0x68); /* num_samples may be null */
|
||||
|
||||
config_sb_sequence(sb, 0x28, 0x10);
|
||||
|
@ -3352,7 +3377,7 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) {
|
|||
if (sb->version == 0x000A0007 && sb->platform == UBI_PS2 && is_bia_ps2) {
|
||||
config_sb_entry(sb, 0x5c, 0x14c);
|
||||
|
||||
config_sb_audio_fb(sb, 0x18, (1 << 2), (1 << 3), (1 << 4));
|
||||
config_sb_audio_fb_ps2(sb, 0x18, (1 << 2), (1 << 3), (1 << 4), (1 << 5));
|
||||
config_sb_audio_hs(sb, 0x20, 0x24, 0x30, 0x38, 0x40, 0x148); /* num_samples may be null */
|
||||
|
||||
config_sb_sequence(sb, 0x28, 0x10);
|
||||
|
@ -3547,7 +3572,7 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) {
|
|||
if (sb->version == 0x00130001 && sb->platform == UBI_PS2) {
|
||||
config_sb_entry(sb, 0x48, 0x4c);
|
||||
|
||||
config_sb_audio_fb(sb, 0x18, (1 << 2), (1 << 3), (1 << 4));
|
||||
config_sb_audio_fb_ps2(sb, 0x18, (1 << 2), (1 << 3), (1 << 4), (1 << 5));
|
||||
config_sb_audio_he(sb, 0x20, 0x24, 0x30, 0x38, 0x40, 0x44);
|
||||
|
||||
config_sb_sequence(sb, 0x28, 0x10);
|
||||
|
@ -3589,7 +3614,7 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) {
|
|||
if (sb->version == 0x00130004 && sb->platform == UBI_PS2) {
|
||||
config_sb_entry(sb, 0x48, 0x50);
|
||||
|
||||
config_sb_audio_fb(sb, 0x18, (1 << 2), (1 << 3), (1 << 4));
|
||||
config_sb_audio_fb_ps2(sb, 0x18, (1 << 2), (1 << 3), (1 << 4), (1 << 5));
|
||||
config_sb_audio_he(sb, 0x20, 0x24, 0x30, 0x38, 0x40, 0x4c);
|
||||
sb->cfg.audio_interleave = 0x8000;
|
||||
|
||||
|
@ -3626,7 +3651,7 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) {
|
|||
if (sb->version == 0x00150000 && sb->platform == UBI_PS2) {
|
||||
config_sb_entry(sb, 0x48, 0x5c);
|
||||
|
||||
config_sb_audio_fb(sb, 0x20, (1 << 2), (1 << 3), (1 << 4));
|
||||
config_sb_audio_fb_ps2(sb, 0x20, (1 << 2), (1 << 3), (1 << 4), (1 << 5));
|
||||
config_sb_audio_he(sb, 0x2c, 0x30, 0x3c, 0x44, 0x4c, 0x50);
|
||||
|
||||
config_sb_sequence(sb, 0x2c, 0x10);
|
||||
|
@ -3683,7 +3708,7 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) {
|
|||
(sb->version == 0x00180007 && sb->platform == UBI_PSP)) {
|
||||
config_sb_entry(sb, 0x48, 0x54);
|
||||
|
||||
config_sb_audio_fb(sb, 0x20, (1 << 2), (1 << 3), (1 << 4));
|
||||
config_sb_audio_fb_ps2(sb, 0x20, (1 << 2), (1 << 3), (1 << 4), (1 << 5));
|
||||
config_sb_audio_he(sb, 0x28, 0x2c, 0x34, 0x3c, 0x44, 0x48);
|
||||
|
||||
config_sb_sequence(sb, 0x2c, 0x10);
|
||||
|
@ -3763,6 +3788,22 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
/* Open Season (2006)(X360)-map */
|
||||
if (sb->version == 0x00180003 && sb->platform == UBI_X360) {
|
||||
config_sb_entry(sb, 0x68, 0x74);
|
||||
|
||||
config_sb_audio_fs(sb, 0x2c, 0x30, 0x34);
|
||||
config_sb_audio_he(sb, 0x5c, 0x54, 0x40, 0x48, 0x64, 0x60);
|
||||
sb->cfg.audio_xma_offset = 0x70;
|
||||
|
||||
config_sb_sequence(sb, 0x2c, 0x14);
|
||||
|
||||
config_sb_layer_he(sb, 0x20, 0x38, 0x3c, 0x44);
|
||||
config_sb_layer_sh(sb, 0x34, 0x00, 0x08, 0x0c, 0x14);
|
||||
|
||||
config_sb_silence_f(sb, 0x1c);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* two configs with same id; use project file as identifier */
|
||||
if (sb->version == 0x00180006 && sb->platform == UBI_PC) {
|
||||
|
@ -3838,7 +3879,7 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) {
|
|||
(sb->version == 0x00190005 && sb->platform == UBI_PSP)) {
|
||||
config_sb_entry(sb, 0x48, 0x58);
|
||||
|
||||
config_sb_audio_fb(sb, 0x20, (1 << 2), (1 << 3), (1 << 4)); /* assumed subblock_flag */
|
||||
config_sb_audio_fb(sb, 0x20, (1 << 2), (1 << 3), (1 << 4)); /* assumed software_flag */
|
||||
config_sb_audio_he(sb, 0x28, 0x2c, 0x34, 0x3c, 0x44, 0x48);
|
||||
|
||||
config_sb_sequence(sb, 0x2c, 0x10);
|
||||
|
@ -3848,11 +3889,29 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
/* TMNT (2007)(PC)-bank 0x00190002 */
|
||||
/* Surf's Up (2007)(PC)-bank 0x00190005 */
|
||||
if ((sb->version == 0x00190002 && sb->platform == UBI_PC) ||
|
||||
(sb->version == 0x00190005 && sb->platform == UBI_PC)) {
|
||||
config_sb_entry(sb, 0x68, 0x74);
|
||||
|
||||
config_sb_audio_fs(sb, 0x28, 0x2c, 0x30);
|
||||
config_sb_audio_he(sb, 0x3c, 0x40, 0x48, 0x50, 0x58, 0x5c);
|
||||
|
||||
config_sb_sequence(sb, 0x2c, 0x14);
|
||||
|
||||
config_sb_layer_he(sb, 0x20, 0x34, 0x38, 0x40);
|
||||
config_sb_layer_sh(sb, 0x30, 0x00, 0x04, 0x08, 0x10);
|
||||
|
||||
config_sb_silence_f(sb, 0x1c);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* TMNT (2007)(PS2)-bank */
|
||||
if (sb->version == 0x00190002 && sb->platform == UBI_PS2) {
|
||||
config_sb_entry(sb, 0x48, 0x5c);
|
||||
|
||||
config_sb_audio_fb(sb, 0x20, (1 << 2), (1 << 3), (1 << 4)); /* assumed subblock_flag */
|
||||
config_sb_audio_fb_ps2(sb, 0x20, (1 << 2), (1 << 3), (1 << 4), (1 << 5)); /* assumed software_flag */
|
||||
config_sb_audio_he(sb, 0x28, 0x2c, 0x34, 0x3c, 0x44, 0x48);
|
||||
|
||||
config_sb_sequence(sb, 0x2c, 0x10);
|
||||
|
@ -3911,24 +3970,6 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
/* TMNT (2007)(PC)-bank 0x00190002 */
|
||||
/* Surf's Up (2007)(PC)-bank 0x00190005 */
|
||||
if ((sb->version == 0x00190002 && sb->platform == UBI_PC) ||
|
||||
(sb->version == 0x00190005 && sb->platform == UBI_PC)) {
|
||||
config_sb_entry(sb, 0x68, 0x74);
|
||||
|
||||
config_sb_audio_fs(sb, 0x28, 0x2c, 0x30);
|
||||
config_sb_audio_he(sb, 0x3c, 0x40, 0x48, 0x50, 0x58, 0x5c);
|
||||
|
||||
config_sb_sequence(sb, 0x2c, 0x14);
|
||||
|
||||
config_sb_layer_he(sb, 0x20, 0x34, 0x38, 0x40);
|
||||
config_sb_layer_sh(sb, 0x30, 0x00, 0x04, 0x08, 0x10);
|
||||
|
||||
config_sb_silence_f(sb, 0x1c);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Tom Clancy's Ghost Recon Advanced Warfighter 2 (2007)(PS3)-bank */
|
||||
if (sb->version == 0x001A0003 && sb->platform == UBI_PS3) {
|
||||
config_sb_entry(sb, 0x6c, 0x78);
|
||||
|
@ -3975,7 +4016,7 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) {
|
|||
if (sb->version == 0x001D0000 && sb->platform == UBI_PSP) {
|
||||
config_sb_entry(sb, 0x40, 0x60);
|
||||
|
||||
config_sb_audio_fb(sb, 0x20, (1 << 2), (1 << 3), (1 << 5)); /* assumed subblock_flag */
|
||||
config_sb_audio_fb(sb, 0x20, (1 << 2), (1 << 3), (1 << 5)); /* assumed software_flag */
|
||||
config_sb_audio_he(sb, 0x28, 0x30, 0x38, 0x40, 0x48, 0x4c);
|
||||
return 1;
|
||||
}
|
||||
|
|
|
@ -603,9 +603,9 @@ VGMSTREAM* init_vgmstream_wwise(STREAMFILE* sf) {
|
|||
if (ww.fmt_size != 0x18) goto fail;
|
||||
if (ww.big_endian) goto fail;
|
||||
|
||||
/* extra_data (size 0x06)
|
||||
/* extra data (size 0x06)
|
||||
* 0x00: samples per block (0x1c)
|
||||
* 0x04: channel config (again?) */
|
||||
* 0x02: channel config (again?) */
|
||||
|
||||
vgmstream->coding_type = coding_HEVAG;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
|
@ -621,9 +621,19 @@ VGMSTREAM* init_vgmstream_wwise(STREAMFILE* sf) {
|
|||
if (ww.fmt_size != 0x24) goto fail;
|
||||
if (ww.extra_size != 0x12) goto fail;
|
||||
|
||||
/* extra data
|
||||
* 0x00: samples per subframe?
|
||||
* 0x02: channel config (again?)
|
||||
* 0x06: config
|
||||
* 0x0a: samples
|
||||
* 0x0e: encoder delay? (same as samples per subframe?)
|
||||
* 0x10: decoder delay? (PS4 only, 0 on Vita?) */
|
||||
|
||||
cfg.channels = ww.channels;
|
||||
cfg.config_data = read_u32be(ww.fmt_offset + 0x18,sf);
|
||||
cfg.encoder_delay = read_u32(ww.fmt_offset + 0x20,sf);
|
||||
cfg.encoder_delay = read_u16(ww.fmt_offset + 0x20,sf);
|
||||
/* PS4 value at 0x22 looks like encoder delay, but using it removes too many
|
||||
* samples [DmC: Definitive Edition (PS4)] */
|
||||
|
||||
vgmstream->codec_data = init_atrac9(&cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
|
|
|
@ -433,8 +433,8 @@ STREAMFILE* open_wrap_streamfile(STREAMFILE *streamfile) {
|
|||
|
||||
return &this_sf->sf;
|
||||
}
|
||||
STREAMFILE* open_wrap_streamfile_f(STREAMFILE *streamfile) {
|
||||
STREAMFILE *new_sf = open_wrap_streamfile(streamfile);
|
||||
STREAMFILE* open_wrap_streamfile_f(STREAMFILE* streamfile) {
|
||||
STREAMFILE* new_sf = open_wrap_streamfile(streamfile);
|
||||
if (!new_sf)
|
||||
close_streamfile(streamfile);
|
||||
return new_sf;
|
||||
|
@ -445,28 +445,36 @@ STREAMFILE* open_wrap_streamfile_f(STREAMFILE *streamfile) {
|
|||
typedef struct {
|
||||
STREAMFILE sf;
|
||||
|
||||
STREAMFILE *inner_sf;
|
||||
STREAMFILE* inner_sf;
|
||||
off_t start;
|
||||
size_t size;
|
||||
} CLAMP_STREAMFILE;
|
||||
|
||||
static size_t clamp_read(CLAMP_STREAMFILE *streamfile, uint8_t *dst, off_t offset, size_t length) {
|
||||
static size_t clamp_read(CLAMP_STREAMFILE* streamfile, uint8_t* dst, off_t offset, size_t length) {
|
||||
off_t inner_offset = streamfile->start + offset;
|
||||
size_t clamp_length = length > (streamfile->size - offset) ? (streamfile->size - offset) : length;
|
||||
size_t clamp_length = length;
|
||||
|
||||
if (offset + length > streamfile->size) {
|
||||
if (offset >= streamfile->size)
|
||||
clamp_length = 0;
|
||||
else
|
||||
clamp_length = streamfile->size - offset;
|
||||
}
|
||||
|
||||
return streamfile->inner_sf->read(streamfile->inner_sf, dst, inner_offset, clamp_length);
|
||||
}
|
||||
static size_t clamp_get_size(CLAMP_STREAMFILE *streamfile) {
|
||||
static size_t clamp_get_size(CLAMP_STREAMFILE* streamfile) {
|
||||
return streamfile->size;
|
||||
}
|
||||
static off_t clamp_get_offset(CLAMP_STREAMFILE *streamfile) {
|
||||
static off_t clamp_get_offset(CLAMP_STREAMFILE* streamfile) {
|
||||
return streamfile->inner_sf->get_offset(streamfile->inner_sf) - streamfile->start;
|
||||
}
|
||||
static void clamp_get_name(CLAMP_STREAMFILE *streamfile, char *buffer, size_t length) {
|
||||
static void clamp_get_name(CLAMP_STREAMFILE* streamfile, char* buffer, size_t length) {
|
||||
streamfile->inner_sf->get_name(streamfile->inner_sf, buffer, length); /* default */
|
||||
}
|
||||
static STREAMFILE *clamp_open(CLAMP_STREAMFILE *streamfile, const char * const filename, size_t buffersize) {
|
||||
static STREAMFILE* clamp_open(CLAMP_STREAMFILE* streamfile, const char* const filename, size_t buffersize) {
|
||||
char original_filename[PATH_LIMIT];
|
||||
STREAMFILE *new_inner_sf = NULL;
|
||||
STREAMFILE* new_inner_sf = NULL;
|
||||
|
||||
new_inner_sf = streamfile->inner_sf->open(streamfile->inner_sf,filename,buffersize);
|
||||
streamfile->inner_sf->get_name(streamfile->inner_sf, original_filename, PATH_LIMIT);
|
||||
|
@ -478,13 +486,13 @@ static STREAMFILE *clamp_open(CLAMP_STREAMFILE *streamfile, const char * const f
|
|||
return new_inner_sf;
|
||||
}
|
||||
}
|
||||
static void clamp_close(CLAMP_STREAMFILE *streamfile) {
|
||||
static void clamp_close(CLAMP_STREAMFILE* streamfile) {
|
||||
streamfile->inner_sf->close(streamfile->inner_sf);
|
||||
free(streamfile);
|
||||
}
|
||||
|
||||
STREAMFILE* open_clamp_streamfile(STREAMFILE *streamfile, off_t start, size_t size) {
|
||||
CLAMP_STREAMFILE *this_sf = NULL;
|
||||
STREAMFILE* open_clamp_streamfile(STREAMFILE* streamfile, off_t start, size_t size) {
|
||||
CLAMP_STREAMFILE* this_sf = NULL;
|
||||
|
||||
if (!streamfile || size == 0) return NULL;
|
||||
if (start + size > get_streamfile_size(streamfile)) return NULL;
|
||||
|
@ -507,8 +515,8 @@ STREAMFILE* open_clamp_streamfile(STREAMFILE *streamfile, off_t start, size_t si
|
|||
|
||||
return &this_sf->sf;
|
||||
}
|
||||
STREAMFILE* open_clamp_streamfile_f(STREAMFILE *streamfile, off_t start, size_t size) {
|
||||
STREAMFILE *new_sf = open_clamp_streamfile(streamfile, start, size);
|
||||
STREAMFILE* open_clamp_streamfile_f(STREAMFILE* streamfile, off_t start, size_t size) {
|
||||
STREAMFILE* new_sf = open_clamp_streamfile(streamfile, start, size);
|
||||
if (!new_sf)
|
||||
close_streamfile(streamfile);
|
||||
return new_sf;
|
||||
|
@ -899,8 +907,12 @@ STREAMFILE* open_streamfile_by_filename(STREAMFILE* sf, const char* filename) {
|
|||
/* check for non-normalized paths first (ex. txth) */
|
||||
path = strrchr(fullname, '/');
|
||||
otherpath = strrchr(fullname, '\\');
|
||||
if (otherpath > path)
|
||||
if (otherpath > path) { //todo cast to ptr?
|
||||
/* foobar makes paths like "(fake protocol)://(windows path with \)".
|
||||
* Hack to work around both separators, though probably foo_streamfile
|
||||
* should just return and handle normalized paths without protocol. */
|
||||
path = otherpath;
|
||||
}
|
||||
|
||||
if (path) {
|
||||
path[1] = '\0'; /* remove name after separator */
|
||||
|
|
|
@ -1648,9 +1648,12 @@ int vgmstream_open_stream_bf(VGMSTREAM* vgmstream, STREAMFILE* sf, off_t start_o
|
|||
offset = start_offset;
|
||||
} else if (is_stereo_codec) {
|
||||
int ch_mod = (ch & 1) ? ch - 1 : ch; /* adjust odd channels (ch 0,1,2,3,4,5 > ch 0,0,2,2,4,4) */
|
||||
offset = start_offset + vgmstream->interleave_block_size*ch_mod;
|
||||
offset = start_offset + vgmstream->interleave_block_size * ch_mod;
|
||||
} else if (vgmstream->interleave_first_block_size) {
|
||||
/* start_offset assumes + vgmstream->interleave_first_block_size, maybe should do it here */
|
||||
offset = start_offset + (vgmstream->interleave_first_block_size + vgmstream->interleave_first_skip) * ch;
|
||||
} else {
|
||||
offset = start_offset + vgmstream->interleave_block_size*ch;
|
||||
offset = start_offset + vgmstream->interleave_block_size * ch;
|
||||
}
|
||||
|
||||
/* open new one if needed, useful to avoid jumping around when each channel data is too apart
|
||||
|
|
Loading…
Reference in New Issue