mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-01-29 19:37:30 +01:00
Remove MTAF deblocking code and cleanup
This commit is contained in:
parent
5b2af165cf
commit
e5d4e2e382
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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"},
|
||||
|
@ -256,6 +256,10 @@
|
||||
RelativePath=".\meta\kma9_streamfile.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\mta2_streamfile.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\opus_interleave_streamfile.h"
|
||||
>
|
||||
|
@ -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" />
|
||||
|
@ -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>
|
||||
|
@ -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
154
src/meta/mta2_streamfile.h
Normal 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_ */
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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) */
|
||||
|
Loading…
x
Reference in New Issue
Block a user