Remove MTAF deblocking code and cleanup

This commit is contained in:
bnnm 2019-03-11 11:58:57 +01:00
parent 5b2af165cf
commit e5d4e2e382
13 changed files with 274 additions and 201 deletions

View File

@ -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);

View File

@ -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;
}
}
}

View File

@ -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;

View File

@ -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"},

View File

@ -256,6 +256,10 @@
RelativePath=".\meta\kma9_streamfile.h"
>
</File>
<File
RelativePath=".\meta\mta2_streamfile.h"
>
</File>
<File
RelativePath=".\meta\opus_interleave_streamfile.h"
>

View File

@ -108,6 +108,7 @@
<ClInclude Include="meta\kma9_streamfile.h" />
<ClInclude Include="meta\ppst_streamfile.h" />
<ClInclude Include="meta\vsv_streamfile.h" />
<ClInclude Include="meta\mta2_streamfile.h" />
<ClInclude Include="meta\opus_interleave_streamfile.h" />
<ClInclude Include="meta\sfh_streamfile.h" />
<ClInclude Include="meta\sqex_scd_streamfile.h" />

View File

@ -92,6 +92,9 @@
<ClInclude Include="meta\kma9_streamfile.h">
<Filter>meta\Header Files</Filter>
</ClInclude>
<ClInclude Include="meta\mta2_streamfile.h">
<Filter>meta\Header Files</Filter>
</ClInclude>
<ClInclude Include="meta\opus_interleave_streamfile.h">
<Filter>meta\Header Files</Filter>
</ClInclude>

View File

@ -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);

154
src/meta/mta2_streamfile.h Normal file
View File

@ -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_ */

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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) */