Add .xopus decoding [Angry Birds Transformers (Android)]

This commit is contained in:
bnnm 2018-10-13 21:01:58 +02:00
parent cca676bb0f
commit e49a688559
10 changed files with 150 additions and 2 deletions

View File

@ -281,12 +281,14 @@ void ffmpeg_set_skip_samples(ffmpeg_codec_data * data, int skip_samples);
ffmpeg_codec_data * init_ffmpeg_switch_opus(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate);
ffmpeg_codec_data * init_ffmpeg_ue4_opus(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate);
ffmpeg_codec_data * init_ffmpeg_ea_opus(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate);
ffmpeg_codec_data * init_ffmpeg_x_opus(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate);
size_t switch_opus_get_samples(off_t offset, size_t data_size, STREAMFILE *streamFile);
size_t switch_opus_get_encoder_delay(off_t offset, STREAMFILE *streamFile);
size_t ue4_opus_get_encoder_delay(off_t offset, STREAMFILE *streamFile);
size_t ea_opus_get_encoder_delay(off_t offset, STREAMFILE *streamFile);
size_t x_opus_get_encoder_delay(off_t offset, STREAMFILE *streamFile);
#endif

View File

@ -16,8 +16,9 @@
static size_t make_oggs_first(uint8_t * buf, int buf_size, int channels, int skip, int sample_rate);
static size_t make_oggs_page(uint8_t * buf, int buf_size, size_t data_size, int page_sequence, int granule);
static size_t opus_get_packet_samples(const uint8_t * buf, int len);
static size_t get_xopus_packet_size(int packet, STREAMFILE * streamfile);
typedef enum { OPUS_SWITCH, OPUS_UE4, OPUS_EA } opus_type_t;
typedef enum { OPUS_SWITCH, OPUS_UE4, OPUS_EA, OPUS_X } opus_type_t;
typedef struct {
/* config */
opus_type_t type;
@ -104,6 +105,10 @@ static size_t opus_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset,
data_size = (uint16_t)read_16bitBE(data->physical_offset, streamfile);
skip_size = 0x02;
break;
case OPUS_X:
data_size = get_xopus_packet_size(data->sequence - 2, streamfile);
skip_size = 0;
break;
default:
return 0;
}
@ -162,6 +167,7 @@ static size_t opus_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset,
static size_t opus_io_size(STREAMFILE *streamfile, opus_io_data* data) {
off_t physical_offset, max_physical_offset;
size_t logical_size = 0;
int packet = 0;
if (data->logical_size)
return data->logical_size;
@ -192,6 +198,10 @@ static size_t opus_io_size(STREAMFILE *streamfile, opus_io_data* data) {
data_size = (uint16_t)read_16bitBE(physical_offset, streamfile);
skip_size = 0x02;
break;
case OPUS_X:
data_size = get_xopus_packet_size(packet, streamfile);
skip_size = 0x00;
break;
default:
return 0;
}
@ -199,6 +209,7 @@ static size_t opus_io_size(STREAMFILE *streamfile, opus_io_data* data) {
physical_offset += data_size + skip_size;
logical_size += oggs_size + data_size;
packet++;
}
/* logical size can be bigger though */
@ -390,6 +401,24 @@ fail:
static size_t make_opus_header(uint8_t * buf, int buf_size, int channels, int skip, int sample_rate) {
size_t header_size = 0x13;
int mapping_family = 0; /* channel config: 0=standard (single stream mono/stereo), 1=vorbis, 255: not defined */
#if 0
int stream_count = 1; /* number of internal mono/stereo streams (N mono/stereo streams form M channels) */
int coupled_count = channels - 1; /* number of stereo streams (packet has which one is mono or stereo) */
/* special multichannel config */
if (channels > 2) {
header_size += 0x01+0x01+channels;
switch(type) {
case ...:
...
break;
default:
goto fail;
}
}
#endif
if (header_size > buf_size) {
VGM_LOG("OPUS: buffer can't hold header\n");
@ -403,7 +432,19 @@ static size_t make_opus_header(uint8_t * buf, int buf_size, int channels, int sk
put_16bitLE(buf+0x0A, skip);
put_32bitLE(buf+0x0c, sample_rate);
put_16bitLE(buf+0x10, 0); /* output gain */
put_8bit (buf+0x12, 0); /* channel mapping family */
put_8bit (buf+0x12, mapping_family);
#if 0
if (mapping_family > 0) {
int 0;
put_8bit(buf+0x13, stream_count);
put_8bit(buf+0x14, coupled_count);
for (i = 0; i < channels; i++) {
put_8bit(buf+0x15+i, i); /* channel mapping (may need to change per family) */
}
}
#endif
return header_size;
fail:
@ -471,6 +512,7 @@ static size_t opus_get_packet_samples(const uint8_t * buf, int len) {
static size_t custom_opus_get_samples(off_t offset, size_t data_size, STREAMFILE *streamFile, opus_type_t type) {
size_t num_samples = 0;
off_t end_offset = offset + data_size;
int packet = 0;
if (end_offset > get_streamfile_size(streamFile)) {
VGM_LOG("OPUS: wrong end offset found\n");
@ -495,6 +537,10 @@ static size_t custom_opus_get_samples(off_t offset, size_t data_size, STREAMFILE
data_size = (uint16_t)read_16bitBE(offset, streamFile);
skip_size = 0x02;
break;
case OPUS_X:
data_size = get_xopus_packet_size(packet, streamFile);
skip_size = 0x00;
break;
default:
return 0;
}
@ -503,6 +549,7 @@ static size_t custom_opus_get_samples(off_t offset, size_t data_size, STREAMFILE
num_samples += opus_get_packet_samples(buf, 0x04);
offset += skip_size + data_size;
packet++;
}
return num_samples;
@ -527,6 +574,9 @@ static size_t custom_opus_get_encoder_delay(off_t offset, STREAMFILE *streamFile
case OPUS_EA:
skip_size = 0x02;
break;
case OPUS_X:
skip_size = 0x00;
break;
default:
return 0;
}
@ -544,8 +594,21 @@ size_t ue4_opus_get_encoder_delay(off_t offset, STREAMFILE *streamFile) {
size_t ea_opus_get_encoder_delay(off_t offset, STREAMFILE *streamFile) {
return custom_opus_get_encoder_delay(offset, streamFile, OPUS_EA);
}
size_t x_opus_get_encoder_delay(off_t offset, STREAMFILE *streamFile) {
return custom_opus_get_encoder_delay(offset, streamFile, OPUS_X);
}
static size_t get_xopus_packet_size(int packet, STREAMFILE * streamfile) {
/* XOPUS has a packet size table at the beginning, get size from there.
* Maybe should copy the table during setup to avoid IO, but all XOPUS are
* quite small so it isn't very noticeable. */
return (uint16_t)read_16bitLE(0x20 + packet*0x02, streamfile);
}
/* ******************************************************* */
static ffmpeg_codec_data * init_ffmpeg_custom_opus(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate, opus_type_t type) {
ffmpeg_codec_data * ffmpeg_data = NULL;
STREAMFILE *temp_streamFile = NULL;
@ -577,5 +640,8 @@ ffmpeg_codec_data * init_ffmpeg_ue4_opus(STREAMFILE *streamFile, off_t start_off
ffmpeg_codec_data * init_ffmpeg_ea_opus(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate) {
return init_ffmpeg_custom_opus(streamFile, start_offset, data_size, channels, skip, sample_rate, OPUS_EA);
}
ffmpeg_codec_data * init_ffmpeg_x_opus(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate) {
return init_ffmpeg_custom_opus(streamFile, start_offset, data_size, channels, skip, sample_rate, OPUS_X);
}
#endif

View File

@ -459,6 +459,7 @@ static const char* extension_list[] = {
"xwav",//fake, to be removed
"xwb",
"xmd",
"xopus",
"xwc",
"xwm",
"xwma",
@ -1094,6 +1095,7 @@ static const meta_info meta_info_list[] = {
{meta_UE4OPUS, "Epic Games UE4OPUS header"},
{meta_XWMA, "Microsoft XWMA RIFF header"},
{meta_VA3, "Konami / Sony ATRAC3 Header" },
{meta_XOPUS, "Rovio XOPUS header"},
};

View File

@ -1593,6 +1593,10 @@
<File
RelativePath=".\meta\xnb.c"
>
</File>
<File
RelativePath=".\meta\xopus.c"
>
</File>
<File
RelativePath=".\meta\xss.c"

View File

@ -472,6 +472,7 @@
<ClCompile Include="meta\x360_pasx.c" />
<ClCompile Include="meta\xma.c" />
<ClCompile Include="meta\xnb.c" />
<ClCompile Include="meta\xopus.c" />
<ClCompile Include="meta\xss.c" />
<ClCompile Include="meta\xvag.c" />
<ClCompile Include="meta\xmd.c" />

View File

@ -1435,6 +1435,9 @@
<ClCompile Include="meta\xnb.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\xopus.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\x360_cxs.c">
<Filter>meta\Source Files</Filter>
</ClCompile>

View File

@ -797,4 +797,6 @@ VGMSTREAM * init_vgmstream_ue4opus(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_xwma(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_xopus(STREAMFILE * streamFile);
#endif /*_META_H*/

66
src/meta/xopus.c Normal file
View File

@ -0,0 +1,66 @@
#include "meta.h"
#include "../coding/coding.h"
/* XOPUS - from Rovio games [Angry Birds: Transformers (Android), Angry Birds: Go (Android)] */
VGMSTREAM * init_vgmstream_xopus(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
int loop_flag = 0, channel_count, sample_rate, num_samples, skip;
size_t data_size;
int entries;
/* checks*/
if (!check_extensions(streamFile, "xopus"))
goto fail;
if (read_32bitBE(0x00, streamFile) != 0x584F7075) /* "XOpu" */
goto fail;
/* 0x04: always 1? 0x30? */
channel_count = read_8bit(0x05, streamFile);
/* 0x06: always 0x30? */
/* 0x08: always 0xc8? encoder delay? */
num_samples = read_32bitLE(0x0c, streamFile);
/* 0x10: always 0x138? max packet size? */
entries = read_32bitLE(0x14, streamFile); /* packet sizes */
/* 0x18: data size */
/* 0x1c: unused */
/* 0x20+: packet sizes table */
sample_rate = 48000;
loop_flag = 0;
start_offset = 0x20 + 0x02*entries;
data_size = get_streamfile_size(streamFile) - start_offset;
skip = x_opus_get_encoder_delay(start_offset, streamFile);
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_XOPUS;
vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = num_samples /*- skip*/;
#ifdef VGM_USE_FFMPEG
{
vgmstream->codec_data = init_ffmpeg_x_opus(streamFile, start_offset,data_size, vgmstream->channels, skip, vgmstream->sample_rate);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
}
#else
goto fail;
#endif
if (!vgmstream_open_stream(vgmstream, streamFile, start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -443,6 +443,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_adpcm_capcom,
init_vgmstream_ue4opus,
init_vgmstream_xwma,
init_vgmstream_xopus,
/* lowest priority metas (should go after all metas, and TXTH should go before raw formats) */

View File

@ -700,6 +700,7 @@ typedef enum {
meta_UE4OPUS,
meta_XWMA,
meta_VA3, /* DDR Supernova 2 AC */
meta_XOPUS,
} meta_t;