mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-01-19 00:04:04 +01:00
Fix hang on full loops with xma1 formats
This commit is contained in:
parent
41bad49872
commit
501af6ad8c
@ -623,7 +623,7 @@ ffmpeg_codec_data* init_ffmpeg_aac(STREAMFILE* sf, off_t offset, size_t size, in
|
||||
ffmpeg_codec_data* init_ffmpeg_xwma(STREAMFILE* sf, uint32_t data_offset, uint32_t data_size, int format, int channels, int sample_rate, int avg_bitrate, int block_size);
|
||||
|
||||
ffmpeg_codec_data* init_ffmpeg_xma1_raw(STREAMFILE* sf, uint32_t data_offset, uint32_t data_size, int channels, int sample_rate, int stream_mode);
|
||||
|
||||
ffmpeg_codec_data* init_ffmpeg_xma_chunk(STREAMFILE* sf, uint32_t data_offset, uint32_t data_size, uint32_t chunk_offset, uint32_t chunk_size);
|
||||
|
||||
/* ffmpeg_decoder_custom_opus.c (helper-things) */
|
||||
typedef struct {
|
||||
@ -681,11 +681,9 @@ ffmpeg_codec_data* init_ffmpeg_mp4_custom_lyn(STREAMFILE* sf, mp4_custom_t* mp4)
|
||||
|
||||
|
||||
/* coding_utils */
|
||||
int ffmpeg_fmt_chunk_swap_endian(uint8_t* chunk, size_t chunk_size, uint16_t codec);
|
||||
int ffmpeg_make_riff_atrac3plus(uint8_t* buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int block_align, int encoder_delay);
|
||||
int ffmpeg_make_riff_xma2(uint8_t* buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int block_count, int block_size);
|
||||
int ffmpeg_make_riff_xma_from_fmt_chunk(uint8_t* buf, size_t buf_size, off_t fmt_offset, size_t fmt_size, size_t data_size, STREAMFILE* sf, int big_endian);
|
||||
int ffmpeg_make_riff_xma2_from_xma2_chunk(uint8_t* buf, size_t buf_size, off_t xma2_offset, size_t xma2_size, size_t data_size, STREAMFILE* sf);
|
||||
int ffmpeg_make_riff_xwma(uint8_t* buf, size_t buf_size, int codec, size_t data_size, int channels, int sample_rate, int avg_bps, int block_align);
|
||||
|
||||
/* MS audio format's sample info (struct to avoid passing so much stuff, separate for reusing) */
|
||||
|
@ -139,6 +139,8 @@ int ffmpeg_make_riff_xma2(uint8_t* buf, size_t buf_size, size_t sample_count, si
|
||||
return riff_size;
|
||||
}
|
||||
|
||||
static int ffmpeg_fmt_chunk_swap_endian(uint8_t* chunk, size_t chunk_size, uint16_t codec);
|
||||
|
||||
/* Makes a XMA1/2 RIFF header for FFmpeg using a "fmt " chunk (XMAWAVEFORMAT or XMA2WAVEFORMATEX) as a base:
|
||||
* Useful to preserve the stream layout */
|
||||
int ffmpeg_make_riff_xma_from_fmt_chunk(uint8_t* buf, size_t buf_size, off_t fmt_offset, size_t fmt_size, size_t data_size, STREAMFILE* sf, int big_endian) {
|
||||
@ -172,35 +174,6 @@ fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Makes a XMA2 RIFF header for FFmpeg using a "XMA2" chunk (XMA2WAVEFORMAT) as a base.
|
||||
* Useful to preserve the stream layout */
|
||||
int ffmpeg_make_riff_xma2_from_xma2_chunk(uint8_t* buf, size_t buf_size, off_t xma2_offset, size_t xma2_size, size_t data_size, STREAMFILE* sf) {
|
||||
uint8_t chunk[0x100];
|
||||
size_t riff_size;
|
||||
|
||||
riff_size = 4+4+ 4 + 4+4+xma2_size + 4+4;
|
||||
if (buf_size < riff_size || xma2_size > 0x100)
|
||||
goto fail;
|
||||
if (read_streamfile(chunk,xma2_offset,xma2_size, sf) != xma2_size)
|
||||
goto fail;
|
||||
|
||||
|
||||
memcpy(buf+0x00, "RIFF", 4);
|
||||
put_32bitLE(buf+0x04, (int32_t)(riff_size-4-4 + data_size)); /* riff size */
|
||||
memcpy(buf+0x08, "WAVE", 4);
|
||||
|
||||
memcpy(buf+0x0c, "XMA2", 4);
|
||||
put_32bitLE(buf+0x10, xma2_size);
|
||||
memcpy(buf+0x14, chunk, xma2_size);
|
||||
|
||||
memcpy(buf+0x14+xma2_size, "data", 4);
|
||||
put_32bitLE(buf+0x14+xma2_size+4, data_size); /* data size */
|
||||
|
||||
return riff_size;
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ffmpeg_make_riff_xwma(uint8_t* buf, size_t buf_size, int codec, size_t data_size, int channels, int sample_rate, int avg_bps, int block_align) {
|
||||
size_t riff_size = 4+4+ 4 + 0x1a + 4+4;
|
||||
@ -259,7 +232,7 @@ int ffmpeg_make_riff_xwma(uint8_t* buf, size_t buf_size, int codec, size_t data_
|
||||
}
|
||||
|
||||
|
||||
int ffmpeg_fmt_chunk_swap_endian(uint8_t* chunk, size_t chunk_size, uint16_t codec) {
|
||||
static int ffmpeg_fmt_chunk_swap_endian(uint8_t* chunk, size_t chunk_size, uint16_t codec) {
|
||||
int i;
|
||||
/* swap from LE to BE or the other way around, doesn't matter */
|
||||
switch(codec) {
|
||||
|
@ -3,46 +3,45 @@
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
|
||||
static int ffmpeg_make_riff_atrac3(uint8_t* buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int block_align, int joint_stereo, int encoder_delay) {
|
||||
uint16_t codec_ATRAC3 = 0x0270;
|
||||
size_t riff_size = 4+4+ 4 + 0x28 + 0x10 + 4+4;
|
||||
int buf_max = (0x04 + 0x04) + 0x4 + 0x28 + 0x10 + (0x04 + 0x04);
|
||||
|
||||
if (buf_size < riff_size)
|
||||
if (buf_max > buf_size)
|
||||
return -1;
|
||||
|
||||
memcpy(buf+0x00, "RIFF", 4);
|
||||
put_32bitLE(buf+0x04, (int32_t)(riff_size-4-4 + data_size)); /* riff size */
|
||||
memcpy(buf+0x08, "WAVE", 4);
|
||||
memcpy (buf+0x00, "RIFF", 0x04);
|
||||
put_u32le(buf+0x04, (uint32_t)(buf_max - 0x04 - 0x04 + data_size)); /* riff size */
|
||||
memcpy (buf+0x08, "WAVE", 0x04);
|
||||
|
||||
memcpy(buf+0x0c, "fmt ", 4);
|
||||
put_32bitLE(buf+0x10, 0x20);/*fmt size*/
|
||||
put_16bitLE(buf+0x14, codec_ATRAC3);
|
||||
put_16bitLE(buf+0x16, channels);
|
||||
put_32bitLE(buf+0x18, sample_rate);
|
||||
put_32bitLE(buf+0x1c, sample_rate*channels / sizeof(sample)); /* average bytes per second (wrong) */
|
||||
put_32bitLE(buf+0x20, (int16_t)(block_align)); /* block align */
|
||||
memcpy (buf+0x0c, "fmt ", 0x04);
|
||||
put_u32le(buf+0x10, 0x20);/*fmt size*/
|
||||
put_u16le(buf+0x14, 0x0270); /* ATRAC3 codec */
|
||||
put_u16le(buf+0x16, channels);
|
||||
put_u32le(buf+0x18, sample_rate);
|
||||
put_u32le(buf+0x1c, sample_rate * channels / sizeof(sample)); /* average bytes per second (wrong) */
|
||||
put_u16le(buf+0x20, (uint16_t)(block_align)); /* block align */
|
||||
|
||||
put_16bitLE(buf+0x24, 0x0e); /* extra data size */
|
||||
put_16bitLE(buf+0x26, 1); /* unknown, always 1 */
|
||||
put_16bitLE(buf+0x28, 0x0800 * channels); /* unknown (some size? 0x1000=2ch, 0x0800=1ch) */
|
||||
put_16bitLE(buf+0x2a, 0); /* unknown, always 0 */
|
||||
put_16bitLE(buf+0x2c, joint_stereo ? 0x0001 : 0x0000);
|
||||
put_16bitLE(buf+0x2e, joint_stereo ? 0x0001 : 0x0000); /* repeated? */
|
||||
put_16bitLE(buf+0x30, 1); /* unknown, always 1 (frame_factor?) */
|
||||
put_16bitLE(buf+0x32, 0); /* unknown, always 0 */
|
||||
put_u16le(buf+0x24, 0x0e); /* extra data size */
|
||||
put_u16le(buf+0x26, 1); /* unknown, always 1 */
|
||||
put_u16le(buf+0x28, 0x0800 * channels); /* unknown (some size? 0x1000=2ch, 0x0800=1ch) */
|
||||
put_u16le(buf+0x2a, 0); /* unknown, always 0 */
|
||||
put_u16le(buf+0x2c, joint_stereo ? 0x0001 : 0x0000);
|
||||
put_u16le(buf+0x2e, joint_stereo ? 0x0001 : 0x0000); /* repeated? */
|
||||
put_u16le(buf+0x30, 1); /* unknown, always 1 (frame_factor?) */
|
||||
put_u16le(buf+0x32, 0); /* unknown, always 0 */
|
||||
|
||||
memcpy(buf+0x34, "fact", 4);
|
||||
put_32bitLE(buf+0x38, 0x8); /* fact size */
|
||||
put_32bitLE(buf+0x3c, sample_count);
|
||||
put_32bitLE(buf+0x40, encoder_delay);
|
||||
memcpy (buf+0x34, "fact", 4);
|
||||
put_u32le(buf+0x38, 0x8); /* fact size */
|
||||
put_u32le(buf+0x3c, sample_count);
|
||||
put_u32le(buf+0x40, encoder_delay);
|
||||
|
||||
memcpy(buf+0x44, "data", 4);
|
||||
put_32bitLE(buf+0x48, data_size); /* data size */
|
||||
memcpy (buf+0x44, "data", 4);
|
||||
put_u32le(buf+0x48, data_size); /* data size */
|
||||
|
||||
return riff_size;
|
||||
return buf_max;
|
||||
}
|
||||
|
||||
ffmpeg_codec_data* init_ffmpeg_atrac3_raw(STREAMFILE* sf, off_t offset, size_t data_size, int sample_count, int channels, int sample_rate, int block_align, int encoder_delay) {
|
||||
ffmpeg_codec_data *ffmpeg_data = NULL;
|
||||
ffmpeg_codec_data* data = NULL;
|
||||
uint8_t buf[0x100];
|
||||
int bytes;
|
||||
int joint_stereo = (block_align == 0x60*channels) && channels > 1; /* only lowest block size does joint stereo */
|
||||
@ -50,8 +49,8 @@ ffmpeg_codec_data* init_ffmpeg_atrac3_raw(STREAMFILE* sf, off_t offset, size_t d
|
||||
|
||||
/* create fake header + init ffmpeg + apply fixes to FFmpeg decoding */
|
||||
bytes = ffmpeg_make_riff_atrac3(buf,sizeof(buf), sample_count, data_size, channels, sample_rate, block_align, joint_stereo, encoder_delay);
|
||||
ffmpeg_data = init_ffmpeg_header_offset(sf, buf,bytes, offset,data_size);
|
||||
if (!ffmpeg_data) goto fail;
|
||||
data = init_ffmpeg_header_offset(sf, buf,bytes, offset,data_size);
|
||||
if (!data) goto fail;
|
||||
|
||||
/* unlike with RIFF ATRAC3 we don't set implicit delay, as raw ATRAC3 headers often give loop/samples
|
||||
* in offsets, so calcs are expected to be handled externally (presumably the game would call raw decoding API
|
||||
@ -59,23 +58,23 @@ ffmpeg_codec_data* init_ffmpeg_atrac3_raw(STREAMFILE* sf, off_t offset, size_t d
|
||||
|
||||
/* encoder delay: encoder introduces some garbage (not always silent) samples to skip at the beginning (at least 1 frame)
|
||||
* FFmpeg doesn't set this, and even if it ever does it's probably better to force it for the implicit skip. */
|
||||
ffmpeg_set_skip_samples(ffmpeg_data, encoder_delay);
|
||||
ffmpeg_set_skip_samples(data, encoder_delay);
|
||||
//ffmpeg_set_samples(sample_count); /* useful? */
|
||||
|
||||
/* invert ATRAC3: waveform is inverted vs official tools (not noticeable but for accuracy) */
|
||||
if (is_at3) {
|
||||
ffmpeg_set_invert_floats(ffmpeg_data);
|
||||
ffmpeg_set_invert_floats(data);
|
||||
}
|
||||
|
||||
return ffmpeg_data;
|
||||
return data;
|
||||
fail:
|
||||
free_ffmpeg(ffmpeg_data);
|
||||
free_ffmpeg(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* init ATRAC3/plus while adding some fixes */
|
||||
ffmpeg_codec_data* init_ffmpeg_atrac3_riff(STREAMFILE* sf, off_t offset, int* p_samples) {
|
||||
ffmpeg_codec_data *ffmpeg_data = NULL;
|
||||
ffmpeg_codec_data* data = NULL;
|
||||
int is_at3 = 0, is_at3p = 0, codec;
|
||||
size_t riff_size;
|
||||
int fact_samples, skip_samples, implicit_skip;
|
||||
@ -84,11 +83,11 @@ ffmpeg_codec_data* init_ffmpeg_atrac3_riff(STREAMFILE* sf, off_t offset, int* p_
|
||||
|
||||
|
||||
/* some simplified checks just in case */
|
||||
if (read_32bitBE(offset + 0x00,sf) != 0x52494646) /* "RIFF" */
|
||||
if (!is_id32be(offset + 0x00,sf, "RIFF"))
|
||||
goto fail;
|
||||
|
||||
riff_size = read_32bitLE(offset + 0x04,sf) + 0x08;
|
||||
codec = (uint16_t)read_16bitLE(offset + 0x14, sf);
|
||||
riff_size = read_u32le(offset + 0x04,sf) + 0x08;
|
||||
codec = read_u16le(offset + 0x14, sf);
|
||||
switch(codec) {
|
||||
case 0x0270: is_at3 = 1; break;
|
||||
case 0xFFFE: is_at3p = 1; break;
|
||||
@ -98,20 +97,20 @@ ffmpeg_codec_data* init_ffmpeg_atrac3_riff(STREAMFILE* sf, off_t offset, int* p_
|
||||
|
||||
/* init ffmpeg + apply fixes to FFmpeg decoding (with these fixes should be
|
||||
* sample-accurate vs official tools, except usual +-1 float-to-pcm conversion) */
|
||||
ffmpeg_data = init_ffmpeg_offset(sf, offset, riff_size);
|
||||
if (!ffmpeg_data) goto fail;
|
||||
data = init_ffmpeg_offset(sf, offset, riff_size);
|
||||
if (!data) goto fail;
|
||||
|
||||
|
||||
/* well behaved .at3 define "fact" but official tools accept files without it */
|
||||
if (find_chunk_le(sf,0x66616374,offset + 0x0c,0, &fact_offset, &fact_size)) { /* "fact" */
|
||||
if (find_chunk_le(sf, get_id32be("fact"), offset + 0x0c,0, &fact_offset, &fact_size)) {
|
||||
if (fact_size == 0x08) { /* early AT3 (mainly PSP games) */
|
||||
fact_samples = read_32bitLE(fact_offset + 0x00, sf);
|
||||
skip_samples = read_32bitLE(fact_offset + 0x04, sf); /* base skip samples */
|
||||
fact_samples = read_s32le(fact_offset + 0x00, sf);
|
||||
skip_samples = read_s32le(fact_offset + 0x04, sf); /* base skip samples */
|
||||
}
|
||||
else if (fact_size == 0x0c) { /* late AT3 (mainly PS3 games and few PSP games) */
|
||||
fact_samples = read_32bitLE(fact_offset + 0x00, sf);
|
||||
fact_samples = read_s32le(fact_offset + 0x00, sf);
|
||||
/* 0x04: base skip samples, ignored by decoder */
|
||||
skip_samples = read_32bitLE(fact_offset + 0x08, sf); /* skip samples with implicit skip of 184 added */
|
||||
skip_samples = read_s32le(fact_offset + 0x08, sf); /* skip samples with implicit skip of 184 added */
|
||||
}
|
||||
else {
|
||||
VGM_LOG("ATRAC3: unknown fact size\n");
|
||||
@ -151,35 +150,35 @@ ffmpeg_codec_data* init_ffmpeg_atrac3_riff(STREAMFILE* sf, off_t offset, int* p_
|
||||
|
||||
/* encoder delay: encoder introduces some garbage (not always silent) samples to skip at the beginning (at least 1 frame)
|
||||
* FFmpeg doesn't set this, and even if it ever does it's probably better to force it for the implicit skip. */
|
||||
ffmpeg_set_skip_samples(ffmpeg_data, skip_samples + implicit_skip);
|
||||
ffmpeg_set_skip_samples(data, skip_samples + implicit_skip);
|
||||
//ffmpeg_set_samples(sample_count); /* useful? */
|
||||
|
||||
/* invert ATRAC3: waveform is inverted vs official tools (not noticeable but for accuracy) */
|
||||
if (is_at3) {
|
||||
ffmpeg_set_invert_floats(ffmpeg_data);
|
||||
ffmpeg_set_invert_floats(data);
|
||||
}
|
||||
|
||||
/* multichannel fix: LFE channel should be reordered on decode (ATRAC3Plus only, only 1/2/6/8ch exist):
|
||||
* - 6ch: FL FR FC BL BR LFE > FL FR FC LFE BL BR
|
||||
* - 8ch: FL FR FC BL BR SL SR LFE > FL FR FC LFE BL BR SL SR */
|
||||
if (is_at3p && ffmpeg_get_channels(ffmpeg_data) == 6) {
|
||||
if (is_at3p && ffmpeg_get_channels(data) == 6) {
|
||||
/* LFE BR BL > LFE BL BR > same */
|
||||
int channel_remap[] = { 0, 1, 2, 5, 5, 5, };
|
||||
ffmpeg_set_channel_remapping(ffmpeg_data, channel_remap);
|
||||
ffmpeg_set_channel_remapping(data, channel_remap);
|
||||
}
|
||||
else if (is_at3p && ffmpeg_get_channels(ffmpeg_data) == 8) {
|
||||
else if (is_at3p && ffmpeg_get_channels(data) == 8) {
|
||||
/* LFE BR SL SR BL > LFE BL SL SR BR > LFE BL BR SR SL > LFE BL BR SL SR > same */
|
||||
int channel_remap[] = { 0, 1, 2, 7, 7, 7, 7, 7};
|
||||
ffmpeg_set_channel_remapping(ffmpeg_data, channel_remap);
|
||||
ffmpeg_set_channel_remapping(data, channel_remap);
|
||||
}
|
||||
|
||||
|
||||
if (p_samples)
|
||||
*p_samples = fact_samples;
|
||||
|
||||
return ffmpeg_data;
|
||||
return data;
|
||||
fail:
|
||||
free_ffmpeg(ffmpeg_data);
|
||||
free_ffmpeg(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -241,19 +240,19 @@ static int ffmpeg_make_riff_xma1(uint8_t* buf, size_t buf_size, size_t data_size
|
||||
if (buf_size < riff_size)
|
||||
return -1;
|
||||
|
||||
memcpy(buf+0x00, "RIFF", 4);
|
||||
put_32bitLE(buf+0x04, (int32_t)(riff_size-4-4 + data_size)); /* riff size */
|
||||
memcpy(buf+0x08, "WAVE", 4);
|
||||
memcpy (buf+0x00, "RIFF", 0x04);
|
||||
put_u32le(buf+0x04, (int32_t)(riff_size-4-4 + data_size)); /* riff size */
|
||||
memcpy (buf+0x08, "WAVE", 0x04);
|
||||
|
||||
memcpy(buf+0x0c, "fmt ", 4);
|
||||
put_32bitLE(buf+0x10, 0xc + 0x14*streams);/*fmt size*/
|
||||
put_16bitLE(buf+0x14, codec_XMA1);
|
||||
put_16bitLE(buf+0x16, 16); /* bits per sample */
|
||||
put_16bitLE(buf+0x18, 0x10D6); /* encoder options */
|
||||
put_16bitLE(buf+0x1a, 0); /* largest stream skip (wrong, unneeded) */
|
||||
put_16bitLE(buf+0x1c, streams); /* number of streams */
|
||||
put_8bit (buf+0x1e, 0); /* loop count */
|
||||
put_8bit (buf+0x1f, 2); /* version */
|
||||
memcpy (buf+0x0c, "fmt ", 0x04);
|
||||
put_u32le(buf+0x10, 0xc + 0x14*streams);/*fmt size*/
|
||||
put_u16le(buf+0x14, codec_XMA1);
|
||||
put_u16le(buf+0x16, 16); /* bits per sample */
|
||||
put_u16le(buf+0x18, 0x10D6); /* encoder options */
|
||||
put_u16le(buf+0x1a, 0); /* largest stream skip (wrong, unneeded) */
|
||||
put_u16le(buf+0x1c, streams); /* number of streams */
|
||||
put_u8 (buf+0x1e, 0); /* loop count */
|
||||
put_u8 (buf+0x1f, 2); /* version */
|
||||
|
||||
for (i = 0; i < streams; i++) {
|
||||
int stream_channels;
|
||||
@ -287,20 +286,20 @@ static int ffmpeg_make_riff_xma1(uint8_t* buf, size_t buf_size, size_t data_size
|
||||
}
|
||||
}
|
||||
|
||||
put_32bitLE(buf+off+0x00, sample_rate*stream_channels / sizeof(sample)); /* average bytes per second (wrong, unneeded) */
|
||||
put_32bitLE(buf+off+0x04, sample_rate);
|
||||
put_32bitLE(buf+off+0x08, 0); /* loop start */
|
||||
put_32bitLE(buf+off+0x0c, 0); /* loop end */
|
||||
put_8bit (buf+off+0x10, 0); /* loop subframe */
|
||||
put_8bit (buf+off+0x11, stream_channels);
|
||||
put_16bitLE(buf+off+0x12, speakers);
|
||||
put_u32le(buf+off+0x00, sample_rate*stream_channels / sizeof(sample)); /* average bytes per second (wrong, unneeded) */
|
||||
put_u32le(buf+off+0x04, sample_rate);
|
||||
put_u32le(buf+off+0x08, 0); /* loop start */
|
||||
put_u32le(buf+off+0x0c, 0); /* loop end */
|
||||
put_u8 (buf+off+0x10, 0); /* loop subframe */
|
||||
put_u8 (buf+off+0x11, stream_channels);
|
||||
put_u16le(buf+off+0x12, speakers);
|
||||
}
|
||||
|
||||
/* xmaencode decoding rejects XMA1 without "seek" chunk, though it doesn't seem to use it
|
||||
* (needs to be have entries but can be bogus, also generates seek for even small sounds) */
|
||||
|
||||
memcpy(buf+riff_size-4-4, "data", 4);
|
||||
put_32bitLE(buf+riff_size-4, data_size); /* data size */
|
||||
memcpy (buf + riff_size - 0x04 - 0x04, "data", 0x04);
|
||||
put_u32le(buf + riff_size - 0x04, data_size); /* data size */
|
||||
|
||||
return riff_size;
|
||||
}
|
||||
@ -312,6 +311,7 @@ ffmpeg_codec_data* init_ffmpeg_xma1_raw(STREAMFILE* sf, uint32_t data_offset, ui
|
||||
|
||||
bytes = ffmpeg_make_riff_xma1(buf, sizeof(buf), data_size, channels, sample_rate, stream_mode);
|
||||
data = init_ffmpeg_header_offset(sf, buf, bytes, data_offset, data_size);
|
||||
if (!data) goto fail;
|
||||
|
||||
/* n5.1.2 XMA1 hangs on seeks near end (infinite loop), presumably due to missing flush in wmapro.c's ff_xma1_decoder + frame skip samples */
|
||||
ffmpeg_set_force_seek(data);
|
||||
@ -322,4 +322,115 @@ fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* swap from LE to BE or the other way around */
|
||||
static int ffmpeg_fmt_chunk_swap_endian(uint8_t* chunk, uint32_t chunk_size, uint16_t codec) {
|
||||
int i;
|
||||
|
||||
switch(codec) {
|
||||
case 0x6501:
|
||||
case 0x0165: /* XMA1 */
|
||||
put_u16le(chunk + 0x00, get_u16be(chunk + 0x00)); /*FormatTag*/
|
||||
put_u16le(chunk + 0x02, get_u16be(chunk + 0x02)); /*BitsPerSample*/
|
||||
put_u16le(chunk + 0x04, get_u16be(chunk + 0x04)); /*EncodeOptions*/
|
||||
put_u16le(chunk + 0x06, get_u16be(chunk + 0x06)); /*LargestSkip*/
|
||||
put_u16le(chunk + 0x08, get_u16be(chunk + 0x08)); /*NumStreams*/
|
||||
// put_u8(chunk + 0x0a, get_u8(chunk + 0x0a)); /*LoopCount*/
|
||||
// put_u8(chunk + 0x0b, get_u8(chunk + 0x0b)); /*Version*/
|
||||
for (i = 0xc; i < chunk_size; i += 0x14) { /* reverse endianness for each stream */
|
||||
put_u32le(chunk + i + 0x00, get_u32be(chunk + i + 0x00)); /*PsuedoBytesPerSec*/
|
||||
put_u32le(chunk + i + 0x04, get_u32be(chunk + i + 0x04)); /*SampleRate*/
|
||||
put_u32le(chunk + i + 0x08, get_u32be(chunk + i + 0x08)); /*LoopStart*/
|
||||
put_u32le(chunk + i + 0x0c, get_u32be(chunk + i + 0x0c)); /*LoopEnd*/
|
||||
// put_u8(chunk + i + 0x10, get_u8(chunk + i + 0x10)); /*SubframeData*/
|
||||
// put_u8(chunk + i + 0x11, get_u8(chunk + i + 0x11)); /*Channels*/
|
||||
put_u16le(chunk + i + 0x12, get_u16be(chunk + i + 0x12)); /*ChannelMask*/
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x6601:
|
||||
case 0x0166: /* XMA2 */
|
||||
put_u16le(chunk + 0x00, get_u16be(chunk + 0x00)); /*wFormatTag*/
|
||||
put_u16le(chunk + 0x02, get_u16be(chunk + 0x02)); /*nChannels*/
|
||||
put_u32le(chunk + 0x04, get_u32be(chunk + 0x04)); /*nSamplesPerSec*/
|
||||
put_u32le(chunk + 0x08, get_u32be(chunk + 0x08)); /*nAvgBytesPerSec*/
|
||||
put_u16le(chunk + 0x0c, get_u16be(chunk + 0x0c)); /*nBlockAlign*/
|
||||
put_u16le(chunk + 0x0e, get_u16be(chunk + 0x0e)); /*wBitsPerSample*/
|
||||
put_u16le(chunk + 0x10, get_u16be(chunk + 0x10)); /*cbSize*/
|
||||
put_u16le(chunk + 0x12, get_u16be(chunk + 0x12)); /*NumStreams*/
|
||||
put_u32le(chunk + 0x14, get_u32be(chunk + 0x14)); /*ChannelMask*/
|
||||
put_u32le(chunk + 0x18, get_u32be(chunk + 0x18)); /*SamplesEncoded*/
|
||||
put_u32le(chunk + 0x1c, get_u32be(chunk + 0x1c)); /*BytesPerBlock*/
|
||||
put_u32le(chunk + 0x20, get_u32be(chunk + 0x20)); /*PlayBegin*/
|
||||
put_u32le(chunk + 0x24, get_u32be(chunk + 0x24)); /*PlayLength*/
|
||||
put_u32le(chunk + 0x28, get_u32be(chunk + 0x28)); /*LoopBegin*/
|
||||
put_u32le(chunk + 0x2c, get_u32be(chunk + 0x2c)); /*LoopLength*/
|
||||
// put_u8(chunk + 0x30, get_u8(chunk + 0x30)); /*LoopCount*/
|
||||
// put_u8(chunk + 0x31, get_u8(chunk + 0x31)); /*EncoderVersion*/
|
||||
put_u16le(chunk + 0x32, get_u16be(chunk + 0x32)); /*BlockCount*/
|
||||
break;
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Makes a XMA1/2 RIFF header using a "fmt " chunk (XMAWAVEFORMAT/XMA2WAVEFORMATEX) or "XMA2" chunk (XMA2WAVEFORMAT), as a base:
|
||||
* Useful to preserve the stream layout */
|
||||
static int ffmpeg_make_riff_xma_chunk(STREAMFILE* sf, uint8_t* buf, int buf_size, uint32_t data_size, uint32_t chunk_offset, uint32_t chunk_size, int* p_is_xma1) {
|
||||
int buf_max = (0x04 + 0x04) + 0x04 + (0x04 + 0x04) + chunk_size + (0x04 + 0x04);
|
||||
if (buf_max > buf_size)
|
||||
return 0;
|
||||
|
||||
if (read_streamfile(buf+0x14, chunk_offset, chunk_size, sf) != chunk_size)
|
||||
return 0;
|
||||
|
||||
/* checks info from the chunk itself */
|
||||
int is_xma1 = 0;
|
||||
int is_xma2_old = buf[0x14] == 0x03 || buf[0x14] == 0x04;
|
||||
if (!is_xma2_old) {
|
||||
uint16_t codec = get_u16le(buf+0x14);
|
||||
int is_be = (codec > 0x1000);
|
||||
if (is_be)
|
||||
ffmpeg_fmt_chunk_swap_endian(buf+0x14, chunk_size, codec);
|
||||
is_xma1 = codec == 0x0165 || codec == 0x6501;
|
||||
}
|
||||
|
||||
memcpy (buf+0x00, "RIFF", 0x04);
|
||||
put_u32le(buf+0x04, (buf_max - 0x08 + data_size)); /* riff size */
|
||||
memcpy (buf+0x08, "WAVE", 0x04);
|
||||
memcpy (buf+0x0c, is_xma2_old ? "XMA2" : "fmt ", 0x04);
|
||||
put_u32le(buf+0x10, chunk_size);
|
||||
/* copied chunk in between */
|
||||
memcpy (buf+0x14 + chunk_size + 0x00, "data", 0x04);
|
||||
put_u32le(buf+0x14 + chunk_size + 0x04, data_size);
|
||||
|
||||
*p_is_xma1 = is_xma1;
|
||||
return buf_max;
|
||||
}
|
||||
|
||||
ffmpeg_codec_data* init_ffmpeg_xma_chunk(STREAMFILE* sf, uint32_t data_offset, uint32_t data_size, uint32_t chunk_offset, uint32_t chunk_size) {
|
||||
ffmpeg_codec_data* data = NULL;
|
||||
uint8_t buf[0x100];
|
||||
int is_xma1 = 0;
|
||||
|
||||
int bytes = ffmpeg_make_riff_xma_chunk(sf, buf, sizeof(buf), data_size, chunk_offset, chunk_size, &is_xma1);
|
||||
data = init_ffmpeg_header_offset(sf, buf, bytes, data_offset, data_size);
|
||||
if (!data) goto fail;
|
||||
|
||||
/* n5.1.2 XMA1 hangs on seeks near end (infinite loop), presumably due to missing flush in wmapro.c's ff_xma1_decoder + frame skip samples */
|
||||
if (is_xma1)
|
||||
ffmpeg_set_force_seek(data);
|
||||
|
||||
return data;
|
||||
fail:
|
||||
free_ffmpeg(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -489,15 +489,7 @@ VGMSTREAM* init_vgmstream_nub_xma(STREAMFILE* sf) {
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
{
|
||||
uint8_t buf[0x100];
|
||||
size_t bytes;
|
||||
|
||||
if (nus_codec == 0x04) {
|
||||
bytes = ffmpeg_make_riff_xma2_from_xma2_chunk(buf,0x100, chunk_offset,chunk_size, data_size, sf);
|
||||
} else {
|
||||
bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,0x100, chunk_offset,chunk_size, data_size, sf, 1);
|
||||
}
|
||||
vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf,bytes, start_offset,data_size);
|
||||
vgmstream->codec_data = init_ffmpeg_xma_chunk(sf, start_offset, data_size, chunk_offset, chunk_size);
|
||||
if ( !vgmstream->codec_data ) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
@ -180,12 +180,8 @@ VGMSTREAM* init_vgmstream_p3d(STREAMFILE* sf) {
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
case 0x786D6100: { /* "xma\0" (X360) */
|
||||
uint8_t buf[0x100];
|
||||
size_t bytes;
|
||||
|
||||
//TODO: some in Spider-Man 4 beta use 18ch but ffmpeg supports max 16ch XMA2
|
||||
bytes = ffmpeg_make_riff_xma2_from_xma2_chunk(buf, sizeof(buf), xma2_offset, xma2_size, data_size, sf);
|
||||
vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf,bytes, start_offset,data_size);
|
||||
vgmstream->codec_data = init_ffmpeg_xma_chunk(sf, start_offset, data_size, xma2_offset, xma2_size);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
@ -275,7 +275,7 @@ static VGMSTREAM* init_vgmstream_ubi_bao_base(ubi_bao_header* bao, STREAMFILE* s
|
||||
vgmstream->coding_type = coding_NGC_DSP;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = bao->stream_size / bao->channels;
|
||||
VGM_LOG("dsp=%x, %x, %x\n", bao->header_offset, bao->header_size, bao->extra_size);
|
||||
|
||||
/* mini DSP header (first 0x10 seem to contain DSP header fields like nibbles and format) */
|
||||
dsp_read_coefs_be(vgmstream, streamHead, bao->header_offset + bao->header_size + bao->extra_size + 0x10, 0x40);
|
||||
dsp_read_hist_be (vgmstream, streamHead, bao->header_offset + bao->header_size + bao->extra_size + 0x34, 0x40); /* after gain/initial ps */
|
||||
@ -286,10 +286,9 @@ VGM_LOG("dsp=%x, %x, %x\n", bao->header_offset, bao->header_size, bao->extra_siz
|
||||
case RAW_XMA1:
|
||||
case RAW_XMA2_OLD:
|
||||
case RAW_XMA2_NEW: {
|
||||
uint8_t buf[0x100];
|
||||
size_t bytes, chunk_size, data_size;
|
||||
size_t chunk_size, data_size;
|
||||
off_t chunk_offset;
|
||||
STREAMFILE* streamXMA;
|
||||
STREAMFILE* sf_xma;
|
||||
|
||||
switch(bao->codec) {
|
||||
case RAW_XMA1: chunk_size = 0x20; break;
|
||||
@ -339,26 +338,19 @@ VGM_LOG("dsp=%x, %x, %x\n", bao->header_offset, bao->header_size, bao->extra_siz
|
||||
header_size += align_size_to_block(sec2_num * bits_per_frame, 32) / 8; /* bitstream seek table? */
|
||||
header_size += sec3_num * 0x08;
|
||||
|
||||
streamXMA = streamData;
|
||||
sf_xma = streamData;
|
||||
chunk_offset = 0x00;
|
||||
start_offset += header_size;
|
||||
data_size = sec2_num * frame_size;
|
||||
}
|
||||
else {
|
||||
streamXMA = streamHead;
|
||||
sf_xma = streamHead;
|
||||
chunk_offset = bao->header_offset + bao->header_size;
|
||||
start_offset = 0x00;
|
||||
data_size = bao->stream_size;
|
||||
}
|
||||
|
||||
if (bao->codec == RAW_XMA2_OLD) {
|
||||
bytes = ffmpeg_make_riff_xma2_from_xma2_chunk(buf,0x100, chunk_offset, chunk_size, data_size, streamXMA);
|
||||
}
|
||||
else {
|
||||
bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,0x100, chunk_offset, chunk_size, data_size, streamXMA, 1);
|
||||
}
|
||||
|
||||
vgmstream->codec_data = init_ffmpeg_header_offset(streamData, buf, bytes, start_offset, data_size);
|
||||
vgmstream->codec_data = init_ffmpeg_xma_chunk(sf_xma, start_offset, data_size, chunk_offset, chunk_size);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
@ -365,9 +365,6 @@ VGMSTREAM* init_vgmstream_wwise_bnk(STREAMFILE* sf, int* p_prefetch) {
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
case XMA2: { /* X360/XBone */
|
||||
uint8_t buf[0x100];
|
||||
int bytes;
|
||||
|
||||
/* endian check should be enough */
|
||||
//if (ww.fmt_size != ...) goto fail; /* XMA1 0x20, XMA2old: 0x34, XMA2new: 0x40, XMA2 Guitar Hero Live/padded: 0x64, etc */
|
||||
|
||||
@ -375,14 +372,7 @@ VGMSTREAM* init_vgmstream_wwise_bnk(STREAMFILE* sf, int* p_prefetch) {
|
||||
if (!(ww.big_endian || (!ww.big_endian && check_extensions(sf,"wem,bnk"))))
|
||||
goto fail;
|
||||
|
||||
if (ww.xma2_offset) { /* older */
|
||||
bytes = ffmpeg_make_riff_xma2_from_xma2_chunk(buf, sizeof(buf), ww.xma2_offset, ww.xma2_size, ww.data_size, sf);
|
||||
}
|
||||
else { /* newer */
|
||||
bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf, sizeof(buf), ww.fmt_offset, ww.fmt_size, ww.data_size, sf, ww.big_endian);
|
||||
}
|
||||
|
||||
vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf,bytes, ww.data_offset,ww.data_size);
|
||||
vgmstream->codec_data = init_ffmpeg_xma_chunk(sf, ww.data_offset, ww.data_size, ww.xma2_offset ? ww.xma2_offset : ww.fmt_offset, ww.xma2_size ? ww.xma2_size : ww.fmt_size);
|
||||
if ( !vgmstream->codec_data ) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
@ -141,35 +141,20 @@ VGMSTREAM* init_vgmstream_xma(STREAMFILE* sf) {
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
{
|
||||
uint8_t buf[0x100];
|
||||
int bytes;
|
||||
|
||||
if (is_xma2_old) {
|
||||
bytes = ffmpeg_make_riff_xma2_from_xma2_chunk(buf, sizeof(buf), chunk_offset,chunk_size, data_size, sf);
|
||||
}
|
||||
else {
|
||||
bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf, sizeof(buf), chunk_offset,chunk_size, data_size, sf, fmt_be);
|
||||
}
|
||||
|
||||
vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf, bytes, start_offset, data_size);
|
||||
vgmstream->codec_data = init_ffmpeg_xma_chunk(sf, start_offset, data_size, chunk_offset, chunk_size);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
xma_fix_raw_samples(vgmstream, sf, start_offset, data_size, chunk_offset, 1,1);
|
||||
|
||||
// some XMA (1?) files hang on seek to 0 near EOF, probably due to end packet/skip samples bugs
|
||||
ffmpeg_set_force_seek(vgmstream->codec_data);
|
||||
}
|
||||
#else
|
||||
goto fail;
|
||||
#endif
|
||||
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
|
@ -2,36 +2,36 @@
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* XMA from Unreal Engine games */
|
||||
VGMSTREAM* init_vgmstream_xma_ue3(STREAMFILE *sf) {
|
||||
VGMSTREAM* init_vgmstream_xma_ue3(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t start_offset, chunk_offset;
|
||||
int loop_flag, channel_count, sample_rate, is_xma2_old = 0;
|
||||
uint32_t start_offset, chunk_offset;
|
||||
int loop_flag, channel_count, sample_rate;
|
||||
int num_samples, loop_start_sample, loop_end_sample;
|
||||
size_t file_size, fmt_size, seek_size, data_size;
|
||||
uint32_t file_size, chunk_size, seek_size, data_size;
|
||||
|
||||
|
||||
/* checks */
|
||||
/* .xma: assumed */
|
||||
/* .x360audio: fake produced by UE Viewer */
|
||||
if (!check_extensions(sf, "xma,x360audio,"))
|
||||
goto fail;
|
||||
|
||||
/* UE3 uses class-like chunks called "SoundNodeWave" to store info and (rarely multi) raw audio data. Other
|
||||
* platforms use standard formats (PC=Ogg, PS3=MSF), while X360 has mutant XMA. Extractors transmogrify
|
||||
* UE3 XMA into RIFF XMA (discarding seek table and changing endianness) but we'll support actual raw
|
||||
* data for completeness. UE4 has .uexp which are very similar so XBone may use the same XMA. */
|
||||
|
||||
/* checks */
|
||||
file_size = get_streamfile_size(sf);
|
||||
fmt_size = read_u32be(0x00, sf);
|
||||
chunk_size = read_u32be(0x00, sf);
|
||||
seek_size = read_u32be(0x04, sf);
|
||||
data_size = read_u32be(0x08, sf);
|
||||
if (0x0c + fmt_size + seek_size + data_size != file_size)
|
||||
if (0x0c + chunk_size + seek_size + data_size != file_size)
|
||||
goto fail;
|
||||
|
||||
/* .xma: assumed */
|
||||
/* .x360audio: fake produced by UE Viewer */
|
||||
if (!check_extensions(sf, "xma,x360audio,"))
|
||||
goto fail;
|
||||
|
||||
chunk_offset = 0x0c;
|
||||
|
||||
/* parse sample data (always BE unlike real XMA) */
|
||||
if (fmt_size != 0x34) { /* old XMA2 [The Last Remnant (X360)] */
|
||||
is_xma2_old = 1;
|
||||
if (chunk_size != 0x34) { /* old XMA2 [The Last Remnant (X360)] */
|
||||
xma2_parse_xma2_chunk(sf, chunk_offset, &channel_count,&sample_rate, &loop_flag, &num_samples, &loop_start_sample, &loop_end_sample);
|
||||
}
|
||||
else { /* new XMA2 [Shadows of the Damned (X360)] */
|
||||
@ -40,7 +40,7 @@ VGMSTREAM* init_vgmstream_xma_ue3(STREAMFILE *sf) {
|
||||
xma2_parse_fmt_chunk_extra(sf, chunk_offset, &loop_flag, &num_samples, &loop_start_sample, &loop_end_sample, 1);
|
||||
}
|
||||
|
||||
start_offset = 0x0c + fmt_size + seek_size;
|
||||
start_offset = 0x0c + chunk_size + seek_size;
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
@ -54,15 +54,7 @@ VGMSTREAM* init_vgmstream_xma_ue3(STREAMFILE *sf) {
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
{
|
||||
uint8_t buf[0x100];
|
||||
size_t bytes;
|
||||
|
||||
if (is_xma2_old) {
|
||||
bytes = ffmpeg_make_riff_xma2_from_xma2_chunk(buf,0x100, chunk_offset,fmt_size, data_size, sf);
|
||||
} else {
|
||||
bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,0x100, chunk_offset,fmt_size, data_size, sf, 1);
|
||||
}
|
||||
vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf,bytes, start_offset,data_size);
|
||||
vgmstream->codec_data = init_ffmpeg_xma_chunk(sf, start_offset, data_size, chunk_offset, chunk_size);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
@ -91,7 +83,6 @@ VGMSTREAM* init_vgmstream_xma_ue3(STREAMFILE *sf) {
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
|
Loading…
x
Reference in New Issue
Block a user