mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-02-17 19:19:16 +01:00
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:
commit
27f80241b8
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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_ */
|
||||
|
@ -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) {
|
||||
|
@ -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" />
|
||||
|
@ -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>
|
||||
|
@ -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);
|
||||
|
@ -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 */
|
||||
|
@ -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
55
src/meta/mpeg.c
Normal 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;
|
||||
}
|
108
src/meta/musx.c
108
src/meta/musx.c
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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];
|
||||
|
@ -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,
|
||||
|
@ -753,6 +753,7 @@ typedef enum {
|
||||
meta_WBK,
|
||||
meta_WBK_NSLB,
|
||||
meta_DSP_APEX,
|
||||
meta_MPEG,
|
||||
|
||||
} meta_t;
|
||||
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user