Fix hang on full loops with xma1 formats

This commit is contained in:
bnnm 2023-01-20 01:02:23 +01:00
parent 41bad49872
commit 501af6ad8c
9 changed files with 216 additions and 188 deletions

View File

@ -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) */

View File

@ -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) {

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;