diff --git a/src/coding/mpeg_custom_utils_eamp3.c b/src/coding/mpeg_custom_utils_eamp3.c new file mode 100644 index 00000000..5925ccc1 --- /dev/null +++ b/src/coding/mpeg_custom_utils_eamp3.c @@ -0,0 +1,175 @@ +#include "mpeg_decoder.h" + +#ifdef VGM_USE_MPEG + +/* parsed info from a single EAMP3 frame */ +typedef struct { + uint32_t extended_flag; + uint32_t stereo_flag; /* assumed */ + uint32_t unknown_flag; /* unused? */ + uint32_t frame_size; /* full size including headers and pcm block */ + uint32_t pcm_number; /* samples in the PCM block (typically 1 MPEG frame, 1152) */ + + uint32_t pre_size; /* size of the header part */ + uint32_t mpeg_size; /* size of the MPEG part */ + uint32_t pcm_size; /* size of the PCM block */ +} eamp3_frame_info; + +static int eamp3_parse_frame(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, eamp3_frame_info * eaf); +static int eamp3_write_pcm_block(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream, eamp3_frame_info * eaf); +static int eamp3_skip_data(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream, int at_start); + +/* init config and validate */ +int mpeg_custom_setup_init_eamp3(STREAMFILE *streamFile, off_t start_offset, mpeg_codec_data *data, coding_t *coding_type) { + mpeg_frame_info info; + uint16_t frame_header; + size_t header_size; + + + /* test unknown stuff */ + frame_header = (uint16_t)read_16bitLE(start_offset+0x00, streamFile); + if (frame_header & 0x2000) { + VGM_LOG("EAMP3: found unknown bit 13\n"); + goto fail; + } + if ((frame_header & 0x8000) && (uint32_t)read_32bitLE(start_offset+0x02, streamFile) > 0xFFFF) { + VGM_LOG("EAMP3: found big PCM block\n"); + goto fail; + } + + /* get frame info at offset */ + header_size = (frame_header & 0x8000) ? 0x06 : 0x02; + if (!mpeg_get_frame_info(streamFile, start_offset+header_size, &info)) + goto fail; + switch(info.layer) { + case 1: *coding_type = coding_MPEG_layer1; break; + case 2: *coding_type = coding_MPEG_layer2; break; + case 3: *coding_type = coding_MPEG_layer3; break; + default: goto fail; + } + data->channels_per_frame = info.channels; + data->samples_per_frame = info.frame_samples; + data->bitrate_per_frame = info.bit_rate; + data->sample_rate_per_frame = info.sample_rate; + + + return 1; +fail: + return 0; +} + +/* reads custom frame header + MPEG data + (optional) PCM block */ +int mpeg_custom_parse_frame_eamp3(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream) { + mpeg_custom_stream *ms = data->streams[num_stream]; + eamp3_frame_info eaf; + int ok; + + + if (!eamp3_skip_data(stream, data, num_stream, 1)) + goto fail; + + ok = eamp3_parse_frame(stream, data, &eaf); + if (!ok) goto fail; + + ok = eamp3_write_pcm_block(stream, data, num_stream, &eaf); + if (!ok) goto fail; + + ms->bytes_in_buffer = read_streamfile(ms->buffer,stream->offset + eaf.pre_size, eaf.mpeg_size, stream->streamfile); + stream->offset += eaf.frame_size; + + if (!eamp3_skip_data(stream, data, num_stream, 0)) + goto fail; + + return 1; +fail: + return 0; +} + + +static int eamp3_parse_frame(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, eamp3_frame_info * eaf) { + uint16_t current_header = (uint16_t)read_16bitLE(stream->offset+0x00, stream->streamfile); + + eaf->extended_flag = (current_header & 0x8000); + eaf->stereo_flag = (current_header & 0x4000); + eaf->unknown_flag = (current_header & 0x2000); + eaf->frame_size = (current_header & 0x1FFF); /* full size including PCM block */ + eaf->pcm_number = 0; + if (eaf->extended_flag > 0) { + eaf->pcm_number = (uint32_t)read_32bitLE(stream->offset+0x02, stream->streamfile); + eaf->pcm_size = sizeof(sample) * eaf->pcm_number * data->channels_per_frame; + eaf->pre_size = 0x06; + eaf->mpeg_size = eaf->frame_size - eaf->pre_size - eaf->pcm_size; + if (eaf->frame_size < eaf->pre_size + eaf->pcm_size) { + VGM_LOG("EAMP3: bad pcm size at %x\n", (uint32_t)stream->offset); + goto fail; + } + } + else { + eaf->pcm_size = 0; + eaf->pre_size = 0x02; + eaf->mpeg_size = eaf->frame_size - eaf->pre_size; + } + + return 1; +fail: + return 0; +} + +/* write PCM block directly to sample buffer and setup decode discard (see EALayer3). */ +static int eamp3_write_pcm_block(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream, eamp3_frame_info * eaf) { + mpeg_custom_stream *ms = data->streams[num_stream]; + size_t bytes_filled; + int i; + + + bytes_filled = sizeof(sample) * ms->samples_filled * data->channels_per_frame; + if (bytes_filled + eaf->pcm_size > ms->output_buffer_size) { + VGM_LOG("EAMP3: can't fill the sample buffer with 0x%x\n", eaf->pcm_size); + goto fail; + } + + + if (eaf->pcm_number) { + + /* read + write PCM block samples (always LE) */ + for (i = 0; i < eaf->pcm_number * data->channels_per_frame; i++) { + off_t pcm_offset = stream->offset + eaf->pre_size + eaf->mpeg_size + sizeof(sample)*i; + int16_t pcm_sample = read_16bitLE(pcm_offset,stream->streamfile); + put_16bitLE(ms->output_buffer + bytes_filled + sizeof(sample)*i, pcm_sample); + } + ms->samples_filled += eaf->pcm_number; + + /* modify decoded samples */ + { + size_t decode_to_discard = eaf->pcm_number; //todo guessed + ms->decode_to_discard += decode_to_discard; + } + } + + return 1; +fail: + return 0; +} + +/* Skip EA-frames from other streams for .sns/sps multichannel (see EALayer3). */ +static int eamp3_skip_data(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream, int at_start) { + int ok, i; + eamp3_frame_info eaf; + int skips = at_start ? num_stream : data->streams_size - 1 - num_stream; + + + for (i = 0; i < skips; i++) { + ok = eamp3_parse_frame(stream, data, &eaf); + if (!ok) goto fail; + + //;VGM_LOG("s%i: skipping %x, now at %lx\n", num_stream,eaf.frame_size,stream->offset); + stream->offset += eaf.frame_size; + } + //;VGM_LOG("s%i: skipped %i frames, now at %lx\n", num_stream,skips,stream->offset); + + return 1; +fail: + return 0; +} + +#endif diff --git a/src/coding/mpeg_decoder.c b/src/coding/mpeg_decoder.c index fd9c5f2e..604e0fa0 100644 --- a/src/coding/mpeg_decoder.c +++ b/src/coding/mpeg_decoder.c @@ -140,6 +140,7 @@ mpeg_codec_data *init_mpeg_custom(STREAMFILE *streamFile, off_t start_offset, co case MPEG_EAL32P: case MPEG_EAL32S: ok = mpeg_custom_setup_init_ealayer3(streamFile, start_offset, data, coding_type); break; case MPEG_AWC: ok = mpeg_custom_setup_init_awc(streamFile, start_offset, data, coding_type); break; + case MPEG_EAMP3: ok = mpeg_custom_setup_init_eamp3(streamFile, start_offset, data, coding_type); break; default: ok = mpeg_custom_setup_init_default(streamFile, start_offset, data, coding_type); break; } if (!ok) @@ -399,6 +400,7 @@ static void decode_mpeg_custom_stream(VGMSTREAMCHANNEL *stream, mpeg_codec_data case MPEG_EAL32S: ok = mpeg_custom_parse_frame_ealayer3(stream, data, num_stream); break; case MPEG_AHX: ok = mpeg_custom_parse_frame_ahx(stream, data, num_stream); break; case MPEG_AWC: ok = mpeg_custom_parse_frame_awc(stream, data, num_stream); break; + case MPEG_EAMP3: ok = mpeg_custom_parse_frame_eamp3(stream, data, num_stream); break; default: ok = mpeg_custom_parse_frame_default(stream, data, num_stream); break; } if (!ok) { diff --git a/src/coding/mpeg_decoder.h b/src/coding/mpeg_decoder.h index 94e31b13..a8f11733 100644 --- a/src/coding/mpeg_decoder.h +++ b/src/coding/mpeg_decoder.h @@ -21,11 +21,13 @@ int mpeg_get_frame_info(STREAMFILE *streamfile, off_t offset, mpeg_frame_info * int mpeg_custom_setup_init_default(STREAMFILE *streamFile, off_t start_offset, mpeg_codec_data *data, coding_t *coding_type); int mpeg_custom_setup_init_ealayer3(STREAMFILE *streamFile, off_t start_offset, mpeg_codec_data *data, coding_t *coding_type); int mpeg_custom_setup_init_awc(STREAMFILE *streamFile, off_t start_offset, mpeg_codec_data *data, coding_t *coding_type); +int mpeg_custom_setup_init_eamp3(STREAMFILE *streamFile, off_t start_offset, mpeg_codec_data *data, coding_t *coding_type); int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream); int mpeg_custom_parse_frame_ahx(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream); int mpeg_custom_parse_frame_ealayer3(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream); int mpeg_custom_parse_frame_awc(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream); +int mpeg_custom_parse_frame_eamp3(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream); #endif/* VGM_USE_MPEG */ diff --git a/src/libvgmstream.vcproj b/src/libvgmstream.vcproj index cb731253..85614108 100644 --- a/src/libvgmstream.vcproj +++ b/src/libvgmstream.vcproj @@ -1837,6 +1837,10 @@ + + + diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters index dfd7938a..f8ab0189 100644 --- a/src/libvgmstream.vcxproj.filters +++ b/src/libvgmstream.vcxproj.filters @@ -1090,6 +1090,9 @@ coding\Source Files + + coding\Source Files + coding\Source Files diff --git a/src/meta/ea_eaac.c b/src/meta/ea_eaac.c index 2b5ff700..80f3f370 100644 --- a/src/meta/ea_eaac.c +++ b/src/meta/ea_eaac.c @@ -19,6 +19,7 @@ #define EAAC_CODEC_DSP 0x08 #define EAAC_CODEC_EASPEEX 0x09 #define EAAC_CODEC_EATRAX 0x0a +#define EAAC_CODEC_EAMP3 0x0b #define EAAC_CODEC_EAOPUS 0x0c #define EAAC_FLAG_NONE 0x00 @@ -680,6 +681,25 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST } #endif + +#ifdef VGM_USE_MPEG + case EAAC_CODEC_EAMP3: { /* "EM30"?: EAMP3 [Need for Speed 2015 (PS4)] */ + mpeg_custom_config cfg = {0}; + + start_offset = 0x00; /* must point to the custom streamfile's beginning */ + + temp_streamFile = setup_eaac_streamfile(streamData, eaac.version, eaac.codec, eaac.streamed,0,0, eaac.stream_offset); + if (!temp_streamFile) goto fail; + + vgmstream->codec_data = init_mpeg_custom(temp_streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_EAMP3, &cfg); + if (!vgmstream->codec_data) goto fail; + vgmstream->layout_type = layout_none; + + break; + } + +#endif + #ifdef VGM_USE_FFMPEG case EAAC_CODEC_EAOPUS: { /* EAOpus (unknown FourCC) [FIFA 17 (PC), FIFA 19 (Switch)]*/ int skip = 0; @@ -701,7 +721,7 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST } #endif - case EAAC_CODEC_EASPEEX: /* EASpeex (libspeex variant, base versions vary: 1.0.5, 1.2beta3) */ //todo + case EAAC_CODEC_EASPEEX: /* "Esp0"?: EASpeex (libspeex variant, base versions vary: 1.0.5, 1.2beta3) */ //todo default: VGM_LOG("EA EAAC: unknown codec 0x%02x\n", eaac.codec); goto fail; diff --git a/src/meta/ea_eaac_streamfile.h b/src/meta/ea_eaac_streamfile.h index 8c6d3ce9..8ece6be2 100644 --- a/src/meta/ea_eaac_streamfile.h +++ b/src/meta/ea_eaac_streamfile.h @@ -85,6 +85,7 @@ static size_t eaac_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, case 0x05: /* EALayer3 v1 */ case 0x06: /* EALayer3 v2 "PCM" */ case 0x07: /* EALayer3 v2 "Spike" */ + case 0x0b: /* EAMP3 */ case 0x0c: /* EAOpus */ data->skip_size = 0x08; data->data_size = data->block_size - data->skip_size; @@ -205,6 +206,7 @@ static size_t eaac_io_size(STREAMFILE *streamfile, eaac_io_data* data) { case 0x05: /* EALayer3 v1 */ case 0x06: /* EALayer3 v2 "PCM" */ case 0x07: /* EALayer3 v2 "Spike" */ + case 0x0b: /* EAMP3 */ case 0x0c: /* EAOpus */ data_size = block_size - 0x08; break; diff --git a/src/vgmstream.h b/src/vgmstream.h index 7ce02dea..1e169121 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -952,7 +952,8 @@ typedef enum { MPEG_EAL32P, /* EALayer3 v2 "PCM", custom frames with v2 header + bigger PCM blocks? */ MPEG_EAL32S, /* EALayer3 v2 "Spike", custom frames with v2 header + smaller PCM blocks? */ MPEG_LYN, /* N streams of fixed interleave */ - MPEG_AWC /* N streams in block layout (music) or absolute offsets (sfx) */ + MPEG_AWC, /* N streams in block layout (music) or absolute offsets (sfx) */ + MPEG_EAMP3 /* custom frame header + MPEG frame + PCM blocks */ } mpeg_custom_t; /* config for the above modes */