From e5d4e2e382245c1eb05b72d6fac48d1601c360ea Mon Sep 17 00:00:00 2001 From: bnnm Date: Mon, 11 Mar 2019 11:58:57 +0100 Subject: [PATCH] Remove MTAF deblocking code and cleanup --- src/coding/coding.h | 4 +- src/coding/mta2_decoder.c | 97 +++++-------------- src/coding/mtaf_decoder.c | 53 +---------- src/formats.c | 4 +- src/libvgmstream.vcproj | 4 + src/libvgmstream.vcxproj | 1 + src/libvgmstream.vcxproj.filters | 3 + src/meta/meta.h | 5 +- src/meta/mta2_streamfile.h | 154 +++++++++++++++++++++++++++++++ src/meta/ps2_mtaf.c | 33 ++----- src/meta/ps3_mta2.c | 105 +++++++++++++-------- src/vgmstream.c | 8 +- src/vgmstream.h | 4 +- 13 files changed, 274 insertions(+), 201 deletions(-) create mode 100644 src/meta/mta2_streamfile.h diff --git a/src/coding/coding.h b/src/coding/coding.h index de66c053..7868547d 100644 --- a/src/coding/coding.h +++ b/src/coding/coding.h @@ -148,10 +148,10 @@ void decode_sassc(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing void decode_lsf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); /* mtaf_decoder */ -void decode_mtaf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_mtaf(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); /* mta2_decoder */ -void decode_mta2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_mta2(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); /* mc3_decoder */ void decode_mc3(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); diff --git a/src/coding/mta2_decoder.c b/src/coding/mta2_decoder.c index 2ecbb327..06480c6d 100644 --- a/src/coding/mta2_decoder.c +++ b/src/coding/mta2_decoder.c @@ -1,7 +1,7 @@ #include "coding.h" #include "../util.h" -/* MTA2 (EA XAS variant?) decoder based on: +/* MTA2 decoder based on: * - MGS Developer Wiki: https://www.mgsdevwiki.com/wiki/index.php/MTA2_(Codec) [codec by daemon1] * - Solid4 tools: https://github.com/GHzGangster/Drebin * @@ -11,23 +11,21 @@ * * up to 16 possible tracks, but max seen is 3 (ex. track0=sneaking, track1=action, track2=ambience) * - each ch frame is divided into 4 headers + 4 vertical groups with nibbles (0x4*4 + 0x20*4) * ex. group1 is 0x04(4) + 0x14(4) + 0x24(4) + 0x34(4) ... (vertically maybe for paralelism?) - * - in case of "macroblock" layout, there are also headers before N tracks (like other MGS games) * * Due to this vertical layout and multiple hist/indexes, it decodes everything in a block between calls * but discards unwanted data, instead of trying to skip to the target nibble. Meaning no need to save hist, and * expects samples_to_do to be block_samples at most (could be simplified, I guess). - * - * Because of how the macroblock/track and stream's offset per channel work, they are supported by - * autodetecting and skipping when needed (ideally should keep a special layout/count, but this is simpler). */ -static const int c1[8] = { /* mod table 1 */ +/* coefs table (extended XA filters) */ +static const int mta2_coefs1[8] = { 0, 240, 460, 392, 488, 460, 460, 240 }; -static const int c2[8] = { /* mod table 2 */ +static const int mta2_coefs2[8] = { 0, 0, -208, -220, -240, -240, -220, -104 }; -static const int c3[32] = { /* shift table */ +/* shift table */ +static const int mta2_shifts[32] = { 256, 335, 438, 573, 749, 979, 1281, 1675, 2190, 2864, 3746, 4898, 6406, 8377, 10955, 14327, 18736, 24503, 32043, 41905, 54802, 71668, 93724, 122568, @@ -35,80 +33,31 @@ static const int c3[32] = { /* shift table */ }; /* expands nibble */ -static short calculate_output(int nibble, short smp1, short smp2, int mod, int sh) { +static short mta2_expand_nibble(int nibble, short hist1, short hist2, int coef_index, int shift_index) { int output; if (nibble > 7) /* sign extend */ nibble = nibble - 16; - output = (smp1 * c1[mod] + smp2 * c2[mod] + (nibble * c3[sh]) + 128) >> 8; + output = (hist1 * mta2_coefs1[coef_index] + hist2 * mta2_coefs2[coef_index] + (nibble * mta2_shifts[shift_index]) + 128) >> 8; output = clamp16(output); return (short)output; } - -/* autodetect and skip "macroblocks" */ -static void mta2_block_update(VGMSTREAMCHANNEL * stream) { - int block_type, block_size, block_tracks, repeat = 1; - - /* may need to skip N empty blocks */ - do { - block_type = read_32bitBE(stream->offset + 0x00, stream->streamfile); - block_size = read_32bitBE(stream->offset + 0x04, stream->streamfile); /* including this header */ - /* 0x08: always null */ - block_tracks = read_32bitBE(stream->offset + 0x0c, stream->streamfile); /* total tracks of variable size (can be 0) */ - - /* 0x10001: music, 0x20001: sfx?, 0xf0: loop control (goes at the end) */ - if (block_type != 0x00010001 && block_type != 0x00020001 && block_type != 0x000000F0) - return; /* not a block */ - - /* frame=010001+00/etc can be mistaken as block_type, do extra checks */ - { - int i, track_channels = 0; - uint16_t channel_layout = (block_size >> 16); - uint16_t track_size = (block_size & 0xFFFF); - - /* has chanel layout == may be a track */ - if (channel_layout > 0 && channel_layout <= 0xFF) { - for (i = 0; i < 8; i++) { - if ((channel_layout >> i) & 0x01) - track_channels++; - } - if (track_channels*0x90 == track_size) - return; - } - } - - if (block_size <= 0 || block_tracks < 0) { /* nonsense block (maybe at EOF) */ - VGM_LOG("MTA2: bad block @ 0x%x\n", (uint32_t)stream->offset); - stream->offset += 0x10; - repeat = 0; - } - else if (block_tracks == 0) { /* empty block (common), keep repeating */ - stream->offset += block_size; - } - else { /* normal block, position into next track header */ - stream->offset += 0x10; - repeat = 0; - } - } while (repeat); -} - -/* decodes a block for a channel, skipping macroblocks/tracks if needed */ -void decode_mta2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { +/* decodes a block for a channel */ +void decode_mta2(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { int samples_done = 0, sample_count = 0, channel_block_samples, channel_first_sample, frame_size = 0; int i, group, row, col; int track_channels = 0, track_channel; - /* block/track skip */ + /* track skip */ do { int num_track = 0, channel_layout; - /* autodetect and skip macroblock header */ - mta2_block_update(stream); /* parse track header (0x10) and skip tracks that our current channel doesn't belong to */ num_track = read_8bit(stream->offset+0x00,stream->streamfile); /* 0=first */ - /* 0x01(3): num_frame (0=first), 0x04(1): 0? */ + /* 0x01(3): num_frame (0=first) */ + /* 0x04(1): 0? */ channel_layout = read_8bit(stream->offset+0x05,stream->streamfile); /* bitmask, see mta2.c */ frame_size = read_16bitBE(stream->offset+0x06,stream->streamfile); /* not including this header */ /* 0x08(8): null */ @@ -151,22 +100,22 @@ void decode_mta2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, /* parse channel frame (header 0x04*4 + data 0x20*4) */ for (group = 0; group < 4; group++) { - short smp2, smp1, mod, sh, output; + short hist2, hist1, coefs, shift, output; int group_header = read_32bitBE(stream->offset + 0x10 + track_channel*0x90 + group*0x4, stream->streamfile); - smp2 = (short) ((group_header >> 16) & 0xfff0); /* upper 16b discarding 4b */ - smp1 = (short) ((group_header >> 4) & 0xfff0); /* lower 16b discarding 4b */ - mod = (group_header >> 5) & 0x7; /* mid 3b */ - sh = group_header & 0x1f; /* lower 5b */ + hist2 = (short) ((group_header >> 16) & 0xfff0); /* upper 16b discarding 4b */ + hist1 = (short) ((group_header >> 4) & 0xfff0); /* lower 16b discarding 4b */ + coefs = (group_header >> 5) & 0x7; /* mid 3b */ + shift = group_header & 0x1f; /* lower 5b */ /* write header samples (skips the last 2 group nibbles), like Drebin's decoder * last 2 nibbles and next 2 header hist should match though */ if (sample_count >= channel_first_sample && samples_done < samples_to_do) { - outbuf[samples_done * channelspacing] = smp2; + outbuf[samples_done * channelspacing] = hist2; samples_done++; } sample_count++; if (sample_count >= channel_first_sample && samples_done < samples_to_do) { - outbuf[samples_done * channelspacing] = smp1; + outbuf[samples_done * channelspacing] = hist1; samples_done++; } sample_count++; @@ -175,7 +124,7 @@ void decode_mta2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, for (col = 0; col < 4*2; col++) { uint8_t nibbles = read_8bit(stream->offset + 0x10 + 0x10 + track_channel*0x90 + group*0x4 + row*0x10 + col/2, stream->streamfile); int nibble_shift = (!(col&1) ? 4 : 0); /* upper first */ - output = calculate_output((nibbles >> nibble_shift) & 0xf, smp1, smp2, mod, sh); + output = mta2_expand_nibble((nibbles >> nibble_shift) & 0xf, hist1, hist2, coefs, shift); /* ignore last 2 nibbles (uses first 2 header samples) */ if (row < 7 || col < 3*2) { @@ -186,8 +135,8 @@ void decode_mta2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, sample_count++; } - smp2 = smp1; - smp1 = output; + hist2 = hist1; + hist1 = output; } } } diff --git a/src/coding/mtaf_decoder.c b/src/coding/mtaf_decoder.c index e2d0b182..bc07dfcf 100644 --- a/src/coding/mtaf_decoder.c +++ b/src/coding/mtaf_decoder.c @@ -1,15 +1,11 @@ #include "coding.h" #include "../util.h" -#define MTAF_BLOCK_SUPPORT - /* A hybrid of IMA and Yamaha ADPCM found in Metal Gear Solid 3 * Thanks to X_Tra (http://metalgear.in/) for pointing me to the step size table. * - * Layout: N tracks of 0x10 header + 0x80*2 (always 2ch; multichannels uses 4ch = 2ch track0 + 2ch track1 xN) - * "macroblocks" support is not really needed as the extractors should remove them but they are - * autodetected and skipped if found (ideally should keep a special layout/count, but this is simpler). + * Layout: N tracks of 0x10 header + 0x80*2 (always 2ch; multichannels uses 4ch = 2ch track0 + 2ch track1 xN). */ static const int index_table[16] = { @@ -84,48 +80,8 @@ static const int16_t step_size[32][16] = { -424, -1273, -2121, -2970, -3819, -4668, -5516, -6365, }, }; -#ifdef MTAF_BLOCK_SUPPORT -/* autodetect and skip "macroblocks" */ -static void mtaf_block_update(VGMSTREAMCHANNEL * stream) { - int block_type, block_size, block_empty, block_tracks, repeat = 1; - do { - block_type = read_32bitLE(stream->offset+0x00, stream->streamfile); - block_size = read_32bitLE(stream->offset+0x04, stream->streamfile); /* including this header */ - block_empty = read_32bitLE(stream->offset+0x08, stream->streamfile); /* always 0 */ - block_tracks = read_32bitLE(stream->offset+0x0c, stream->streamfile); /* total tracks of 0x110 (can be 0)*/ - - /* 0x110001: music (type 0x11=adpcm), 0xf0: loop control (goes at the end) */ - if ((block_type != 0x00110001 && block_type != 0x000000F0) || block_empty != 0) - return; /* not a block */ - - /* track=001100+01 could be mistaken as block_type, do extra checks */ - { - int track = read_8bit(stream->offset+0x10, stream->streamfile); - if (track != 0 && track != 1) - return; /* if this is a block, next header should be from track 0/1 */ - if (block_tracks > 0 && (block_size-0x10) != block_tracks*0x110) - return; /* wrong expected size */ - } - - if (block_size <= 0 || block_tracks < 0) { /* nonsense block (maybe at EOF) */ - VGM_LOG("MTAF: bad block @ %x\n", (uint32_t)stream->offset); - stream->offset += 0x10; - repeat = 0; - } - else if (block_tracks == 0) { /* empty block (common), keep repeating */ - stream->offset += block_size; - } - else { /* normal block, position into next track header */ - stream->offset += 0x10; - repeat = 0; - } - - } while(repeat); -} -#endif - -void decode_mtaf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { +void decode_mtaf(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { int32_t sample_count; int i; int c = channel%2; /* global channel to track channel */ @@ -133,11 +89,6 @@ void decode_mtaf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t step_idx = stream->adpcm_step_index; - #ifdef MTAF_BLOCK_SUPPORT - /* autodetect and skip macroblock header */ - mtaf_block_update(stream); - #endif - /* read header when we hit a new track every 0x100 samples */ first_sample = first_sample % 0x100; diff --git a/src/formats.c b/src/formats.c index 047ecb37..1a59bc9b 100644 --- a/src/formats.c +++ b/src/formats.c @@ -1032,7 +1032,7 @@ static const meta_info meta_info_list[] = { {meta_OTNS_ADP, "Omikron: The Nomad Soul ADP header"}, {meta_EB_SFX, "Excitebots .sfx header"}, {meta_EB_SF0, "assumed Excitebots .sf0 by extension"}, - {meta_PS2_MTAF, "Konami MTAF header"}, + {meta_MTAF, "Konami MTAF header"}, {meta_PS2_VAG1, "Konami VAG1 header"}, {meta_PS2_VAG2, "Konami VAG2 header"}, {meta_TUN, "Lego Racers ALP header"}, @@ -1073,7 +1073,7 @@ static const meta_info meta_info_list[] = { {meta_TA_AAC_X360, "tri-Ace AAC (X360) header"}, {meta_TA_AAC_PS3, "tri-Ace AAC (PS3) header"}, {meta_TA_AAC_MOBILE, "tri-Ace AAC (Mobile) header"}, - {meta_PS3_MTA2, "Konami MTA2 header"}, + {meta_MTA2, "Konami MTA2 header"}, {meta_NGC_ULW, "Criterion ULW raw header"}, {meta_PC_XA30, "Reflections XA30 PC header"}, {meta_WII_04SW, "Reflections 04SW header"}, diff --git a/src/libvgmstream.vcproj b/src/libvgmstream.vcproj index 1f376012..ffbb4a90 100644 --- a/src/libvgmstream.vcproj +++ b/src/libvgmstream.vcproj @@ -256,6 +256,10 @@ RelativePath=".\meta\kma9_streamfile.h" > + + diff --git a/src/libvgmstream.vcxproj b/src/libvgmstream.vcxproj index da59bdce..bdb9a9be 100644 --- a/src/libvgmstream.vcxproj +++ b/src/libvgmstream.vcxproj @@ -108,6 +108,7 @@ + diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters index 9aa2053f..02a09a5a 100644 --- a/src/libvgmstream.vcxproj.filters +++ b/src/libvgmstream.vcxproj.filters @@ -92,6 +92,9 @@ meta\Header Files + + meta\Header Files + meta\Header Files diff --git a/src/meta/meta.h b/src/meta/meta.h index 9cb2acb2..49666621 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -551,7 +551,7 @@ VGMSTREAM * init_vgmstream_pc_adp_otns(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_eb_sfx(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_eb_sf0(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_ps2_mtaf(STREAMFILE* streamFile); +VGMSTREAM * init_vgmstream_mtaf(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_tun(STREAMFILE* streamFile); @@ -628,7 +628,8 @@ VGMSTREAM * init_vgmstream_ta_aac_vita(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_va3(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_ps3_mta2(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_mta2(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_mta2_container(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ngc_ulw(STREAMFILE * streamFile); diff --git a/src/meta/mta2_streamfile.h b/src/meta/mta2_streamfile.h new file mode 100644 index 00000000..c38d20b5 --- /dev/null +++ b/src/meta/mta2_streamfile.h @@ -0,0 +1,154 @@ +#ifndef _MTA2_STREAMFILE_H_ +#define _MTA2_STREAMFILE_H_ +#include "../streamfile.h" + +typedef struct { + /* config */ + int big_endian; + uint32_t target_type; + off_t stream_offset; + size_t stream_size; + + /* state */ + off_t logical_offset; /* fake offset */ + off_t physical_offset; /* actual offset */ + size_t block_size; /* current size */ + size_t skip_size; /* size from block start to reach data */ + size_t data_size; /* usable size in a block */ + + size_t logical_size; +} mta2_io_data; + + +static size_t mta2_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, mta2_io_data* data) { + size_t total_read = 0; + uint32_t (*read_u32)(off_t,STREAMFILE*) = data->big_endian ? read_u32be : read_u32le; + + + + /* re-start when previous offset (can't map logical<>physical offsets) */ + if (data->logical_offset < 0 || offset < data->logical_offset) { + data->physical_offset = data->stream_offset; + data->logical_offset = 0x00; + data->data_size = 0; + } + + /* read blocks */ + while (length > 0) { + + /* ignore EOF */ + if (offset < 0 || data->physical_offset >= data->stream_offset + data->stream_size) { + break; + } + + /* process new block */ + if (data->data_size == 0) { + uint32_t block_type, block_size, block_track; + + block_type = read_u32(data->physical_offset+0x00, streamfile); /* subtype and type */ + block_size = read_u32(data->physical_offset+0x04, streamfile); + //block_unk = read_u32(data->physical_offset+0x08, streamfile); /* usually 0 except for 0xF0 'end' block */ + block_track = read_u32(data->physical_offset+0x0c, streamfile); + + if (block_type != data->target_type || block_size == 0xFFFFFFFF) + break; + + data->block_size = block_size; + data->skip_size = 0x10; + data->data_size = block_size - data->skip_size; + /* no audio data (padding block), but write first (header) */ + if (block_track == 0 && data->logical_offset > 0) + data->data_size = 0; + } + + /* move to next block */ + if (data->data_size == 0 || offset >= data->logical_offset + data->data_size) { + data->physical_offset += data->block_size; + data->logical_offset += data->data_size; + data->data_size = 0; + continue; + } + + /* read data */ + { + size_t bytes_consumed, bytes_done, to_read; + + bytes_consumed = offset - data->logical_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); + + total_read += bytes_done; + dest += bytes_done; + offset += bytes_done; + length -= bytes_done; + + if (bytes_done != to_read || bytes_done == 0) { + break; /* error/EOF */ + } + } + } + + return total_read; +} + +static size_t mta2_io_size(STREAMFILE *streamfile, mta2_io_data* data) { + uint8_t buf[1]; + + if (data->logical_size) + return data->logical_size; + + /* force a fake read at max offset, to get max logical_offset (will be reset next read) */ + mta2_io_read(streamfile, buf, 0x7FFFFFFF, 1, data); + data->logical_size = data->logical_offset; + + return data->logical_size; +} + +/* Handles removing KCE Japan-style blocks in MTA2 streams + * (these blocks exist in most KCEJ games and aren't actually related to audio) */ +static STREAMFILE* setup_mta2_streamfile(STREAMFILE *streamFile, off_t stream_offset, int big_endian, const char* extension) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + mta2_io_data io_data = {0}; + size_t io_data_size = sizeof(mta2_io_data); + uint32_t (*read_u32)(off_t,STREAMFILE*) = big_endian ? read_u32be : read_u32le; + + + /* blocks must start with a 'new sub-stream' id */ + if (read_u32(stream_offset+0x00, streamFile) != 0x00000010) + goto fail; + + io_data.target_type = read_u32(stream_offset + 0x0c, streamFile); + io_data.stream_offset = stream_offset + 0x10; + io_data.stream_size = get_streamfile_size(streamFile) - io_data.stream_offset; + io_data.big_endian = big_endian; + io_data.logical_offset = -1; /* force phys offset reset */ + + /* setup subfile */ + new_streamFile = open_wrap_streamfile(streamFile); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_io_streamfile(new_streamFile, &io_data,io_data_size, mta2_io_read,mta2_io_size); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_buffer_streamfile(new_streamFile,0); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + if (extension) { + new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,extension); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + } + + return temp_streamFile; + +fail: + close_streamfile(temp_streamFile); + return NULL; +} + +#endif /* _MTA2_STREAMFILE_H_ */ diff --git a/src/meta/ps2_mtaf.c b/src/meta/ps2_mtaf.c index e760399b..b061c5e2 100644 --- a/src/meta/ps2_mtaf.c +++ b/src/meta/ps2_mtaf.c @@ -2,19 +2,17 @@ #include "../util.h" -/* MTAF - found in Metal Gear Solid 3: Snake Eater (Subsistence and HD too) */ -VGMSTREAM * init_vgmstream_ps2_mtaf(STREAMFILE *streamFile) { +/* MTAF - found in Metal Gear Solid 3: Snake Eater (PS2), Subsistence and HD too */ +VGMSTREAM * init_vgmstream_mtaf(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; off_t start_offset; int loop_flag, channel_count; int32_t loop_start, loop_end; - /* check extension */ + /* checks */ if ( !check_extensions(streamFile,"mtaf")) goto fail; - - /* base header */ if (read_32bitBE(0x00, streamFile) != 0x4d544146) /* "MTAF" */ goto fail; /* 0x04(4): pseudo file size (close but smaller) */ @@ -51,12 +49,9 @@ VGMSTREAM * init_vgmstream_ps2_mtaf(STREAMFILE *streamFile) { goto fail; /* 0x7fc: data size (without blocks in case of blocked layout) */ - /* without blocks it should start with 0x00000100 ("frame 1 from track 0") */ - //is_blocked = read_32bitLE(0x800,streamFile) != 0x00000100 && read_32bitLE(0x810,streamFile) == 0x00000100; - - start_offset = 0x800; + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count, loop_flag); if (!vgmstream) goto fail; @@ -68,23 +63,11 @@ VGMSTREAM * init_vgmstream_ps2_mtaf(STREAMFILE *streamFile) { vgmstream->coding_type = coding_MTAF; vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x110/2; /* kinda hacky for MTAF track layout */ - vgmstream->meta_type = meta_PS2_MTAF; + vgmstream->interleave_block_size = 0x110 / 2; /* kinda hacky for MTAF (stereo codec) track layout */ + vgmstream->meta_type = meta_MTAF; - - /* open the file for reading, in a specific way */ - { - int i; - char filename[PATH_LIMIT]; - - streamFile->get_name(streamFile,filename,sizeof(filename)); - for (i = 0; i < channel_count; i++) { - STREAMFILE * file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - vgmstream->ch[i].streamfile = file; - vgmstream->ch[i].channel_start_offset = vgmstream->ch[i].offset = start_offset + vgmstream->interleave_block_size*2*(i/2); - } - } + if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) + goto fail; return vgmstream; diff --git a/src/meta/ps3_mta2.c b/src/meta/ps3_mta2.c index 83825f81..2139d705 100644 --- a/src/meta/ps3_mta2.c +++ b/src/meta/ps3_mta2.c @@ -1,68 +1,54 @@ #include "meta.h" -#include "../util.h" +#include "mta2_streamfile.h" -/* MTA2 - found in Metal Gear Solid 4 */ -VGMSTREAM * init_vgmstream_ps3_mta2(STREAMFILE *streamFile) { +/* MTA2 - found in Metal Gear Solid 4 (PS3) */ +VGMSTREAM * init_vgmstream_mta2(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - off_t header_offset, start_offset; - int loop_flag, channel_count, sample_rate; //block_offset; + off_t start_offset; + int loop_flag, channel_count, sample_rate; int32_t loop_start, loop_end; + uint32_t sample_rate_int; - /* check extension */ - /* .mta2: normal file, .bgm: mta2 with block layout, .dbm: iPod metadata + block layout mta2 */ - if ( !check_extensions(streamFile,"mta2,bgm,dbm")) + /* checks */ + if ( !check_extensions(streamFile,"mta2")) + goto fail; + + if (read_32bitBE(0x00,streamFile) != 0x4d544132) /* "MTA2" */ goto fail; /* base header (everything is very similar to MGS3's MTAF but BE) */ - if (read_32bitBE(0x00,streamFile) == 0x4d544132) { /* "MTA2" (.mta) */ - //block_offset = 0; - header_offset = 0x00; - } else if (read_32bitBE(0x20,streamFile) == 0x4d544132) { /* "MTA2" @ 0x20 (.bgm) */ - //block_offset = 0x10; - header_offset = 0x20; - } else if (read_32bitBE(0x00, streamFile) == 0x444C424D - && read_32bitBE(0x820,streamFile) == 0x4d544132) { /* "DLBM" + "MTA2" @ 0x820 (.dbm) */ - //block_offset = 0x810; - header_offset = 0x820; - } else { - goto fail; - } - /* 0x04(4): file size -4-4 (not including block headers in case of block layout) */ + /* 0x04(4): file size -4-4 (not including block headers in case of KCEJ blocks) */ /* 0x08(4): version? (1), 0x0c(52): null */ - /* HEAD chunk */ - if (read_32bitBE(header_offset+0x40, streamFile) != 0x48454144) /* "HEAD" */ + if (read_32bitBE(0x40, streamFile) != 0x48454144) /* "HEAD" */ goto fail; - if (read_32bitBE(header_offset+0x44, streamFile) != 0xB0) /* HEAD size */ + if (read_32bitBE(0x44, streamFile) != 0xB0) /* HEAD size */ goto fail; - - /* 0x48(4): null, 0x4c: ? (0x10), 0x50(4): 0x7F (vol?), 0x54(2): 0x40 (pan?) */ - channel_count = read_16bitBE(header_offset+0x56, streamFile); /* counting all tracks */ + channel_count = read_16bitBE(0x56, streamFile); /* counting all tracks */ /* 0x60(4): full block size (0x110 * channels), indirectly channels_per_track = channels / (block_size / 0x110) */ /* 0x80 .. 0xf8: null */ - loop_start = read_32bitBE(header_offset+0x58, streamFile); - loop_end = read_32bitBE(header_offset+0x5c, streamFile); + loop_start = read_32bitBE(0x58, streamFile); + loop_end = read_32bitBE(0x5c, streamFile); loop_flag = (loop_start != loop_end); /* also flag possibly @ 0x73 */ #if 0 /* those values look like some kind of loop offsets */ - if (loop_start/0x100 != read_32bitBE(header_offset+0x68, streamFile) || - loop_end /0x100 != read_32bitBE(header_offset+0x6C, streamFile) ) { + if (loop_start/0x100 != read_32bitBE(0x68, streamFile) || + loop_end /0x100 != read_32bitBE(0x6C, streamFile) ) { VGM_LOG("MTA2: wrong loop points\n"); goto fail; } #endif - sample_rate = read_32bitBE(header_offset+0x7c, streamFile); - if (sample_rate) { /* sample rate in 32b float (WHY?) typically 48000.0 */ - float sample_float; - memcpy(&sample_float, &sample_rate, 4); - sample_rate = (int)sample_float; + sample_rate_int = read_32bitBE(0x7c, streamFile); + if (sample_rate_int) { /* sample rate in 32b float (WHY?) typically 48000.0 */ + float* sample_float = (float*)&sample_rate_int; + sample_rate = (int)*sample_float; } else { /* default when not specified (most of the time) */ sample_rate = 48000; } @@ -74,11 +60,11 @@ VGMSTREAM * init_vgmstream_ps3_mta2(STREAMFILE *streamFile) { * FRONT_L = 0x01, FRONT_R = 0x02, FRONT_M = 0x04, BACK_L = 0x08, BACK_R = 0x10, BACK_M = 0x20 */ /* DATA chunk */ - if (read_32bitBE(header_offset+0x7f8, streamFile) != 0x44415441) // "DATA" + if (read_32bitBE(0x7f8, streamFile) != 0x44415441) // "DATA" goto fail; /* 0x7fc: data size (without blocks in case of blocked layout) */ - start_offset = header_offset + 0x800; + start_offset = 0x800; /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); @@ -91,7 +77,7 @@ VGMSTREAM * init_vgmstream_ps3_mta2(STREAMFILE *streamFile) { vgmstream->coding_type = coding_MTA2; vgmstream->layout_type = layout_none; - vgmstream->meta_type = meta_PS3_MTA2; + vgmstream->meta_type = meta_MTA2; /* open the file for reading */ if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) @@ -102,3 +88,42 @@ fail: close_vgmstream(vgmstream); return NULL; } + +/* ****************************************************************************** */ + +/* MTA2 in containers */ +VGMSTREAM * init_vgmstream_mta2_container(STREAMFILE *streamFile) { + VGMSTREAM *vgmstream = NULL; + STREAMFILE *temp_streamFile = NULL; + off_t subfile_offset; + + + /* checks */ + /* .dbm: iPod metadata + mta2 with KCEJ blocks, .bgm: mta2 with KCEJ blocks (fake?) */ + if ( !check_extensions(streamFile,"dbm,bgm,mta2")) + goto fail; + + if (read_32bitBE(0x00,streamFile) == 0x444C424D) { /* "DLBM" */ + subfile_offset = 0x800; + } + else if (read_32bitBE(0x00,streamFile) == 0x00000010) { + subfile_offset = 0x00; + } + else { + goto fail; + } + /* subfile size is implicit in KCEJ blocks */ + + temp_streamFile = setup_mta2_streamfile(streamFile, subfile_offset, 1, "mta2"); + if (!temp_streamFile) goto fail; + + vgmstream = init_vgmstream_mta2(temp_streamFile); + close_streamfile(temp_streamFile); + + return vgmstream; + +fail: + close_streamfile(temp_streamFile); + close_vgmstream(vgmstream); + return NULL; +} diff --git a/src/vgmstream.c b/src/vgmstream.c index ff915477..0fcbbc32 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -300,7 +300,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_pc_adp_otns, init_vgmstream_eb_sfx, init_vgmstream_eb_sf0, - init_vgmstream_ps2_mtaf, + init_vgmstream_mtaf, init_vgmstream_tun, init_vgmstream_wpd, init_vgmstream_mn_str, @@ -346,7 +346,8 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_ta_aac_mobile_vorbis, init_vgmstream_ta_aac_vita, init_vgmstream_va3, - init_vgmstream_ps3_mta2, + init_vgmstream_mta2, + init_vgmstream_mta2_container, init_vgmstream_ngc_ulw, init_vgmstream_pc_xa30, init_vgmstream_wii_04sw, @@ -2844,7 +2845,8 @@ int vgmstream_open_stream(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t s } /* stereo codecs interleave in 2ch pairs (interleave size should still be: full_block_size / channels) */ - if (vgmstream->layout_type == layout_interleave && vgmstream->coding_type == coding_XBOX_IMA) { + if (vgmstream->layout_type == layout_interleave && + (vgmstream->coding_type == coding_XBOX_IMA || vgmstream->coding_type == coding_MTAF)) { is_stereo_codec = 1; } diff --git a/src/vgmstream.h b/src/vgmstream.h index 62eecf05..81bbe08d 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -589,7 +589,7 @@ typedef enum { meta_OTNS_ADP, /* Omikron: The Nomad Soul .adp (PC/DC) */ meta_EB_SFX, /* Excitebots .sfx */ meta_EB_SF0, /* Excitebots .sf0 */ - meta_PS2_MTAF, /* Metal Gear Solid 3 MTAF */ + meta_MTAF, meta_PS2_VAG1, /* Metal Gear Solid 3 VAG1 */ meta_PS2_VAG2, /* Metal Gear Solid 3 VAG2 */ meta_TUN, /* LEGO Racers (PC) */ @@ -627,7 +627,7 @@ typedef enum { meta_TA_AAC_X360, /* tri-Ace AAC (Star Ocean 4, End of Eternity, Infinite Undiscovery) */ meta_TA_AAC_PS3, /* tri-Ace AAC (Star Ocean International, Resonance of Fate) */ meta_TA_AAC_MOBILE, /* tri-Ace AAC (Star Ocean Anamnesis, Heaven x Inferno) */ - meta_PS3_MTA2, /* Metal Gear Solid 4 MTA2 */ + meta_MTA2, meta_NGC_ULW, /* Burnout 1 (GC only) */ meta_PC_XA30, /* Driver - Parallel Lines (PC) */ meta_WII_04SW, /* Driver - Parallel Lines (Wii) */