Merge pull request #1073 from bnnm/eaac-mpeg

- Fix some .sps [Madden 13 (Vita), Madden 22 (PC)]
- Fix .wav with broken interleave [Rayman 2 (DC)]
- Fix .bnsf key detection in rare cases
- Add .mp3 parser using mpg123 for consistency
- Fix xmplay plugin
This commit is contained in:
bnnm 2022-02-06 13:27:19 +01:00 committed by GitHub
commit 27f80241b8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 259 additions and 108 deletions

View File

@ -244,7 +244,7 @@ Renaming extensions should prevent those issues, or just uninstall those *Web
Media Extension* for better experience anyway.
#### Fallout SFX .ACM
Due to technical limitations, to play Fallout 1 SFX you need to rename them from
Due to technical limitations, to play Fallout 1/2 SFX you need to rename them from
`.acm` to `.wavc` (forces mono).
### Demuxed videos

View File

@ -481,6 +481,18 @@ void free_mpeg(mpeg_codec_data* data);
int mpeg_get_sample_rate(mpeg_codec_data* data);
long mpeg_bytes_to_samples(long bytes, const mpeg_codec_data* data);
typedef struct {
int version;
int layer;
int bit_rate;
int sample_rate;
int frame_samples;
int frame_size; /* bytes */
int channels;
} mpeg_frame_info;
int mpeg_get_frame_info(STREAMFILE* sf, off_t offset, mpeg_frame_info* info);
#endif

View File

@ -1072,7 +1072,7 @@ static int unpack_frame(int bit_rate, const uint8_t* data, int frame_size, /*int
/* test for errors (in refdec but not Namco's, useful to detect decryption) */
if (test_errors) {
int max_pad_bytes = 0x7; /* usually 0x04 and rarely ~0x07 */
int max_pad_bytes = 0x8; /* usually 0x04 and rarely ~0x08 */
int bits_left = 8 * expected_frame_size - bitpos;
int i, endpos, test_bits;
@ -1088,7 +1088,7 @@ static int unpack_frame(int bit_rate, const uint8_t* data, int frame_size, /*int
return -1;
}
/* extra: test we aren't in the middle of padding (happens with bad keys)
/* extra: test we aren't in the middle of padding (happens with bad keys, this test catches most)
* After reading the whole frame, last bit position should land near last useful
* data, a few bytes into padding, so check there aren't too many padding bits. */
endpos = bitpos;

View File

@ -75,16 +75,4 @@ int mpeg_custom_parse_frame_eamp3(VGMSTREAMCHANNEL* stream, mpeg_codec_data* dat
#endif/* VGM_USE_MPEG */
typedef struct {
int version;
int layer;
int bit_rate;
int sample_rate;
int frame_samples;
int frame_size; /* bytes */
int channels;
} mpeg_frame_info;
int mpeg_get_frame_info(STREAMFILE* sf, off_t offset, mpeg_frame_info* info);
#endif/*_MPEG_DECODER_H_ */

View File

@ -1382,6 +1382,7 @@ static const meta_info meta_info_list[] = {
{meta_WBK, "Treyarch WBK header"},
{meta_WBK_NSLB, "Treyarch NSLB header"},
{meta_DSP_APEX, "Koei Tecmo APEX header"},
{meta_MPEG, "MPEG header"},
};
void get_vgmstream_coding_description(VGMSTREAM* vgmstream, char* out, size_t out_size) {

View File

@ -392,6 +392,7 @@
<ClCompile Include="meta\maxis_xa.c" />
<ClCompile Include="meta\mc3.c" />
<ClCompile Include="meta\mca.c" />
<ClCompile Include="meta\mpeg.c" />
<ClCompile Include="meta\mus_vc.c" />
<ClCompile Include="meta\mus_acm.c" />
<ClCompile Include="meta\musc.c" />

View File

@ -652,6 +652,9 @@
<ClCompile Include="meta\mca.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\mpeg.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\mus_acm.c">
<Filter>meta\Source Files</Filter>
</ClCompile>

View File

@ -1050,7 +1050,7 @@ typedef struct {
off_t prefetch_offset;
} eaac_header;
static segmented_layout_data* build_segmented_eaaudiocore_looping(STREAMFILE *sf_data, eaac_header *eaac);
static segmented_layout_data* build_segmented_eaaudiocore_looping(STREAMFILE* sf_head, STREAMFILE* sf_data, eaac_header* eaac);
static layered_layout_data* build_layered_eaaudiocore(STREAMFILE* sf, eaac_header *eaac, off_t start_offset);
static STREAMFILE *setup_eaac_streamfile(eaac_header *ea, STREAMFILE* sf_head, STREAMFILE* sf_data);
static size_t calculate_eaac_size(STREAMFILE* sf, eaac_header *ea, uint32_t num_samples, off_t start_offset, int is_ram);
@ -1137,8 +1137,9 @@ static VGMSTREAM* init_vgmstream_eaaudiocore_header(STREAMFILE* sf_head, STREAMF
eaac.codec == EAAC_CODEC_EALAYER3_V2_PCM ||
eaac.codec == EAAC_CODEC_EALAYER3_V2_SPIKE ||
eaac.codec == EAAC_CODEC_EAXMA ||
eaac.codec == EAAC_CODEC_XAS1)) {
VGM_LOG("EA EAAC: unknown actual looping for codec %x\n", eaac.codec);
eaac.codec == EAAC_CODEC_XAS1 ||
eaac.codec == EAAC_CODEC_EATRAX)) {
VGM_LOG("EA EAAC: unknown actual looping %i for codec %x\n", eaac.loop_start, eaac.codec);
goto fail;
}
}
@ -1220,7 +1221,7 @@ static VGMSTREAM* init_vgmstream_eaaudiocore_header(STREAMFILE* sf_head, STREAMF
if (eaac.version == EAAC_VERSION_V0 && eaac.streamed) {
/* open SNS file if needed */
if (standalone) {
snsFile = open_streamfile_by_ext(sf_head, "sns");
snsFile = open_streamfile_by_ext(sf_head, "sns"); //todo clean
sf_data = snsFile;
}
if (!sf_data) goto fail;
@ -1255,7 +1256,7 @@ static VGMSTREAM* init_vgmstream_eaaudiocore_header(STREAMFILE* sf_head, STREAMF
/* special (if hacky) loop handling, see comments */
if (eaac.loop_start > 0) {
segmented_layout_data *data = build_segmented_eaaudiocore_looping(sf, &eaac);
segmented_layout_data *data = build_segmented_eaaudiocore_looping(sf, sf, &eaac);
if (!data) goto fail;
vgmstream->layout_data = data;
vgmstream->coding_type = data->segments[0]->coding_type;
@ -1276,7 +1277,7 @@ static VGMSTREAM* init_vgmstream_eaaudiocore_header(STREAMFILE* sf_head, STREAMF
/* special (if hacky) loop handling, see comments */
if (eaac.loop_start > 0) {
segmented_layout_data *data = build_segmented_eaaudiocore_looping(sf, &eaac);
segmented_layout_data *data = build_segmented_eaaudiocore_looping(sf, sf, &eaac);
if (!data) goto fail;
vgmstream->layout_data = data;
vgmstream->coding_type = data->segments[0]->coding_type;
@ -1301,14 +1302,14 @@ static VGMSTREAM* init_vgmstream_eaaudiocore_header(STREAMFILE* sf_head, STREAMF
/* special (if hacky) loop handling, see comments */
if (eaac.loop_start > 0) {
segmented_layout_data *data = build_segmented_eaaudiocore_looping(sf, &eaac);
segmented_layout_data *data = build_segmented_eaaudiocore_looping(sf, sf, &eaac);
if (!data) goto fail;
vgmstream->layout_data = data;
vgmstream->coding_type = data->segments[0]->coding_type;
vgmstream->layout_type = layout_segmented;
}
else {
temp_sf = setup_eaac_audio_streamfile(sf, eaac.version, eaac.codec, eaac.streamed,0,0, 0x00);
temp_sf = setup_eaac_audio_streamfile(sf, eaac.version, eaac.codec, eaac.streamed,0,0, 0x00, 0);
if (!temp_sf) goto fail;
vgmstream->codec_data = init_mpeg_custom(temp_sf, 0x00, &vgmstream->coding_type, vgmstream->channels, type, &cfg);
@ -1336,7 +1337,7 @@ static VGMSTREAM* init_vgmstream_eaaudiocore_header(STREAMFILE* sf_head, STREAMF
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);
temp_sf = setup_eaac_audio_streamfile(sf, eaac.version, eaac.codec, eaac.streamed,0,0, 0x00, 0);
if (!temp_sf) goto fail;
break;
@ -1349,9 +1350,21 @@ static VGMSTREAM* init_vgmstream_eaaudiocore_header(STREAMFILE* sf_head, STREAMF
/* EATrax is "buffered" ATRAC9, uses custom IO since it's kind of complex to add to the decoder */
#if 0
/* For looped EATRAX, since we are using a deblocker SF no need to make segmented looping, though it works [Madden NFL 13 Demo (Vita)]
* An issue with segmented is that AT9 state is probably not reset between loops, which segmented can't simulate */
if (eaac.loop_start > 0) {
segmented_layout_data *data = build_segmented_eaaudiocore_looping(sf_head, sf, &eaac);
if (!data) goto fail;
vgmstream->layout_data = data;
vgmstream->coding_type = data->segments[0]->coding_type;
vgmstream->layout_type = layout_segmented;
}
#endif
cfg.channels = eaac.channels;
/* sub-header after normal header */
cfg.config_data = read_32bitBE(header_offset + header_size + 0x00,sf_head);
cfg.config_data = read_u32be(header_offset + header_size + 0x00,sf_head);
/* 0x04: data size without blocks, LE b/c why make sense (but don't use it in case of truncated files) */
/* 0x08: 16b frame size (same as config data) */
@ -1360,7 +1373,7 @@ static VGMSTREAM* init_vgmstream_eaaudiocore_header(STREAMFILE* sf_head, STREAMF
vgmstream->coding_type = coding_ATRAC9;
vgmstream->layout_type = layout_none;
temp_sf = setup_eaac_audio_streamfile(sf, eaac.version, eaac.codec, eaac.streamed,0,0, 0x00);
temp_sf = setup_eaac_audio_streamfile(sf, eaac.version, eaac.codec, eaac.streamed,0,0, 0x00, 0);
if (!temp_sf) goto fail;
break;
@ -1372,7 +1385,7 @@ static VGMSTREAM* init_vgmstream_eaaudiocore_header(STREAMFILE* sf_head, STREAMF
case EAAC_CODEC_EAMP3: { /* "EM30": EA-MP3 [Need for Speed 2015 (PS4), FIFA 2021 (PC)] */
mpeg_custom_config cfg = {0};
temp_sf = setup_eaac_audio_streamfile(sf, eaac.version, eaac.codec, eaac.streamed,0,0, 0x00);
temp_sf = setup_eaac_audio_streamfile(sf, eaac.version, eaac.codec, eaac.streamed,0,0, 0x00, 0);
if (!temp_sf) goto fail;
vgmstream->codec_data = init_mpeg_custom(temp_sf, 0x00, &vgmstream->coding_type, vgmstream->channels, MPEG_EAMP3, &cfg);
@ -1420,7 +1433,7 @@ static VGMSTREAM* init_vgmstream_eaaudiocore_header(STREAMFILE* sf_head, STREAMF
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 */
case 1: cfg.coupled_count = 0; break; /* 1ch, 1 stream [Madden 22 (PC)] */
default: goto fail;
}
}
@ -1431,7 +1444,7 @@ static VGMSTREAM* init_vgmstream_eaaudiocore_header(STREAMFILE* sf_head, STREAMF
/* 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);
//temp_sf = setup_eaac_audio_streamfile(sf_data, eaac->version, eaac->codec, eaac->streamed,0,0, 0x00, 0);
//if (!temp_sf) goto fail;
vgmstream->codec_data = init_ffmpeg_ea_opusm(sf, offset, data_size, &cfg);
@ -1613,10 +1626,12 @@ fail:
* We use the segmented layout, since the eaac_streamfile doesn't handle padding,
* and the segments seem fully separate (so even skipping would probably decode wrong). */
// todo reorganize code for more standard init
static segmented_layout_data* build_segmented_eaaudiocore_looping(STREAMFILE *sf_data, eaac_header *eaac) {
static segmented_layout_data* build_segmented_eaaudiocore_looping(STREAMFILE *sf_head, STREAMFILE *sf_data, eaac_header *eaac) {
segmented_layout_data *data = NULL;
STREAMFILE* temp_sf = NULL;
uint32_t data_size = get_streamfile_size(sf_data);
off_t offsets[2] = { 0x00, eaac->loop_offset };
uint32_t sizes[2] = { eaac->loop_offset, data_size - eaac->loop_offset };
off_t start_offset;
int num_samples[2] = { eaac->loop_start, eaac->num_samples - eaac->loop_start};
int segment_count = 2; /* intro/loop */
@ -1628,8 +1643,11 @@ static segmented_layout_data* build_segmented_eaaudiocore_looping(STREAMFILE *sf
if (!data) goto fail;
for (i = 0; i < segment_count; i++) {
data->segments[i] = allocate_vgmstream(eaac->channels, 0);
if (!data->segments[i]) goto fail;
VGMSTREAM* vgmstream = allocate_vgmstream(eaac->channels, 0);
if (!vgmstream) goto fail;
data->segments[i] = vgmstream;
data->segments[i]->sample_rate = eaac->sample_rate;
data->segments[i]->num_samples = num_samples[i];
//data->segments[i]->meta_type = eaac->meta_type; /* bleh */
@ -1669,7 +1687,7 @@ static segmented_layout_data* build_segmented_eaaudiocore_looping(STREAMFILE *sf
start_offset = 0x00; /* must point to the custom streamfile's beginning */
temp_sf = setup_eaac_audio_streamfile(sf_data, eaac->version,eaac->codec,eaac->streamed,0,0, offsets[i]);
temp_sf = setup_eaac_audio_streamfile(sf_data, eaac->version,eaac->codec,eaac->streamed,0,0, offsets[i], sizes[i]);
if (!temp_sf) goto fail;
data->segments[i]->codec_data = init_mpeg_custom(temp_sf, 0x00, &data->segments[i]->coding_type, eaac->channels, type, &cfg);
@ -1678,11 +1696,37 @@ static segmented_layout_data* build_segmented_eaaudiocore_looping(STREAMFILE *sf
break;
}
#endif
#ifdef VGM_USE_ATRAC9
case EAAC_CODEC_EATRAX: { /* EATrax (unknown FourCC) [Need for Speed: Most Wanted (Vita)] */
atrac9_config cfg = {0};
/* EATrax is "buffered" ATRAC9, uses custom IO since it's kind of complex to add to the decoder */
cfg.channels = eaac->channels;
/* sub-header after normal header */
cfg.config_data = read_u32be(0x14 + 0x00, sf_head); //todo pass header offset
/* 0x04: data size without blocks, LE b/c why make sense (but don't use it in case of truncated files) */
/* 0x08: 16b frame size (same as config data) */
vgmstream->codec_data = init_atrac9(&cfg);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_ATRAC9;
vgmstream->layout_type = layout_none;
start_offset = 0x00; /* must point to the custom streamfile's beginning */
//todo should make sizes
temp_sf = setup_eaac_audio_streamfile(sf_data, eaac->version, eaac->codec, eaac->streamed,0,0, offsets[i], sizes[i]);
if (!temp_sf) goto fail;
break;
}
#endif
default:
goto fail;
}
if (!vgmstream_open_stream(data->segments[i],temp_sf == NULL ? sf_data : temp_sf, start_offset))
if (!vgmstream_open_stream(data->segments[i], temp_sf == NULL ? sf_data : temp_sf, start_offset))
goto fail;
close_streamfile(temp_sf);
@ -1732,7 +1776,7 @@ static layered_layout_data* build_layered_eaaudiocore(STREAMFILE *sf_data, eaac_
size_t stream_size;
int is_xma1;
temp_sf = setup_eaac_audio_streamfile(sf_data, eaac->version, eaac->codec, eaac->streamed,i,layers, start_offset);
temp_sf = setup_eaac_audio_streamfile(sf_data, eaac->version, eaac->codec, eaac->streamed,i,layers, start_offset, 0);
if (!temp_sf) goto fail;
stream_size = get_streamfile_size(temp_sf);
@ -1764,7 +1808,7 @@ static layered_layout_data* build_layered_eaaudiocore(STREAMFILE *sf_data, eaac_
size_t data_size;
/* We'll remove EA blocks and pass raw data to FFmpeg Opus decoder */
temp_sf = setup_eaac_audio_streamfile(sf_data, eaac->version, eaac->codec, eaac->streamed,i,layers, start_offset);
temp_sf = setup_eaac_audio_streamfile(sf_data, eaac->version, eaac->codec, eaac->streamed,i,layers, start_offset, 0);
if (!temp_sf) goto fail;
skip = ea_opus_get_encoder_delay(0x00, temp_sf);

View File

@ -13,6 +13,7 @@ typedef struct {
int stream_number;
int stream_count;
off_t stream_offset;
uint32_t stream_size;
/* state */
off_t logical_offset; /* offset that corresponds to physical_offset */
@ -30,7 +31,7 @@ typedef struct {
/* Reads skipping EA's block headers, so the resulting data is smaller or larger than physical data.
* physical/logical_offset will be at the start of a block and only advance when a block is done */
static size_t eaac_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, eaac_io_data* data) {
static size_t eaac_io_read(STREAMFILE* sf, uint8_t *dest, off_t offset, size_t length, eaac_io_data* data) {
size_t total_read = 0;
/* ignore bad reads */
@ -58,8 +59,8 @@ static size_t eaac_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset,
/* process new block */
if (data->data_size == 0) {
data->block_flag = (uint8_t)read_8bit(data->physical_offset+0x00,streamfile);
data->block_size = read_32bitBE(data->physical_offset+0x00,streamfile) & 0x00FFFFFF;
data->block_flag = (uint8_t)read_8bit(data->physical_offset+0x00,sf);
data->block_size = read_32bitBE(data->physical_offset+0x00,sf) & 0x00FFFFFF;
/* ignore header block */
if (data->version == 1 && data->block_flag == 0x48) {
@ -74,9 +75,9 @@ static size_t eaac_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset,
data->skip_size = 0x04 + 0x04;
for (i = 0; i < data->stream_number; i++) {
data->skip_size += read_32bitBE(data->physical_offset+data->skip_size, streamfile) / 4;
data->skip_size += read_32bitBE(data->physical_offset+data->skip_size, sf) / 4;
}
data->data_size = read_32bitBE(data->physical_offset+data->skip_size, streamfile) / 4; /* why size*4...? */
data->data_size = read_32bitBE(data->physical_offset+data->skip_size, sf) / 4; /* why size*4...? */
data->skip_size += 0x04; /* skip mini header */
data->data_size -= 0x04; /* remove mini header */
if (data->data_size % XMA_FRAME_SIZE)
@ -96,7 +97,7 @@ static size_t eaac_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset,
case 0x0a: /* EATrax */
data->skip_size = 0x08;
data->data_size = read_32bitBE(data->physical_offset+0x04,streamfile); /* also block_size - 0x08 */
data->data_size = read_32bitBE(data->physical_offset+0x04,sf); /* also block_size - 0x08 */
break;
default:
@ -125,7 +126,7 @@ static size_t eaac_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset,
to_read = data->data_size - bytes_consumed;
if (to_read > length)
to_read = length;
bytes_done = read_streamfile(dest, data->physical_offset + data->skip_size + bytes_consumed, to_read, streamfile);
bytes_done = read_streamfile(dest, data->physical_offset + data->skip_size + bytes_consumed, to_read, sf);
}
else { /* offset falls within logical padded data */
to_read = data->data_size + data->extra_size - bytes_consumed;
@ -141,7 +142,7 @@ static size_t eaac_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset,
to_read = data->data_size - bytes_consumed;
if (to_read > length)
to_read = length;
bytes_done = read_streamfile(dest, data->physical_offset + data->skip_size + bytes_consumed, to_read, streamfile);
bytes_done = read_streamfile(dest, data->physical_offset + data->skip_size + bytes_consumed, to_read, sf);
break;
}
@ -168,7 +169,7 @@ static size_t eaac_io_size(STREAMFILE *streamfile, eaac_io_data* data) {
return data->logical_size;
physical_offset = data->stream_offset;
max_physical_offset = get_streamfile_size(streamfile);
max_physical_offset = physical_offset + data->stream_size;
/* get size of the logical stream */
while (physical_offset < max_physical_offset) {
@ -231,7 +232,7 @@ static size_t eaac_io_size(STREAMFILE *streamfile, eaac_io_data* data) {
}
/* logical size can be bigger in EA-XMA though */
if (physical_offset > get_streamfile_size(streamfile)) {
if (physical_offset > max_physical_offset) {
VGM_LOG("EA EAAC: wrong size\n");
return 0;
}
@ -247,10 +248,13 @@ static size_t eaac_io_size(STREAMFILE *streamfile, eaac_io_data* data) {
* - EATrax: ATRAC9 frames can be split between blooks
* - EAOpus: multiple Opus packets of frame size + Opus data per block
*/
static STREAMFILE* setup_eaac_audio_streamfile(STREAMFILE *sf, int version, int codec, int streamed, int stream_number, int stream_count, off_t stream_offset) {
static STREAMFILE* setup_eaac_audio_streamfile(STREAMFILE* sf, int version, int codec, int streamed, int stream_number, int stream_count, uint32_t stream_offset, uint32_t stream_size) {
STREAMFILE *new_sf = NULL;
eaac_io_data io_data = {0};
if (!stream_size)
stream_size = get_streamfile_size(sf) - stream_offset;
io_data.version = version;
io_data.codec = codec;
io_data.streamed = streamed;
@ -258,6 +262,7 @@ static STREAMFILE* setup_eaac_audio_streamfile(STREAMFILE *sf, int version, int
io_data.stream_count = stream_count;
io_data.stream_offset = stream_offset;
io_data.physical_offset = stream_offset;
io_data.stream_size = stream_size;
io_data.logical_size = eaac_io_size(sf, &io_data); /* force init */
/* setup subfile */

View File

@ -974,4 +974,6 @@ VGMSTREAM* init_vgmstream_wbk_nslb(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_ubi_ckd_cwav(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_mpeg(STREAMFILE* sf);
#endif /*_META_H*/

55
src/meta/mpeg.c Normal file
View File

@ -0,0 +1,55 @@
#include "meta.h"
#include "../coding/coding.h"
/* MPEG - standard MP1/2/3 audio MP3 */
VGMSTREAM* init_vgmstream_mpeg(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
int loop_flag = 0;
mpeg_frame_info info = {0};
/* checks */
if (!mpeg_get_frame_info(sf, 0x00, &info))
goto fail;
/* .mp3/mp2: standard (is .mp1 ever used in games?)
* .lmp1/2/3: for plugins
* .mus: Marc Ecko's Getting Up (PC) */
if (!check_extensions(sf, "mp3,mp2,mp1,mus,lmp3,lmp2,lmp1"))
goto fail;
loop_flag = 0;
/* build VGMSTREAM */
vgmstream = allocate_vgmstream(info.channels, loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_MPEG;
vgmstream->sample_rate = info.sample_rate;
#ifdef VGM_USE_MPEG
/* more strict, use? */
//mpeg_custom_config cfg = {0};
//cfg.skip_samples = ...
//vgmstream->codec_data = init_mpeg_custom(sf, start_offset, &vgmstream->coding_type, fmt.channels, MPEG_STANDARD, &cfg);
vgmstream->codec_data = init_mpeg(sf, 0x00, &vgmstream->coding_type, info.channels);
if (!vgmstream->codec_data) goto fail;
vgmstream->layout_type = layout_none;
#else
goto fail;
#endif
//vgmstream->num_samples = mpeg_bytes_to_samples(data_size, vgmstream->codec_data);
vgmstream->num_samples = mpeg_get_samples(sf, 0x00, get_streamfile_size(sf));
if (!vgmstream_open_stream(vgmstream, sf, 0x00))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -608,56 +608,80 @@ static int parse_musx(STREAMFILE* sf, musx_header* musx) {
case SBNK: {
off_t target_offset, head_offset, data_offset;
uint8_t codec;
uint8_t codec = 0;
uint32_t version;
/* 0x00: id */
/* 0x04: always 0x12? */
/* 0x08: file ID (same as base file) */
/* 0x0c: some ID? */
/* 0x10: table1 count */
/* 0x14: table1 offset (from here) */
/* 0x18: table2 count */
/* 0x1c: table2 offset (from here) */
/* 0x20: table3 count */
/* 0x24: table3 offset (from here) */
/* 0x28: table4 count */
/* 0x2c: table4 offset (from here) */
/* 0x30: table5 count (waves) */
/* 0x34: table5 offset (from here) */
/* 0x38: table6 count */
/* 0x3c: table6 offset (from here) */
/* 0x40: table7 count */
/* 0x44: table7 offset (from here) */
/* 0x48: data size */
/* 0x4c: data offset (absolute) */
musx->tables_offset = 0x800;
if (!is_id32be(musx->tables_offset+0x00, sf, "SBNK"))
if (!is_id32be(0x800 + 0x00, sf, "SBNK"))
goto fail;
musx->total_subsongs = read_u32(musx->tables_offset+0x30, sf);
version = read_u32(0x800 + 0x04, sf);
if (version == 0x2A) {
/* - Goldeneye 007 (X360) */
/* 0x08: "COM " */
/* 0x0c: file ID (same as base file) */
/* 0x10: some ID? */
musx->tables_offset = 0x814;
}
else {
/* - v0x12 (all others) */
/* 0x08: file ID (same as base file) */
/* 0x0c: some ID? */
musx->tables_offset = 0x810;
}
/* 0x00: table1 count */
/* 0x04: table1 offset (from here) */
/* 0x08: table2 count */
/* 0x0c: table2 offset (from here) */
/* 0x10: table3 count */
/* 0x14: table3 offset (from here) */
/* 0x18: table4 count */
/* 0x1c: table4 offset (from here) */
/* 0x20: table5 count (waves) */
/* 0x24: table5 offset (from here) */
/* 0x28: table6 count */
/* 0x2c: table6 offset (from here) */
/* 0x30: table7 count */
/* 0x34: table7 offset (from here) */
/* 0x38: data size */
/* 0x3c: data offset (absolute in older versions) */
musx->total_subsongs = read_u32(musx->tables_offset+0x20, sf);
if (target_subsong == 0) target_subsong = 1;
if (target_subsong > musx->total_subsongs || musx->total_subsongs <= 0) goto fail;
head_offset = read_u32(musx->tables_offset+0x34, sf) + musx->tables_offset + 0x34;
data_offset = read_u32(musx->tables_offset+0x4c, sf);
if (version == 0x2A) {
;VGM_LOG("MUSX: unknown version format\n");
goto fail;
/* subheader is 0x10 but variable?, offset table may be table 7?
- 0x00: ID?
- 0x04: samples?
- 0x08: sample rate
- 0x0a: codec?
- 0x0b: channels
- 0x0c: size?
*/
}
else {
head_offset = read_u32(musx->tables_offset+0x24, sf) + musx->tables_offset + 0x24;
data_offset = read_u32(musx->tables_offset+0x3c, sf);
target_offset = head_offset + (target_subsong - 1)*0x1c;
;VGM_LOG("MUSX: ho=%lx, do=%lx, to=%lx\n", head_offset, data_offset, target_offset);
target_offset = head_offset + (target_subsong - 1) * 0x1c;
;VGM_LOG("MUSX: ho=%lx, do=%lx, to=%lx\n", head_offset, data_offset, target_offset);
/* 0x00: subfile ID */
musx->num_samples = read_s32(target_offset + 0x04, sf);
musx->loop_start_sample = read_s32(target_offset + 0x08, sf); /* -1 if no loop */
musx->sample_rate = read_u16(target_offset + 0x0c, sf);
codec = read_u8 (target_offset + 0x0e, sf);
musx->channels = read_u8 (target_offset + 0x0f, sf);
musx->stream_offset = read_u32(target_offset + 0x10, sf) + data_offset;
musx->stream_size = read_u32(target_offset + 0x14, sf);
musx->loop_start = read_s32(target_offset + 0x18, sf);
/* 0x00: subfile ID */
musx->num_samples = read_s32(target_offset + 0x04, sf);
musx->loop_start_sample = read_s32(target_offset + 0x08, sf); /* -1 if no loop */
musx->sample_rate = read_u16(target_offset + 0x0c, sf);
codec = read_u8 (target_offset + 0x0e, sf);
musx->channels = read_u8 (target_offset + 0x0f, sf);
musx->stream_offset = read_u32(target_offset + 0x10, sf) + data_offset;
musx->stream_size = read_u32(target_offset + 0x14, sf);
musx->loop_start = read_s32(target_offset + 0x18, sf);
}
musx->loop_end_sample = musx->num_samples;
musx->loop_flag = (musx->loop_start_sample >= 0);

View File

@ -150,6 +150,9 @@ static int read_fmt(int big_endian, STREAMFILE* sf, off_t offset, riff_fmt_chunk
break;
case 16:
fmt->coding_type = big_endian ? coding_PCM16BE : coding_PCM16LE;
/* broken block size [Rayman 2 (DC)] */
if (fmt->block_size == 0x02 && fmt->channels > 1)
fmt->block_size = 0x02 * fmt->channels;
break;
case 8:
fmt->coding_type = coding_PCM8_U;

View File

@ -1062,6 +1062,7 @@ static int parse_keyval(STREAMFILE* sf_, txth_header* txth, const char* key, cha
/* apply */
if (!txth->data_size_set) {
//TODO: this doesn't work when using name_table + subsongs, since values are pre-read
/* with subsongs we want to clamp data_size from this subsong start to next subsong start */
txth->next_offset = txth->data_size;
if (txth->subsong_count > 1 && txth->target_subsong < txth->subsong_count) {
@ -1919,6 +1920,7 @@ static int parse_num(STREAMFILE* sf, txth_header* txth, const char* val, uint32_
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;
else if ((n = is_string_field(val,"data_size"))) value = txth->data_size;
else if ((n = is_string_field(val,"padding_size"))) value = txth->padding_size;
else if ((n = is_string_field(val,"num_samples"))) value = txth->num_samples;
else if ((n = is_string_field(val,"loop_start_sample"))) value = txth->loop_start_sample;
else if ((n = is_string_field(val,"loop_start"))) value = txth->loop_start_sample;
@ -1932,7 +1934,16 @@ static int parse_num(STREAMFILE* sf, txth_header* txth, const char* val, uint32_
else if ((n = is_string_field(val,"subfile_size"))) value = txth->subfile_size;
else if ((n = is_string_field(val,"base_offset"))) value = txth->base_offset;
else if ((n = is_string_field(val,"coef_offset"))) value = txth->coef_offset;
else if ((n = is_string_field(val,"coef_spacing"))) value = txth->coef_spacing;
else if ((n = is_string_field(val,"hist_offset"))) value = txth->hist_offset;
else if ((n = is_string_field(val,"hist_spacing"))) value = txth->hist_spacing;
else if ((n = is_string_field(val,"chunk_count"))) value = txth->chunk_count;
else if ((n = is_string_field(val,"chunk_start"))) value = txth->chunk_start;
else if ((n = is_string_field(val,"chunk_size"))) value = txth->chunk_size;
else if ((n = is_string_field(val,"chunk_size_offset"))) value = txth->chunk_size_offset;
else if ((n = is_string_field(val,"chunk_number"))) value = txth->chunk_number;
else if ((n = is_string_field(val,"chunk_data_size"))) value = txth->chunk_data_size;
else if ((n = is_string_field(val,"chunk_header_size"))) value = txth->chunk_header_size;
//todo whatever, improve
else if ((n = is_string_field(val,"name_value"))) value = txth->name_values[0];
else if ((n = is_string_field(val,"name_value1"))) value = txth->name_values[0];

View File

@ -521,6 +521,7 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
init_vgmstream_ubi_ckd_cwav,
/* lower priority metas (no clean header identity, somewhat ambiguous, or need extension/companion file to identify) */
init_vgmstream_mpeg,
init_vgmstream_agsc,
init_vgmstream_dtk,
init_vgmstream_rsf,

View File

@ -753,6 +753,7 @@ typedef enum {
meta_WBK,
meta_WBK_NSLB,
meta_DSP_APEX,
meta_MPEG,
} meta_t;

View File

@ -75,7 +75,7 @@ typedef struct _XMPLAY_STREAMFILE {
static STREAMFILE* open_xmplay_streamfile_by_xmpfile(XMPFILE file, const char* path, int internal);
static size_t xmpsf_read(XMPLAY_STREAMFILE* sf, uint8_t* dest, off_t offset, size_t length) {
static size_t xmpsf_read(XMPLAY_STREAMFILE* sf, uint8_t* dst, offv_t offset, size_t length) {
size_t read;
if (sf->offset != offset) {
@ -85,7 +85,7 @@ static size_t xmpsf_read(XMPLAY_STREAMFILE* sf, uint8_t* dest, off_t offset, siz
sf->offset = xmpffile->Tell(sf->infile);
}
read = xmpffile->Read(sf->infile, dest, length);
read = xmpffile->Read(sf->infile, dst, length);
if (read > 0)
sf->offset += read;
@ -105,7 +105,7 @@ static void xmpsf_get_name(XMPLAY_STREAMFILE* sf, char* buffer, size_t length) {
buffer[length-1] = '\0';
}
static STREAMFILE *xmpsf_open(XMPLAY_STREAMFILE* sf, const char* const filename, size_t buffersize) {
static STREAMFILE* xmpsf_open(XMPLAY_STREAMFILE* sf, const char* const filename, size_t buffersize) {
XMPFILE newfile;
if (!filename)
@ -130,30 +130,30 @@ static void xmpsf_close(XMPLAY_STREAMFILE* sf) {
free(sf);
}
static STREAMFILE *open_xmplay_streamfile_by_xmpfile(XMPFILE infile, const char* path, int internal) {
XMPLAY_STREAMFILE* sf = calloc(1, sizeof(XMPLAY_STREAMFILE));
if (!sf) return NULL;
static STREAMFILE* open_xmplay_streamfile_by_xmpfile(XMPFILE infile, const char* path, int internal) {
XMPLAY_STREAMFILE* this_sf = calloc(1, sizeof(XMPLAY_STREAMFILE));
if (!this_sf) return NULL;
sf->sf.read = (void*)xmpsf_read;
sf->sf.get_size = (void*)xmpsf_get_size;
sf->sf.get_offset = (void*)xmpsf_get_offset;
sf->sf.get_name = (void*)xmpsf_get_name;
sf->sf.open = (void*)xmpsf_open;
sf->sf.close = (void*)xmpsf_close;
sf->infile = infile;
sf->offset = 0;
strncpy(sf->name, path, sizeof(sf->name));
this_sf->sf.read = (void*)xmpsf_read;
this_sf->sf.get_size = (void*)xmpsf_get_size;
this_sf->sf.get_offset = (void*)xmpsf_get_offset;
this_sf->sf.get_name = (void*)xmpsf_get_name;
this_sf->sf.open = (void*)xmpsf_open;
this_sf->sf.close = (void*)xmpsf_close;
this_sf->infile = infile;
this_sf->offset = 0;
strncpy(this_sf->name, path, sizeof(this_sf->name));
sf->internal_xmpfile = internal;
this_sf->internal_xmpfile = internal;
return &sf->sf; /* pointer to STREAMFILE start = rest of the custom data follows */
return &this_sf->sf; /* pointer to STREAMFILE start = rest of the custom data follows */
}
VGMSTREAM* init_vgmstream_xmplay(XMPFILE file, const char *path, int subsong) {
VGMSTREAM* init_vgmstream_xmplay(XMPFILE infile, const char* path, int subsong) {
STREAMFILE* sf = NULL;
VGMSTREAM* vgmstream = NULL;
sf = open_xmplay_streamfile_by_xmpfile(file, path, 0); /* external XMPFILE */
sf = open_xmplay_streamfile_by_xmpfile(infile, path, 0); /* external XMPFILE */
if (!sf) return NULL;
sf->stream_index = subsong;