From df28a1ad34d30eb4de06cf6d14967fbc0718981d Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Sat, 20 May 2017 17:38:53 -0700 Subject: [PATCH] Updated VGMStream to r1050-518-g3103029c. --- .../vgmstream.xcodeproj/project.pbxproj | 16 +- .../vgmstream/vgmstream/src/coding/coding.h | 7 + .../vgmstream/src/coding/ffmpeg_decoder.c | 486 +++++++++++++++++- .../vgmstream/src/coding/mta2_decoder.c | 186 +++++++ .../vgmstream/src/coding/mtaf_decoder.c | 257 ++++----- Frameworks/vgmstream/vgmstream/src/formats.c | 11 +- Frameworks/vgmstream/vgmstream/src/meta/akb.c | 2 +- .../vgmstream/vgmstream/src/meta/ffmpeg.c | 466 +---------------- Frameworks/vgmstream/vgmstream/src/meta/gtd.c | 4 +- .../vgmstream/vgmstream/src/meta/meta.h | 13 +- Frameworks/vgmstream/vgmstream/src/meta/mp4.c | 3 +- .../vgmstream/vgmstream/src/meta/ps2_ast.c | 97 ++-- .../vgmstream/vgmstream/src/meta/ps2_mtaf.c | 196 ++----- .../vgmstream/vgmstream/src/meta/ps2_strlr.c | 4 + .../vgmstream/vgmstream/src/meta/ps2_xau.c | 67 --- .../vgmstream/vgmstream/src/meta/ps3_msf.c | 26 +- .../vgmstream/vgmstream/src/meta/ps3_mta2.c | 104 ++++ .../vgmstream/vgmstream/src/meta/riff.c | 19 +- .../vgmstream/vgmstream/src/meta/sgxd.c | 135 ++--- .../vgmstream/vgmstream/src/meta/sqex_scd.c | 31 +- Frameworks/vgmstream/vgmstream/src/meta/xau.c | 80 +++ Frameworks/vgmstream/vgmstream/src/meta/xma.c | 4 +- Frameworks/vgmstream/vgmstream/src/meta/xwb.c | 14 +- .../vgmstream/vgmstream/src/vgmstream.c | 51 +- .../vgmstream/vgmstream/src/vgmstream.h | 12 +- 25 files changed, 1255 insertions(+), 1036 deletions(-) create mode 100644 Frameworks/vgmstream/vgmstream/src/coding/mta2_decoder.c delete mode 100644 Frameworks/vgmstream/vgmstream/src/meta/ps2_xau.c create mode 100644 Frameworks/vgmstream/vgmstream/src/meta/ps3_mta2.c create mode 100644 Frameworks/vgmstream/vgmstream/src/meta/xau.c diff --git a/Frameworks/vgmstream/vgmstream.xcodeproj/project.pbxproj b/Frameworks/vgmstream/vgmstream.xcodeproj/project.pbxproj index 58b31ea94..abc4a96f7 100644 --- a/Frameworks/vgmstream/vgmstream.xcodeproj/project.pbxproj +++ b/Frameworks/vgmstream/vgmstream.xcodeproj/project.pbxproj @@ -36,7 +36,10 @@ 83299FD01E7660C7003A3242 /* bik.c in Sources */ = {isa = PBXBuildFile; fileRef = 83299FCE1E7660C7003A3242 /* bik.c */; }; 83299FD11E7660C7003A3242 /* dsp_adx.c in Sources */ = {isa = PBXBuildFile; fileRef = 83299FCF1E7660C7003A3242 /* dsp_adx.c */; }; 832C70BF1E9335E400BD7B4E /* Vorbis.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83F4128F1E932F9A002E37D0 /* Vorbis.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 833A7A2E1ED11961003EC53E /* xau.c in Sources */ = {isa = PBXBuildFile; fileRef = 833A7A2D1ED11961003EC53E /* xau.c */; }; 834D3A6E19F47C98001C54F6 /* g1l.c in Sources */ = {isa = PBXBuildFile; fileRef = 834D3A6D19F47C98001C54F6 /* g1l.c */; }; + 8350270D1ED119D200C25929 /* ps3_mta2.c in Sources */ = {isa = PBXBuildFile; fileRef = 8350270C1ED119D200C25929 /* ps3_mta2.c */; }; + 835027131ED119E000C25929 /* mta2_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 835027121ED119E000C25929 /* mta2_decoder.c */; }; 8350C0551E071881009E0A93 /* xma.c in Sources */ = {isa = PBXBuildFile; fileRef = 8350C0541E071881009E0A93 /* xma.c */; }; 8350C05A1E071990009E0A93 /* ps2_svag_snk.c in Sources */ = {isa = PBXBuildFile; fileRef = 8350C0591E071990009E0A93 /* ps2_svag_snk.c */; }; 836E15DE1E88A3380029F8AD /* fsb_vorbis_data.h in Headers */ = {isa = PBXBuildFile; fileRef = 836E15DD1E88A3380029F8AD /* fsb_vorbis_data.h */; }; @@ -269,7 +272,6 @@ 836F700D18BDC2190095E648 /* ps2_wmus.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6ED118BDC2190095E648 /* ps2_wmus.c */; }; 836F700E18BDC2190095E648 /* ps2_xa2.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6ED218BDC2190095E648 /* ps2_xa2.c */; }; 836F700F18BDC2190095E648 /* ps2_xa30.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6ED318BDC2190095E648 /* ps2_xa30.c */; }; - 836F701018BDC2190095E648 /* ps2_xau.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6ED418BDC2190095E648 /* ps2_xau.c */; }; 836F701118BDC2190095E648 /* ps3_cps.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6ED518BDC2190095E648 /* ps3_cps.c */; }; 836F701218BDC2190095E648 /* ps3_ivag.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6ED618BDC2190095E648 /* ps3_ivag.c */; }; 836F701318BDC2190095E648 /* ps3_klbs.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6ED718BDC2190095E648 /* ps3_klbs.c */; }; @@ -512,7 +514,10 @@ 83299FCB1E76607A003A3242 /* header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = header.h; sourceTree = ""; }; 83299FCE1E7660C7003A3242 /* bik.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bik.c; sourceTree = ""; }; 83299FCF1E7660C7003A3242 /* dsp_adx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dsp_adx.c; sourceTree = ""; }; + 833A7A2D1ED11961003EC53E /* xau.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xau.c; sourceTree = ""; }; 834D3A6D19F47C98001C54F6 /* g1l.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = g1l.c; sourceTree = ""; }; + 8350270C1ED119D200C25929 /* ps3_mta2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps3_mta2.c; sourceTree = ""; }; + 835027121ED119E000C25929 /* mta2_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mta2_decoder.c; sourceTree = ""; }; 8350C0541E071881009E0A93 /* xma.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xma.c; sourceTree = ""; }; 8350C0591E071990009E0A93 /* ps2_svag_snk.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_svag_snk.c; sourceTree = ""; }; 836E15DD1E88A3380029F8AD /* fsb_vorbis_data.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fsb_vorbis_data.h; sourceTree = ""; }; @@ -747,7 +752,6 @@ 836F6ED118BDC2190095E648 /* ps2_wmus.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_wmus.c; sourceTree = ""; }; 836F6ED218BDC2190095E648 /* ps2_xa2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_xa2.c; sourceTree = ""; }; 836F6ED318BDC2190095E648 /* ps2_xa30.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_xa30.c; sourceTree = ""; }; - 836F6ED418BDC2190095E648 /* ps2_xau.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_xau.c; sourceTree = ""; }; 836F6ED518BDC2190095E648 /* ps3_cps.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps3_cps.c; sourceTree = ""; }; 836F6ED618BDC2190095E648 /* ps3_ivag.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps3_ivag.c; sourceTree = ""; }; 836F6ED718BDC2190095E648 /* ps3_klbs.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps3_klbs.c; sourceTree = ""; }; @@ -998,6 +1002,7 @@ 836F6DDF18BDC2180095E648 /* coding */ = { isa = PBXGroup; children = ( + 835027121ED119E000C25929 /* mta2_decoder.c */, 83709E0B1ECBC1C3005C03D3 /* mc3_decoder.c */, 83709E0C1ECBC1C3005C03D3 /* psv_decoder.c */, 831BA6221EAC61CB00CF89B0 /* coding_utils.c */, @@ -1088,6 +1093,8 @@ 836F6E2718BDC2180095E648 /* meta */ = { isa = PBXGroup; children = ( + 8350270C1ED119D200C25929 /* ps3_mta2.c */, + 833A7A2D1ED11961003EC53E /* xau.c */, 83709DFF1ECBC1A4005C03D3 /* gtd.c */, 83709E001ECBC1A4005C03D3 /* mc3.c */, 83709E011ECBC1A4005C03D3 /* mss.c */, @@ -1284,7 +1291,6 @@ 836F6ED118BDC2190095E648 /* ps2_wmus.c */, 836F6ED218BDC2190095E648 /* ps2_xa2.c */, 836F6ED318BDC2190095E648 /* ps2_xa30.c */, - 836F6ED418BDC2190095E648 /* ps2_xau.c */, 836F6ED518BDC2190095E648 /* ps3_cps.c */, 836F6ED618BDC2190095E648 /* ps3_ivag.c */, 836F6ED718BDC2190095E648 /* ps3_klbs.c */, @@ -1608,11 +1614,11 @@ 836F705118BDC2190095E648 /* zsd.c in Sources */, 836F6FD218BDC2190095E648 /* ps2_bmdx.c in Sources */, 836F703C18BDC2190095E648 /* wii_bns.c in Sources */, + 835027131ED119E000C25929 /* mta2_decoder.c in Sources */, 836F6FA718BDC2190095E648 /* nds_strm.c in Sources */, 83A3F0741E3AD8B900D6A794 /* formats.c in Sources */, 836F6F6E18BDC2190095E648 /* aix.c in Sources */, 836F703118BDC2190095E648 /* ss_stream.c in Sources */, - 836F701018BDC2190095E648 /* ps2_xau.c in Sources */, 836F6F8718BDC2190095E648 /* ffw.c in Sources */, 836F6FE418BDC2190095E648 /* ps2_leg.c in Sources */, 836F705618BDC2190095E648 /* util.c in Sources */, @@ -1629,6 +1635,7 @@ 83709E0D1ECBC1C3005C03D3 /* mc3_decoder.c in Sources */, 836F6FA818BDC2190095E648 /* nds_swav.c in Sources */, 836F6F3F18BDC2190095E648 /* ast_blocked.c in Sources */, + 833A7A2E1ED11961003EC53E /* xau.c in Sources */, 836F6F5018BDC2190095E648 /* mxch_blocked.c in Sources */, 836F6FB518BDC2190095E648 /* ngc_pdt.c in Sources */, 836F6F4F18BDC2190095E648 /* mus_acm_layout.c in Sources */, @@ -1846,6 +1853,7 @@ 836F6F7618BDC2190095E648 /* brstm.c in Sources */, 836F700718BDC2190095E648 /* ps2_vgv.c in Sources */, 836F704F18BDC2190095E648 /* xwb.c in Sources */, + 8350270D1ED119D200C25929 /* ps3_mta2.c in Sources */, 836F6FC518BDC2190095E648 /* pc_sob.c in Sources */, 836F6FE918BDC2190095E648 /* ps2_mihb.c in Sources */, 836F6FA918BDC2190095E648 /* ngc_adpdtk.c in Sources */, diff --git a/Frameworks/vgmstream/vgmstream/src/coding/coding.h b/Frameworks/vgmstream/vgmstream/src/coding/coding.h index c487ef387..da20e0ec0 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/coding.h +++ b/Frameworks/vgmstream/vgmstream/src/coding/coding.h @@ -114,6 +114,9 @@ void decode_lsf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, /* mtaf_decoder */ void decode_mtaf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int channels); +/* mta2_decoder */ +void decode_mta2(VGMSTREAMCHANNEL * stream, sample * 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); @@ -201,9 +204,13 @@ void free_at3plus(maiatrac3plus_codec_data *data); #ifdef VGM_USE_FFMPEG /* ffmpeg_decoder */ +ffmpeg_codec_data * init_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size); +ffmpeg_codec_data * init_ffmpeg_header_offset(STREAMFILE *streamFile, uint8_t * header, uint64_t header_size, uint64_t start, uint64_t size); + void decode_ffmpeg(VGMSTREAM *stream, sample * outbuf, int32_t samples_to_do, int channels); void reset_ffmpeg(VGMSTREAM *vgmstream); void seek_ffmpeg(VGMSTREAM *vgmstream, int32_t num_sample); +void free_ffmpeg(ffmpeg_codec_data *data); void ffmpeg_set_skip_samples(ffmpeg_codec_data * data, int skip_samples); #endif diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c index d6d6376d6..0947c5db3 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c @@ -1,7 +1,34 @@ -#include "../vgmstream.h" +#include "coding.h" #ifdef VGM_USE_FFMPEG +/* internal sizes, can be any value */ +#define FFMPEG_DEFAULT_BUFFER_SIZE 2048 +#define FFMPEG_DEFAULT_IO_BUFFER_SIZE 128 * 1024 + + +static volatile int g_ffmpeg_initialized = 0; + + +/* ******************************************** */ +/* INTERNAL UTILS */ +/* ******************************************** */ + +/* Global FFmpeg init */ +static void g_init_ffmpeg() { + if (g_ffmpeg_initialized == 1) { + while (g_ffmpeg_initialized < 2); /* active wait for lack of a better way */ + } + else if (g_ffmpeg_initialized == 0) { + g_ffmpeg_initialized = 1; + av_log_set_flags(AV_LOG_SKIP_REPEATED); + av_log_set_level(AV_LOG_ERROR); + av_register_all(); + g_ffmpeg_initialized = 2; + } +} + +/* converts codec's samples (can be in any format, ex. Ogg's float32) to PCM16 */ static void convert_audio(sample *outbuf, const uint8_t *inbuf, int sampleCount, int bitsPerSample, int floatingPoint) { int s; switch (bitsPerSample) { @@ -59,15 +86,390 @@ static void convert_audio(sample *outbuf, const uint8_t *inbuf, int sampleCount, } } +/** + * Special patching for FFmpeg's buggy seek code. + * + * To seek with avformat_seek_file/av_seek_frame, FFmpeg's demuxers can implement read_seek2 (newest API) + * or read_seek (older API), with various search modes. If none are available it will use seek_frame_generic, + * which manually reads frame by frame until the selected timestamp. However, the prev frame will be consumed + * (so after seeking to 0 next av_read_frame will actually give the second frame and so on). + * + * Fortunately seek_frame_generic can use an index to find the correct position. This function reads the + * first frame/packet and sets up index to timestamp 0. This ensures faulty demuxers will seek to 0 correctly. + * Some formats may not seek to 0 even with this, though. + */ +static int init_seek(ffmpeg_codec_data * data) { + int ret, ts_index, found_first = 0; + int64_t ts = 0; + int64_t pos = 0; /* offset */ + int size = 0; /* coded size */ + int distance = 0; /* always? */ + + AVStream * stream; + AVPacket * pkt; + + stream = data->formatCtx->streams[data->streamIndex]; + pkt = data->lastReadPacket; + + /* read_seek shouldn't need this index, but direct access to FFmpeg's internals is no good */ + /* if (data->formatCtx->iformat->read_seek || data->formatCtx->iformat->read_seek2) + return 0; */ + + /* some formats already have a proper index (e.g. M4A) */ + ts_index = av_index_search_timestamp(stream, ts, AVSEEK_FLAG_ANY); + if (ts_index>=0) + goto test_seek; + + + /* find the first + second packets to get pos/size */ + while (1) { + av_packet_unref(pkt); + ret = av_read_frame(data->formatCtx, pkt); + if (ret < 0) + break; + if (pkt->stream_index != data->streamIndex) + continue; /* ignore non-selected streams */ + + if (!found_first) { /* first found */ + found_first = 1; + pos = pkt->pos; + ts = pkt->dts; + continue; + } else { /* second found */ + size = pkt->pos - pos; /* coded, pkt->size is decoded size */ + break; + } + } + if (!found_first) + goto fail; + + /* in rare cases there is only one packet */ + /* if (size == 0) { size = data_end - pos; } */ /* no easy way to know, ignore (most formats don's need size) */ + + /* some formats (XMA1) don't seem to have packet.dts, pretend it's 0 */ + if (ts == INT64_MIN) + ts = 0; + + /* Some streams start with negative DTS (observed in Ogg). For Ogg seeking to negative or 0 doesn't alter the output. + * It does seem seeking before decoding alters a bunch of (inaudible) +-1 lower bytes though. */ + VGM_ASSERT(ts != 0, "FFMPEG: negative start_ts (%li)\n", (long)ts); + if (ts != 0) + ts = 0; + + /* add index 0 */ + ret = av_add_index_entry(stream, pos, ts, size, distance, AVINDEX_KEYFRAME); + if ( ret < 0 ) + return ret; + + +test_seek: + /* seek to 0 test / move back to beginning, since we just consumed packets */ + ret = avformat_seek_file(data->formatCtx, data->streamIndex, ts, ts, ts, AVSEEK_FLAG_ANY); + if ( ret < 0 ) + return ret; /* we can't even reset_vgmstream the file */ + + avcodec_flush_buffers(data->codecCtx); + + return 0; + +fail: + return -1; +} + + +/* ******************************************** */ +/* AVIO CALLBACKS */ +/* ******************************************** */ + +/* AVIO callback: read stream, skipping external headers if needed */ +static int ffmpeg_read(void *opaque, uint8_t *buf, int buf_size) { + ffmpeg_codec_data *data = (ffmpeg_codec_data *) opaque; + uint64_t offset = data->offset; + int max_to_copy = 0; + int ret; + + if (data->header_insert_block) { + if (offset < data->header_size) { + max_to_copy = (int)(data->header_size - offset); + if (max_to_copy > buf_size) { + max_to_copy = buf_size; + } + memcpy(buf, data->header_insert_block + offset, max_to_copy); + buf += max_to_copy; + buf_size -= max_to_copy; + offset += max_to_copy; + if (!buf_size) { + data->offset = offset; + return max_to_copy; + } + } + offset -= data->header_size; + } + + /* when "fake" size is smaller than "real" size we need to make sure bytes_read (ret) is clamped; + * it confuses FFmpeg in rare cases (STREAMFILE may have valid data after size) */ + if (offset + buf_size > data->size + data->header_size) { + buf_size = data->size - offset; /* header "read" is manually inserted later */ + } + + ret = read_streamfile(buf, offset + data->start, buf_size, data->streamfile); + if (ret > 0) { + offset += ret; + if (data->header_insert_block) { + ret += max_to_copy; + } + } + + if (data->header_insert_block) { + offset += data->header_size; + } + + data->offset = offset; + return ret; +} + +/* AVIO callback: write stream not needed */ +static int ffmpeg_write(void *opaque, uint8_t *buf, int buf_size) { + return -1; +} + +/* AVIO callback: seek stream, skipping external headers if needed */ +static int64_t ffmpeg_seek(void *opaque, int64_t offset, int whence) { + ffmpeg_codec_data *data = (ffmpeg_codec_data *) opaque; + int ret = 0; + + if (whence & AVSEEK_SIZE) { + return data->size + data->header_size; + } + whence &= ~(AVSEEK_SIZE | AVSEEK_FORCE); + /* false offsets, on reads data->start will be added */ + switch (whence) { + case SEEK_SET: + break; + + case SEEK_CUR: + offset += data->offset; + break; + + case SEEK_END: + offset += data->size; + if (data->header_insert_block) + offset += data->header_size; + break; + } + + /* clamp offset; fseek returns 0 when offset > size, too */ + if (offset > data->size + data->header_size) { + offset = data->size + data->header_size; + } + + data->offset = offset; + return ret; +} + + +/* ******************************************** */ +/* MAIN INIT/DECODER */ +/* ******************************************** */ + +/** + * Manually init FFmpeg, from an offset. + * Used if the stream has internal data recognized by FFmpeg. + */ +ffmpeg_codec_data * init_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size) { + return init_ffmpeg_header_offset(streamFile, NULL, 0, start, size); +} + +/** + * Manually init FFmpeg, from a fake header / offset. + * + * Takes a fake header, to trick FFmpeg into demuxing/decoding the stream. + * This header will be seamlessly inserted before 'start' offset, and total filesize will be 'header_size' + 'size'. + * The header buffer will be copied and memory-managed internally. + */ +ffmpeg_codec_data * init_ffmpeg_header_offset(STREAMFILE *streamFile, uint8_t * header, uint64_t header_size, uint64_t start, uint64_t size) { + char filename[PATH_LIMIT]; + ffmpeg_codec_data * data; + int errcode, i; + + int streamIndex, streamCount; + AVStream *stream; + AVCodecParameters *codecPar; + + AVRational tb; + + + /* basic setup */ + g_init_ffmpeg(); + + data = ( ffmpeg_codec_data * ) calloc(1, sizeof(ffmpeg_codec_data)); + if (!data) return NULL; + + streamFile->get_name( streamFile, filename, sizeof(filename) ); + + data->streamfile = streamFile->open(streamFile, filename, STREAMFILE_DEFAULT_BUFFER_SIZE); + if (!data->streamfile) goto fail; + + data->start = start; + data->size = size; + + + /* insert fake header to trick FFmpeg into demuxing/decoding the stream */ + if (header_size > 0) { + data->header_size = header_size; + data->header_insert_block = av_memdup(header, header_size); + if (!data->header_insert_block) goto fail; + } + + /* setup IO, attempt to autodetect format and gather some info */ + data->buffer = av_malloc(FFMPEG_DEFAULT_IO_BUFFER_SIZE); + if (!data->buffer) goto fail; + + data->ioCtx = avio_alloc_context(data->buffer, FFMPEG_DEFAULT_IO_BUFFER_SIZE, 0, data, ffmpeg_read, ffmpeg_write, ffmpeg_seek); + if (!data->ioCtx) goto fail; + + data->formatCtx = avformat_alloc_context(); + if (!data->formatCtx) goto fail; + + data->formatCtx->pb = data->ioCtx; + + if ((errcode = avformat_open_input(&data->formatCtx, "", NULL, NULL)) < 0) goto fail; /* autodetect */ + + if ((errcode = avformat_find_stream_info(data->formatCtx, NULL)) < 0) goto fail; + + + /* find valid audio stream inside */ + streamIndex = -1; + streamCount = 0; /* audio streams only */ + + for (i = 0; i < data->formatCtx->nb_streams; ++i) { + stream = data->formatCtx->streams[i]; + codecPar = stream->codecpar; + if (streamIndex < 0 && codecPar->codec_type == AVMEDIA_TYPE_AUDIO) { + streamIndex = i; /* select first audio stream found */ + } else { + stream->discard = AVDISCARD_ALL; /* disable demuxing unneded streams */ + } + if (codecPar->codec_type == AVMEDIA_TYPE_AUDIO) + streamCount++; + } + + if (streamIndex < 0) goto fail; + + data->streamIndex = streamIndex; + stream = data->formatCtx->streams[streamIndex]; + data->streamCount = streamCount; + + + /* prepare codec and frame/packet buffers */ + data->codecCtx = avcodec_alloc_context3(NULL); + if (!data->codecCtx) goto fail; + + if ((errcode = avcodec_parameters_to_context(data->codecCtx, codecPar)) < 0) goto fail; + + av_codec_set_pkt_timebase(data->codecCtx, stream->time_base); + + data->codec = avcodec_find_decoder(data->codecCtx->codec_id); + if (!data->codec) goto fail; + + if ((errcode = avcodec_open2(data->codecCtx, data->codec, NULL)) < 0) goto fail; + + data->lastDecodedFrame = av_frame_alloc(); + if (!data->lastDecodedFrame) goto fail; + av_frame_unref(data->lastDecodedFrame); + + data->lastReadPacket = malloc(sizeof(AVPacket)); + if (!data->lastReadPacket) goto fail; + av_new_packet(data->lastReadPacket, 0); + + data->readNextPacket = 1; + data->bytesConsumedFromDecodedFrame = INT_MAX; + + + /* other setup */ + data->sampleRate = data->codecCtx->sample_rate; + data->channels = data->codecCtx->channels; + data->floatingPoint = 0; + + switch (data->codecCtx->sample_fmt) { + case AV_SAMPLE_FMT_U8: + case AV_SAMPLE_FMT_U8P: + data->bitsPerSample = 8; + break; + + case AV_SAMPLE_FMT_S16: + case AV_SAMPLE_FMT_S16P: + data->bitsPerSample = 16; + break; + + case AV_SAMPLE_FMT_S32: + case AV_SAMPLE_FMT_S32P: + data->bitsPerSample = 32; + break; + + case AV_SAMPLE_FMT_FLT: + case AV_SAMPLE_FMT_FLTP: + data->bitsPerSample = 32; + data->floatingPoint = 1; + break; + + case AV_SAMPLE_FMT_DBL: + case AV_SAMPLE_FMT_DBLP: + data->bitsPerSample = 64; + data->floatingPoint = 1; + break; + + default: + goto fail; + } + + data->bitrate = (int)(data->codecCtx->bit_rate); + data->endOfStream = 0; + data->endOfAudio = 0; + + /* try to guess frames/samples (duration isn't always set) */ + tb.num = 1; tb.den = data->codecCtx->sample_rate; + data->totalSamples = av_rescale_q(stream->duration, stream->time_base, tb); + if (data->totalSamples < 0) + data->totalSamples = 0; /* caller must consider this */ + + data->blockAlign = data->codecCtx->block_align; + data->frameSize = data->codecCtx->frame_size; + if(data->frameSize == 0) /* some formats don't set frame_size but can get on request, and vice versa */ + data->frameSize = av_get_audio_frame_duration(data->codecCtx,0); + + /* setup decode buffer */ + data->sampleBufferBlock = FFMPEG_DEFAULT_BUFFER_SIZE; + data->sampleBuffer = av_malloc( data->sampleBufferBlock * (data->bitsPerSample / 8) * data->channels ); + if (!data->sampleBuffer) + goto fail; + + + /* setup decent seeking for faulty formats */ + errcode = init_seek(data); + if (errcode < 0) goto fail; + + /* expose start samples to be skipped (encoder delay, usually added by MDCT-based encoders like AAC/MP3/ATRAC3/XMA/etc) + * get after init_seek because some demuxers like AAC only fill skip_samples for the first packet */ + if (stream->start_skip_samples) /* samples to skip in the first packet */ + data->skipSamples = stream->start_skip_samples; + else if (stream->skip_samples) /* samples to skip in any packet (first in this case), used sometimes instead (ex. AAC) */ + data->skipSamples = stream->skip_samples; + + return data; + +fail: + free_ffmpeg(data); + + return NULL; +} + +/* decode samples of any kind of FFmpeg format */ void decode_ffmpeg(VGMSTREAM *vgmstream, sample * outbuf, int32_t samples_to_do, int channels) { ffmpeg_codec_data *data = (ffmpeg_codec_data *) vgmstream->codec_data; - int bytesPerSample; - int bytesPerFrame; - int frameSize; - - int bytesToRead; - int bytesRead; + int bytesPerSample, bytesPerFrame, frameSize; + int bytesToRead, bytesRead; uint8_t *targetBuf; @@ -78,10 +480,7 @@ void decode_ffmpeg(VGMSTREAM *vgmstream, sample * outbuf, int32_t samples_to_do, int bytesConsumedFromDecodedFrame; - int readNextPacket; - int endOfStream; - int endOfAudio; - + int readNextPacket, endOfStream, endOfAudio; int framesReadNow; @@ -114,12 +513,7 @@ void decode_ffmpeg(VGMSTREAM *vgmstream, sample * outbuf, int32_t samples_to_do, /* keep reading and decoding packets until the requested number of samples (in bytes) */ while (bytesRead < bytesToRead) { - int planeSize; - int planar; - int dataSize; - int toConsume; - int errcode; - + int planeSize, planar, dataSize, toConsume, errcode; /* size of previous frame */ dataSize = av_samples_get_buffer_size(&planeSize, codecCtx->channels, lastDecodedFrame->nb_samples, codecCtx->sample_fmt, 1); @@ -231,10 +625,10 @@ void decode_ffmpeg(VGMSTREAM *vgmstream, sample * outbuf, int32_t samples_to_do, end: framesReadNow = bytesRead / frameSize; - // Convert the audio + /* Convert the audio */ convert_audio(outbuf, data->sampleBuffer, framesReadNow * channels, data->bitsPerSample, data->floatingPoint); - // Output the state back to the structure + /* Output the state back to the structure */ data->bytesConsumedFromDecodedFrame = bytesConsumedFromDecodedFrame; data->readNextPacket = readNextPacket; data->endOfStream = endOfStream; @@ -242,6 +636,10 @@ end: } +/* ******************************************** */ +/* UTILS */ +/* ******************************************** */ + void reset_ffmpeg(VGMSTREAM *vgmstream) { ffmpeg_codec_data *data = (ffmpeg_codec_data *) vgmstream->codec_data; @@ -268,7 +666,6 @@ void reset_ffmpeg(VGMSTREAM *vgmstream) { } } - void seek_ffmpeg(VGMSTREAM *vgmstream, int32_t num_sample) { ffmpeg_codec_data *data = (ffmpeg_codec_data *) vgmstream->codec_data; int64_t ts; @@ -297,6 +694,55 @@ void seek_ffmpeg(VGMSTREAM *vgmstream, int32_t num_sample) { } } +void free_ffmpeg(ffmpeg_codec_data *data) { + if (data == NULL) + return; + + if (data->lastReadPacket) { + av_packet_unref(data->lastReadPacket); + free(data->lastReadPacket); + data->lastReadPacket = NULL; + } + if (data->lastDecodedFrame) { + av_free(data->lastDecodedFrame); + data->lastDecodedFrame = NULL; + } + if (data->codecCtx) { + avcodec_close(data->codecCtx); + avcodec_free_context(&(data->codecCtx)); + data->codecCtx = NULL; + } + if (data->formatCtx) { + avformat_close_input(&(data->formatCtx)); + data->formatCtx = NULL; + } + if (data->ioCtx) { + // buffer passed in is occasionally freed and replaced. + // the replacement must be freed as well. + data->buffer = data->ioCtx->buffer; + av_free(data->ioCtx); + data->ioCtx = NULL; + } + if (data->buffer) { + av_free(data->buffer); + data->buffer = NULL; + } + if (data->sampleBuffer) { + av_free(data->sampleBuffer); + data->sampleBuffer = NULL; + } + if (data->header_insert_block) { + av_free(data->header_insert_block); + data->header_insert_block = NULL; + } + if (data->streamfile) { + close_streamfile(data->streamfile); + data->streamfile = NULL; + } + free(data); +} + + /** * Sets the number of samples to skip at the beginning of the stream, needed by some "gapless" formats. * (encoder delay, usually added by MDCT-based encoders like AAC/MP3/ATRAC3/XMA/etc to "set up" the decoder). diff --git a/Frameworks/vgmstream/vgmstream/src/coding/mta2_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/mta2_decoder.c new file mode 100644 index 000000000..650bb8560 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/coding/mta2_decoder.c @@ -0,0 +1,186 @@ +#include "coding.h" +#include "../util.h" + +/* MTA2 (EA XAS variant?) 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 + * + * MTA2 layout: + * - data is divided into N tracks of 0x10 header + 0x90 frame per track channel, forming N streams + * ex: 8ch: track0 4ch + track1 4ch + track0 4ch + track1 4ch ...; or 2ch = 1ch track0 + 1ch track1 + * * 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 */ + 0, 240, 460, 392, 488, 460, 460, 240 +}; +static const int c2[8] = { /* mod table 2 */ + 0, 0, -208, -220, -240, -240, -220, -104 +}; +static const int c3[32] = { /* shift table */ + 256, 335, 438, 573, 749, 979, 1281, 1675, + 2190, 2864, 3746, 4898, 6406, 8377, 10955, 14327, + 18736, 24503, 32043, 41905, 54802, 71668, 93724, 122568, + 160290, 209620, 274133, 358500, 468831, 613119, 801811, 1048576 +}; + +/* expands nibble */ +static short calculate_output(int nibble, short smp1, short smp2, int mod, int sh) { + int output; + if (nibble > 7) /* sign extend */ + nibble = nibble - 16; + + output = (smp1 * c1[mod] + smp2 * c2[mod] + (nibble * c3[sh]) + 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 @ %08lx\n", 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) { + int samples_done = 0, sample_count = 0, channel_block_samples, channel_first_sample, frame_size = 0; + int i, group, row, col; + int num_track = 0, channel_layout, track_channels = 0, track_channel; + + + /* block/track skip */ + do { + /* 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? */ + 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 */ + + if (num_track < 0) + break; /* EOF: whatever */ + + track_channels = 0; + for (i = 0; i < 8; i++) { + if ((channel_layout >> i) & 0x01) + track_channels++; + } + + /* assumes tracks channels are divided evenly in all tracks (ex. not 2ch + 1ch + 1ch) */ + if (channel / track_channels == num_track) + break; /* channel belongs to this track */ + + /* keep looping for our track */ + stream->offset += 0x10 + frame_size; + } + while (1); + + track_channel = channel % track_channels; + channel_block_samples = (0x80*2); + channel_first_sample = first_sample % (0x80*2); + + + /* parse channel frame (header 0x04*4 + data 0x20*4) */ + for (group = 0; group < 4; group++) { + short smp2, smp1, mod, sh, 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 */ + + /* 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; + samples_done++; + } + sample_count++; + if (sample_count >= channel_first_sample && samples_done < samples_to_do) { + outbuf[samples_done * channelspacing] = smp1; + samples_done++; + } + sample_count++; + + for (row = 0; row < 8; row++) { + 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); + + /* ignore last 2 nibbles (uses first 2 header samples) */ + if (row < 7 || col < 3*2) { + if (sample_count >= channel_first_sample && samples_done < samples_to_do) { + outbuf[samples_done * channelspacing] = output; + samples_done++; + } + sample_count++; + } + + smp2 = smp1; + smp1 = output; + } + } + } + + + /* block fully done */ + if (channel_first_sample + samples_done == channel_block_samples) { + stream->offset += 0x10 + frame_size; + } +} diff --git a/Frameworks/vgmstream/vgmstream/src/coding/mtaf_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/mtaf_decoder.c index 50e27672a..642aa34da 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/mtaf_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/mtaf_decoder.c @@ -1,142 +1,152 @@ -//#include -//#include #include "coding.h" #include "../util.h" -#define MTAF_BLOCK_SUPPORT 0 +#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. -int index_table[16] = { +/* 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). + */ + +static const int index_table[16] = { -1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8 }; -static int16_t step_size[32][16] = { -{ 1, 5, 9, 13, 16, 20, 24, 28, - -1, -5, -9, -13, -16, -20, -24, -28, }, -{ 2, 6, 11, 15, 20, 24, 29, 33, - -2, -6, -11, -15, -20, -24, -29, -33, }, -{ 2, 7, 13, 18, 23, 28, 34, 39, - -2, -7, -13, -18, -23, -28, -34, -39, }, -{ 3, 9, 15, 21, 28, 34, 40, 46, - -3, -9, -15, -21, -28, -34, -40, -46, }, -{ 3, 11, 18, 26, 33, 41, 48, 56, - -3, -11, -18, -26, -33, -41, -48, -56, }, -{ 4, 13, 22, 31, 40, 49, 58, 67, - -4, -13, -22, -31, -40, -49, -58, -67, }, -{ 5, 16, 26, 37, 48, 59, 69, 80, - -5, -16, -26, -37, -48, -59, -69, -80, }, -{ 6, 19, 31, 44, 57, 70, 82, 95, - -6, -19, -31, -44, -57, -70, -82, -95, }, -{ 7, 22, 38, 53, 68, 83, 99, 114, - -7, -22, -38, -53, -68, -83, -99, -114, }, -{ 9, 27, 45, 63, 81, 99, 117, 135, - -9, -27, -45, -63, -81, -99, -117, -135, }, -{ 10, 32, 53, 75, 96, 118, 139, 161, - -10, -32, -53, -75, -96, -118, -139, -161, }, -{ 12, 38, 64, 90, 115, 141, 167, 193, - -12, -38, -64, -90, -115, -141, -167, -193, }, -{ 15, 45, 76, 106, 137, 167, 198, 228, - -15, -45, -76, -106, -137, -167, -198, -228, }, -{ 18, 54, 91, 127, 164, 200, 237, 273, - -18, -54, -91, -127, -164, -200, -237, -273, }, -{ 21, 65, 108, 152, 195, 239, 282, 326, - -21, -65, -108, -152, -195, -239, -282, -326, }, -{ 25, 77, 129, 181, 232, 284, 336, 388, - -25, -77, -129, -181, -232, -284, -336, -388, }, -{ 30, 92, 153, 215, 276, 338, 399, 461, - -30, -92, -153, -215, -276, -338, -399, -461, }, -{ 36, 109, 183, 256, 329, 402, 476, 549, - -36, -109, -183, -256, -329, -402, -476, -549, }, -{ 43, 130, 218, 305, 392, 479, 567, 654, - -43, -130, -218, -305, -392, -479, -567, -654, }, -{ 52, 156, 260, 364, 468, 572, 676, 780, - -52, -156, -260, -364, -468, -572, -676, -780, }, -{ 62, 186, 310, 434, 558, 682, 806, 930, - -62, -186, -310, -434, -558, -682, -806, -930, }, -{ 73, 221, 368, 516, 663, 811, 958, 1106, - -73, -221, -368, -516, -663, -811, -958, -1106, }, -{ 87, 263, 439, 615, 790, 966, 1142, 1318, - -87, -263, -439, -615, -790, -966, -1142, -1318, }, -{ 104, 314, 523, 733, 942, 1152, 1361, 1571, - -104, -314, -523, -733, -942, -1152, -1361, -1571, }, -{ 124, 374, 623, 873, 1122, 1372, 1621, 1871, - -124, -374, -623, -873, -1122, -1372, -1621, -1871, }, -{ 148, 445, 743, 1040, 1337, 1634, 1932, 2229, - -148, -445, -743, -1040, -1337, -1634, -1932, -2229, }, -{ 177, 531, 885, 1239, 1593, 1947, 2301, 2655, - -177, -531, -885, -1239, -1593, -1947, -2301, -2655, }, -{ 210, 632, 1053, 1475, 1896, 2318, 2739, 3161, - -210, -632, -1053, -1475, -1896, -2318, -2739, -3161, }, -{ 251, 753, 1255, 1757, 2260, 2762, 3264, 3766, - -251, -753, -1255, -1757, -2260, -2762, -3264, -3766, }, -{ 299, 897, 1495, 2093, 2692, 3290, 3888, 4486, - -299, -897, -1495, -2093, -2692, -3290, -3888, -4486, }, -{ 356, 1068, 1781, 2493, 3206, 3918, 4631, 5343, - -356, -1068, -1781, -2493, -3206, -3918, -4631, -5343, }, -{ 424, 1273, 2121, 2970, 3819, 4668, 5516, 6365, - -424, -1273, -2121, -2970, -3819, -4668, -5516, -6365, }, +static const int16_t step_size[32][16] = { + { 1, 5, 9, 13, 16, 20, 24, 28, + -1, -5, -9, -13, -16, -20, -24, -28, }, + { 2, 6, 11, 15, 20, 24, 29, 33, + -2, -6, -11, -15, -20, -24, -29, -33, }, + { 2, 7, 13, 18, 23, 28, 34, 39, + -2, -7, -13, -18, -23, -28, -34, -39, }, + { 3, 9, 15, 21, 28, 34, 40, 46, + -3, -9, -15, -21, -28, -34, -40, -46, }, + { 3, 11, 18, 26, 33, 41, 48, 56, + -3, -11, -18, -26, -33, -41, -48, -56, }, + { 4, 13, 22, 31, 40, 49, 58, 67, + -4, -13, -22, -31, -40, -49, -58, -67, }, + { 5, 16, 26, 37, 48, 59, 69, 80, + -5, -16, -26, -37, -48, -59, -69, -80, }, + { 6, 19, 31, 44, 57, 70, 82, 95, + -6, -19, -31, -44, -57, -70, -82, -95, }, + { 7, 22, 38, 53, 68, 83, 99, 114, + -7, -22, -38, -53, -68, -83, -99, -114, }, + { 9, 27, 45, 63, 81, 99, 117, 135, + -9, -27, -45, -63, -81, -99, -117, -135, }, + { 10, 32, 53, 75, 96, 118, 139, 161, + -10, -32, -53, -75, -96, -118, -139, -161, }, + { 12, 38, 64, 90, 115, 141, 167, 193, + -12, -38, -64, -90, -115, -141, -167, -193, }, + { 15, 45, 76, 106, 137, 167, 198, 228, + -15, -45, -76, -106, -137, -167, -198, -228, }, + { 18, 54, 91, 127, 164, 200, 237, 273, + -18, -54, -91, -127, -164, -200, -237, -273, }, + { 21, 65, 108, 152, 195, 239, 282, 326, + -21, -65, -108, -152, -195, -239, -282, -326, }, + { 25, 77, 129, 181, 232, 284, 336, 388, + -25, -77, -129, -181, -232, -284, -336, -388, }, + { 30, 92, 153, 215, 276, 338, 399, 461, + -30, -92, -153, -215, -276, -338, -399, -461, }, + { 36, 109, 183, 256, 329, 402, 476, 549, + -36, -109, -183, -256, -329, -402, -476, -549, }, + { 43, 130, 218, 305, 392, 479, 567, 654, + -43, -130, -218, -305, -392, -479, -567, -654, }, + { 52, 156, 260, 364, 468, 572, 676, 780, + -52, -156, -260, -364, -468, -572, -676, -780, }, + { 62, 186, 310, 434, 558, 682, 806, 930, + -62, -186, -310, -434, -558, -682, -806, -930, }, + { 73, 221, 368, 516, 663, 811, 958, 1106, + -73, -221, -368, -516, -663, -811, -958, -1106, }, + { 87, 263, 439, 615, 790, 966, 1142, 1318, + -87, -263, -439, -615, -790, -966, -1142, -1318, }, + { 104, 314, 523, 733, 942, 1152, 1361, 1571, + -104, -314, -523, -733, -942, -1152, -1361, -1571, }, + { 124, 374, 623, 873, 1122, 1372, 1621, 1871, + -124, -374, -623, -873, -1122, -1372, -1621, -1871, }, + { 148, 445, 743, 1040, 1337, 1634, 1932, 2229, + -148, -445, -743, -1040, -1337, -1634, -1932, -2229, }, + { 177, 531, 885, 1239, 1593, 1947, 2301, 2655, + -177, -531, -885, -1239, -1593, -1947, -2301, -2655, }, + { 210, 632, 1053, 1475, 1896, 2318, 2739, 3161, + -210, -632, -1053, -1475, -1896, -2318, -2739, -3161, }, + { 251, 753, 1255, 1757, 2260, 2762, 3264, 3766, + -251, -753, -1255, -1757, -2260, -2762, -3264, -3766, }, + { 299, 897, 1495, 2093, 2692, 3290, 3888, 4486, + -299, -897, -1495, -2093, -2692, -3290, -3888, -4486, }, + { 356, 1068, 1781, 2493, 3206, 3918, 4631, 5343, + -356, -1068, -1781, -2493, -3206, -3918, -4631, -5343, }, + { 424, 1273, 2121, 2970, 3819, 4668, 5516, 6365, + -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 @ %08lx\n", 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, int channels) { int32_t sample_count; - off_t cur_off = stream->offset; int i; - int c = channel%2; /* global channel to stream channel */ + int c = channel%2; /* global channel to track channel */ int32_t hist = stream->adpcm_history1_16; int32_t step_idx = stream->adpcm_step_index; - uint8_t byte = 0; -#if MTAF_BLOCK_SUPPORT - { - /* "macroblock" support (layout/mtaf_block.c) was removed since the extractor now produces clean files; - * this a hack to skip those blocks, left as a reminder (not well tested) */ - int unk, size, empty, frames, repeat = 1; - do { - unk = read_32bitLE(cur_off+0x00, stream->streamfile); /* always BE 0x01001100? */ - size = read_32bitLE(cur_off+0x04, stream->streamfile); /* block size */ - empty = read_32bitLE(cur_off+0x08, stream->streamfile); /* always 0? */ - frames = read_32bitLE(cur_off+0x0c, stream->streamfile); /* total frames of 0x110 */ - if (unk == 0x00110001 && empty == 0 && size > 0) { - if (frames == 0) { - stream->offset += size; /* full skip */ - } else if ((size-0x10) == frames*0x110) { - stream->offset += 0x10; /* header skip */ - repeat = 0; - } - cur_off = stream->offset; - } - else { - repeat = 0; - } - - } while(repeat); - } -#endif + #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; - /* read header when we hit a new frame every 0x100 samples */ if (first_sample == 0) { - int32_t init_idx, init_hist; - - /* 0x10 header: owner stream, frame count, step-L, step-R, hist-L, hist-R */ - /* uint32_t stream = read_8bit(cur_off+0+c*2, stream->streamfile); */ /* 0=first */ - /* uint24_t frames = (uint24_t)read_16bitLE(cur_off+1, stream->streamfile); */ /* 1=first */ - init_idx = read_16bitLE(cur_off+4+c*2, stream->streamfile); /* step-L/R */ - init_hist = read_16bitLE(cur_off+4+4+c*4, stream->streamfile); /* hist-L/R: hist 16bit + empty 16bit */ - - VGM_ASSERT( read_16bitLE(cur_off+4+4+2+c*4, stream->streamfile) != 0, - "init_hist not 16bit at 0x%lx, ch=%d\n", cur_off, c); - VGM_ASSERT( init_idx < 0 || init_idx > 31, - "init_idx out of range at 0x%lx, ch=%d\n", cur_off, c); - VGM_ASSERT( step_idx != init_idx, - "step_idx does not match init_idx at 0x%lx, step=%d, init=%d\n",cur_off,step_idx, init_idx); + /* 0x10 header: track (8b, 0=first), track count (24b, 1=first), step-L, step-R, hist-L, hist-R */ + int32_t init_idx = read_16bitLE(stream->offset+4+0+c*2, stream->streamfile); /* step-L/R */ + int32_t init_hist = read_16bitLE(stream->offset+4+4+c*4, stream->streamfile); /* hist-L/R: hist 16bit + empty 16bit */ + VGM_ASSERT(init_idx < 0 || init_idx > 31, "MTAF: bad header idx @ 0x%lx\n", stream->offset); /* avoid index out of range in corrupt files */ if (init_idx < 0) { init_idx = 0; @@ -149,18 +159,12 @@ void decode_mtaf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, } + /* skip to nibble */ for (i=first_sample,sample_count=0; istreamfile); - nibble = byte & 0x0f; - } else { /* high nibble last */ - nibble = byte >> 4; - } + uint8_t byte = read_8bit(stream->offset + 0x10 + 0x80*c + i/2, stream->streamfile); + uint8_t nibble = (byte >> (!(i&1)?0:4)) & 0xf; /* lower first */ hist = clamp16(hist+step_size[step_idx][nibble]); - outbuf[sample_count] = hist; step_idx += index_table[nibble]; @@ -169,10 +173,9 @@ void decode_mtaf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, } else if (step_idx > 31) { step_idx = 31; } - } /* end sample loop */ + } - // update state + /* update state */ stream->adpcm_step_index = step_idx; stream->adpcm_history1_16 = hist; - } diff --git a/Frameworks/vgmstream/vgmstream/src/formats.c b/Frameworks/vgmstream/vgmstream/src/formats.c index d6e4c9eb8..c9c438be1 100644 --- a/Frameworks/vgmstream/vgmstream/src/formats.c +++ b/Frameworks/vgmstream/vgmstream/src/formats.c @@ -53,6 +53,7 @@ static const char* extension_list[] = { "bfwav", "bfwavnsmbu", "bg00", + "bgm", "bgw", "bh2pcm", "bik", @@ -82,6 +83,7 @@ static const char* extension_list[] = { "cps", "cxs", + "dbm", "dcs", "ddsp", "de2", @@ -143,6 +145,7 @@ static const char* extension_list[] = { "kovs", "kraw", + "laac", //fake extension, for tri-Ace/FFmpeg "leg", "lmp4", //fake extension, for looping "logg", //fake extension, for looping @@ -156,6 +159,7 @@ static const char* extension_list[] = { "mca", "mcg", "mds", + "mdsp", "mi4", "mib", "mic", @@ -168,6 +172,7 @@ static const char* extension_list[] = { "msf", "mss", "msvp", + "mta2", "mtaf", "mus", "musc", @@ -288,11 +293,9 @@ static const char* extension_list[] = { "vgs", "vgv", "vig", - "vds", "vdm", "vms", - "vms", "voi", "vpk", "vs", @@ -446,6 +449,7 @@ static const coding_info coding_info_list[] = { {coding_SASSC, "Activision / EXAKT SASSC 8-bit DPCM"}, {coding_LSF, "lsf 4-bit ADPCM"}, {coding_MTAF, "Konami MTAF 4-bit ADPCM"}, + {coding_MTA2, "Konami MTA2 4-bit ADPCM"}, {coding_MC3, "Paradigm MC3 3-bit ADPCM"}, #ifdef VGM_USE_VORBIS @@ -791,7 +795,7 @@ static const meta_info meta_info_list[] = { {meta_DSP_CABELAS, "Cabelas games dsp header"}, {meta_PS2_LPCM, "LPCM header"}, {meta_PS2_VMS, "VMS Header"}, - {meta_PS2_XAU, "XAU Header"}, + {meta_XAU, "XPEC XAU header"}, {meta_GH3_BAR, "Guitar Hero III Mobile .bar"}, {meta_FFW, "Freedom Fighters BGM header"}, {meta_DSP_DSPW, "DSPW dsp header"}, @@ -861,6 +865,7 @@ static const meta_info meta_info_list[] = { {meta_GTD, "GTD/GHS header"}, {meta_TA_AAC_X360, "tri-Ace AAC (X360) header"}, {meta_TA_AAC_PS3, "tri-Ace AAC (PS3) header"}, + {meta_PS3_MTA2, "Konami MTA2 header"}, #ifdef VGM_USE_VORBIS {meta_OGG_VORBIS, "Ogg Vorbis"}, diff --git a/Frameworks/vgmstream/vgmstream/src/meta/akb.c b/Frameworks/vgmstream/vgmstream/src/meta/akb.c index 7c34461c9..8a22de08a 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/akb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/akb.c @@ -1,5 +1,5 @@ -#include "../vgmstream.h" #include "meta.h" +#include "../coding/coding.h" #if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) /* AKB (AAC only) - found in SQEX iOS games */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ffmpeg.c b/Frameworks/vgmstream/vgmstream/src/meta/ffmpeg.c index 022f8a9eb..87c8662e6 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ffmpeg.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ffmpeg.c @@ -1,41 +1,11 @@ -#include "../vgmstream.h" #include "meta.h" -#include "../util.h" +#include "../coding/coding.h" #ifdef VGM_USE_FFMPEG -/* internal sizes, can be any value */ -#define FFMPEG_DEFAULT_BUFFER_SIZE 2048 -#define FFMPEG_DEFAULT_IO_BUFFER_SIZE 128 * 1024 - -static int init_seek(ffmpeg_codec_data * data); - - -static volatile int g_ffmpeg_initialized = 0; - -/* - * Global FFmpeg init - */ -static void g_init_ffmpeg() -{ - if (g_ffmpeg_initialized == 1) - { - while (g_ffmpeg_initialized < 2); - } - else if (g_ffmpeg_initialized == 0) - { - g_ffmpeg_initialized = 1; - av_log_set_flags(AV_LOG_SKIP_REPEATED); - av_log_set_level(AV_LOG_ERROR); - av_register_all(); - g_ffmpeg_initialized = 2; - } -} - - /** * Generic init FFmpeg and vgmstream for any file supported by FFmpeg. - * Always called by vgmstream when trying to identify the file type (if the player allows it). + * Called by vgmstream when trying to identify the file type (if the player allows it). */ VGMSTREAM * init_vgmstream_ffmpeg(STREAMFILE *streamFile) { return init_vgmstream_ffmpeg_offset( streamFile, 0, streamFile->get_size(streamFile) ); @@ -105,436 +75,4 @@ fail: return NULL; } - -/** - * AVIO callback: read stream, skipping external headers if needed - */ -static int ffmpeg_read(void *opaque, uint8_t *buf, int buf_size) -{ - ffmpeg_codec_data *data = (ffmpeg_codec_data *) opaque; - uint64_t offset = data->offset; - int max_to_copy = 0; - int ret; - - if (data->header_insert_block) { - if (offset < data->header_size) { - max_to_copy = (int)(data->header_size - offset); - if (max_to_copy > buf_size) { - max_to_copy = buf_size; - } - memcpy(buf, data->header_insert_block + offset, max_to_copy); - buf += max_to_copy; - buf_size -= max_to_copy; - offset += max_to_copy; - if (!buf_size) { - data->offset = offset; - return max_to_copy; - } - } - offset -= data->header_size; - } - - /* when "fake" size is smaller than "real" size we need to make sure bytes_read (ret) is clamped; - * it confuses FFmpeg in rare cases (STREAMFILE may have valid data after size) */ - if (offset + buf_size > data->size + data->header_size) { - buf_size = data->size - offset; /* header "read" is manually inserted later */ - } - - ret = read_streamfile(buf, offset + data->start, buf_size, data->streamfile); - if (ret > 0) { - offset += ret; - if (data->header_insert_block) { - ret += max_to_copy; - } - } - - if (data->header_insert_block) { - offset += data->header_size; - } - - data->offset = offset; - return ret; -} - -/** - * AVIO callback: write stream not needed - */ -static int ffmpeg_write(void *opaque, uint8_t *buf, int buf_size) -{ - return -1; -} - -/** - * AVIO callback: seek stream, skipping external headers if needed - */ -static int64_t ffmpeg_seek(void *opaque, int64_t offset, int whence) -{ - ffmpeg_codec_data *data = (ffmpeg_codec_data *) opaque; - int ret = 0; - - if (whence & AVSEEK_SIZE) { - return data->size + data->header_size; - } - whence &= ~(AVSEEK_SIZE | AVSEEK_FORCE); - /* false offsets, on reads data->start will be added */ - switch (whence) { - case SEEK_SET: - break; - - case SEEK_CUR: - offset += data->offset; - break; - - case SEEK_END: - offset += data->size; - if (data->header_insert_block) - offset += data->header_size; - break; - } - - /* clamp offset; fseek returns 0 when offset > size, too */ - if (offset > data->size + data->header_size) { - offset = data->size + data->header_size; - } - - data->offset = offset; - return ret; -} - - -/** - * Manually init FFmpeg, from an offset. - * Can be used if the stream has an extra header over data recognized by FFmpeg. - */ -ffmpeg_codec_data * init_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size) { - return init_ffmpeg_header_offset(streamFile, NULL, 0, start, size); -} - - -/** - * Manually init FFmpeg, from a fake header / offset. - * - * Can take a fake header, to trick FFmpeg into demuxing/decoding the stream. - * This header will be seamlessly inserted before 'start' offset, and total filesize will be 'header_size' + 'size'. - * The header buffer will be copied and memory-managed internally. - */ -ffmpeg_codec_data * init_ffmpeg_header_offset(STREAMFILE *streamFile, uint8_t * header, uint64_t header_size, uint64_t start, uint64_t size) { - char filename[PATH_LIMIT]; - - ffmpeg_codec_data * data; - - int errcode, i; - - int streamIndex, streamCount; - AVStream *stream; - AVCodecParameters *codecPar; - - AVRational tb; - - - /* basic setup */ - g_init_ffmpeg(); - - data = ( ffmpeg_codec_data * ) calloc(1, sizeof(ffmpeg_codec_data)); - if (!data) return NULL; - - streamFile->get_name( streamFile, filename, sizeof(filename) ); - - data->streamfile = streamFile->open(streamFile, filename, STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!data->streamfile) goto fail; - - data->start = start; - data->size = size; - - - /* insert fake header to trick FFmpeg into demuxing/decoding the stream */ - if (header_size > 0) { - data->header_size = header_size; - data->header_insert_block = av_memdup(header, header_size); - if (!data->header_insert_block) goto fail; - } - - /* setup IO, attempt to autodetect format and gather some info */ - data->buffer = av_malloc(FFMPEG_DEFAULT_IO_BUFFER_SIZE); - if (!data->buffer) goto fail; - - data->ioCtx = avio_alloc_context(data->buffer, FFMPEG_DEFAULT_IO_BUFFER_SIZE, 0, data, ffmpeg_read, ffmpeg_write, ffmpeg_seek); - if (!data->ioCtx) goto fail; - - data->formatCtx = avformat_alloc_context(); - if (!data->formatCtx) goto fail; - - data->formatCtx->pb = data->ioCtx; - - if ((errcode = avformat_open_input(&data->formatCtx, "", NULL, NULL)) < 0) goto fail; /* autodetect */ - - if ((errcode = avformat_find_stream_info(data->formatCtx, NULL)) < 0) goto fail; - - - /* find valid audio stream inside */ - streamIndex = -1; - streamCount = 0; /* audio streams only */ - - for (i = 0; i < data->formatCtx->nb_streams; ++i) { - stream = data->formatCtx->streams[i]; - codecPar = stream->codecpar; - if (streamIndex < 0 && codecPar->codec_type == AVMEDIA_TYPE_AUDIO) { - streamIndex = i; /* select first audio stream found */ - } else { - stream->discard = AVDISCARD_ALL; /* disable demuxing unneded streams */ - } - if (codecPar->codec_type == AVMEDIA_TYPE_AUDIO) - streamCount++; - } - - if (streamIndex < 0) goto fail; - - data->streamIndex = streamIndex; - stream = data->formatCtx->streams[streamIndex]; - data->streamCount = streamCount; - - - /* prepare codec and frame/packet buffers */ - data->codecCtx = avcodec_alloc_context3(NULL); - if (!data->codecCtx) goto fail; - - if ((errcode = avcodec_parameters_to_context(data->codecCtx, codecPar)) < 0) goto fail; - - av_codec_set_pkt_timebase(data->codecCtx, stream->time_base); - - data->codec = avcodec_find_decoder(data->codecCtx->codec_id); - if (!data->codec) goto fail; - - if ((errcode = avcodec_open2(data->codecCtx, data->codec, NULL)) < 0) goto fail; - - data->lastDecodedFrame = av_frame_alloc(); - if (!data->lastDecodedFrame) goto fail; - av_frame_unref(data->lastDecodedFrame); - - data->lastReadPacket = malloc(sizeof(AVPacket)); - if (!data->lastReadPacket) goto fail; - av_new_packet(data->lastReadPacket, 0); - - data->readNextPacket = 1; - data->bytesConsumedFromDecodedFrame = INT_MAX; - - - /* other setup */ - data->sampleRate = data->codecCtx->sample_rate; - data->channels = data->codecCtx->channels; - data->floatingPoint = 0; - - switch (data->codecCtx->sample_fmt) { - case AV_SAMPLE_FMT_U8: - case AV_SAMPLE_FMT_U8P: - data->bitsPerSample = 8; - break; - - case AV_SAMPLE_FMT_S16: - case AV_SAMPLE_FMT_S16P: - data->bitsPerSample = 16; - break; - - case AV_SAMPLE_FMT_S32: - case AV_SAMPLE_FMT_S32P: - data->bitsPerSample = 32; - break; - - case AV_SAMPLE_FMT_FLT: - case AV_SAMPLE_FMT_FLTP: - data->bitsPerSample = 32; - data->floatingPoint = 1; - break; - - case AV_SAMPLE_FMT_DBL: - case AV_SAMPLE_FMT_DBLP: - data->bitsPerSample = 64; - data->floatingPoint = 1; - break; - - default: - goto fail; - } - - data->bitrate = (int)(data->codecCtx->bit_rate); - data->endOfStream = 0; - data->endOfAudio = 0; - - /* try to guess frames/samples (duration isn't always set) */ - tb.num = 1; tb.den = data->codecCtx->sample_rate; - data->totalSamples = av_rescale_q(stream->duration, stream->time_base, tb); - if (data->totalSamples < 0) - data->totalSamples = 0; /* caller must consider this */ - - data->blockAlign = data->codecCtx->block_align; - data->frameSize = data->codecCtx->frame_size; - if(data->frameSize == 0) /* some formats don't set frame_size but can get on request, and vice versa */ - data->frameSize = av_get_audio_frame_duration(data->codecCtx,0); - - /* setup decode buffer */ - data->sampleBufferBlock = FFMPEG_DEFAULT_BUFFER_SIZE; - data->sampleBuffer = av_malloc( data->sampleBufferBlock * (data->bitsPerSample / 8) * data->channels ); - if (!data->sampleBuffer) - goto fail; - - - /* setup decent seeking for faulty formats */ - errcode = init_seek(data); - if (errcode < 0) goto fail; - - /* expose start samples to be skipped (encoder delay, usually added by MDCT-based encoders like AAC/MP3/ATRAC3/XMA/etc) - * get after init_seek because some demuxers like AAC only fill skip_samples for the first packet */ - if (stream->start_skip_samples) /* samples to skip in the first packet */ - data->skipSamples = stream->start_skip_samples; - else if (stream->skip_samples) /* samples to skip in any packet (first in this case), used sometimes instead (ex. AAC) */ - data->skipSamples = stream->skip_samples; - - return data; - -fail: - free_ffmpeg(data); - - return NULL; -} - - -/** - * Special patching for FFmpeg's buggy seek code. - * - * To seek with avformat_seek_file/av_seek_frame, FFmpeg's demuxers can implement read_seek2 (newest API) - * or read_seek (older API), with various search modes. If none are available it will use seek_frame_generic, - * which manually reads frame by frame until the selected timestamp. However, the prev frame will be consumed - * (so after seeking to 0 next av_read_frame will actually give the second frame and so on). - * - * Fortunately seek_frame_generic can use an index to find the correct position. This function reads the - * first frame/packet and sets up index to timestamp 0. This ensures faulty demuxers will seek to 0 correctly. - * Some formats may not seek to 0 even with this, though. - */ -static int init_seek(ffmpeg_codec_data * data) { - int ret, ts_index, found_first = 0; - int64_t ts = 0; - int64_t pos = 0; /* offset */ - int size = 0; /* coded size */ - int distance = 0; /* always? */ - - AVStream * stream; - AVPacket * pkt; - - stream = data->formatCtx->streams[data->streamIndex]; - pkt = data->lastReadPacket; - - /* read_seek shouldn't need this index, but direct access to FFmpeg's internals is no good */ - /* if (data->formatCtx->iformat->read_seek || data->formatCtx->iformat->read_seek2) - return 0; */ - - /* some formats already have a proper index (e.g. M4A) */ - ts_index = av_index_search_timestamp(stream, ts, AVSEEK_FLAG_ANY); - if (ts_index>=0) - goto test_seek; - - - /* find the first + second packets to get pos/size */ - while (1) { - av_packet_unref(pkt); - ret = av_read_frame(data->formatCtx, pkt); - if (ret < 0) - break; - if (pkt->stream_index != data->streamIndex) - continue; /* ignore non-selected streams */ - - if (!found_first) { /* first found */ - found_first = 1; - pos = pkt->pos; - ts = pkt->dts; - continue; - } else { /* second found */ - size = pkt->pos - pos; /* coded, pkt->size is decoded size */ - break; - } - } - if (!found_first) - goto fail; - - /* in rare cases there is only one packet */ - /* if (size == 0) { size = data_end - pos; } */ /* no easy way to know, ignore (most formats don's need size) */ - - /* some formats (XMA1) don't seem to have packet.dts, pretend it's 0 */ - if (ts == INT64_MIN) - ts = 0; - - /* Some streams start with negative DTS (observed in Ogg). For Ogg seeking to negative or 0 doesn't alter the output. - * It does seem seeking before decoding alters a bunch of (inaudible) +-1 lower bytes though. */ - VGM_ASSERT(ts != 0, "FFMPEG: negative start_ts (%li)\n", (long)ts); - if (ts != 0) - ts = 0; - - /* add index 0 */ - ret = av_add_index_entry(stream, pos, ts, size, distance, AVINDEX_KEYFRAME); - if ( ret < 0 ) - return ret; - - -test_seek: - /* seek to 0 test / move back to beginning, since we just consumed packets */ - ret = avformat_seek_file(data->formatCtx, data->streamIndex, ts, ts, ts, AVSEEK_FLAG_ANY); - if ( ret < 0 ) - return ret; /* we can't even reset_vgmstream the file */ - - avcodec_flush_buffers(data->codecCtx); - - return 0; - - -fail: - return -1; -} - - -void free_ffmpeg(ffmpeg_codec_data *data) { - if (data == NULL) - return; - - if (data->lastReadPacket) { - av_packet_unref(data->lastReadPacket); - free(data->lastReadPacket); - data->lastReadPacket = NULL; - } - if (data->lastDecodedFrame) { - av_free(data->lastDecodedFrame); - data->lastDecodedFrame = NULL; - } - if (data->codecCtx) { - avcodec_close(data->codecCtx); - avcodec_free_context(&(data->codecCtx)); - data->codecCtx = NULL; - } - if (data->formatCtx) { - avformat_close_input(&(data->formatCtx)); - data->formatCtx = NULL; - } - if (data->ioCtx) { - // buffer passed in is occasionally freed and replaced. - // the replacement must be freed as well. - data->buffer = data->ioCtx->buffer; - av_free(data->ioCtx); - data->ioCtx = NULL; - } - if (data->buffer) { - av_free(data->buffer); - data->buffer = NULL; - } - if (data->sampleBuffer) { - av_free(data->sampleBuffer); - data->sampleBuffer = NULL; - } - if (data->header_insert_block) { - av_free(data->header_insert_block); - data->header_insert_block = NULL; - } - if (data->streamfile) { - close_streamfile(data->streamfile); - data->streamfile = NULL; - } - free(data); -} #endif diff --git a/Frameworks/vgmstream/vgmstream/src/meta/gtd.c b/Frameworks/vgmstream/vgmstream/src/meta/gtd.c index 8aaf228ab..8550c79eb 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/gtd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/gtd.c @@ -19,7 +19,7 @@ VGMSTREAM * init_vgmstream_gtd(STREAMFILE *streamFile) { if (read_32bitBE(0x00,streamFile) != 0x47485320) /* "GHS " */ goto fail; -VGM_LOG("1"); + /* header type, not formally specified */ if (read_32bitBE(0x04,streamFile) == 1 && read_16bitBE(0x0C,streamFile) == 0x0166) { /* XMA2 */ /* 0x08(4): seek table size */ @@ -55,8 +55,8 @@ VGM_LOG("1"); vgmstream->meta_type = meta_GTD; switch(codec) { - case XMA2: { #ifdef VGM_USE_FFMPEG + case XMA2: { uint8_t buf[0x100]; size_t bytes; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/meta.h b/Frameworks/vgmstream/vgmstream/src/meta/meta.h index 7f0be80be..1e2596343 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/meta.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/meta.h @@ -121,21 +121,14 @@ VGMSTREAM * init_vgmstream_sli_ogg(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_hca(STREAMFILE *streamFile); #ifdef VGM_USE_FFMPEG -ffmpeg_codec_data * init_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size); -ffmpeg_codec_data * init_ffmpeg_header_offset(STREAMFILE *streamFile, uint8_t * header, uint64_t header_size, uint64_t start, uint64_t size); - -void free_ffmpeg(ffmpeg_codec_data *data); - -VGMSTREAM * init_vgmstream_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size); - VGMSTREAM * init_vgmstream_ffmpeg(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size); VGMSTREAM * init_vgmstream_mp4_aac_ffmpeg(STREAMFILE * streamFile); #endif #if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) VGMSTREAM * init_vgmstream_mp4_aac(STREAMFILE * streamFile); - VGMSTREAM * init_vgmstream_mp4_aac_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size); VGMSTREAM * init_vgmstream_akb(STREAMFILE *streamFile); @@ -541,7 +534,7 @@ VGMSTREAM * init_vgmstream_dsp_bdsp(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_ps2_vms(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_ps2_xau(STREAMFILE* streamFile); +VGMSTREAM * init_vgmstream_xau(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_gh3_bar(STREAMFILE* streamFile); @@ -677,4 +670,6 @@ VGMSTREAM * init_vgmstream_gtd(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ta_aac_x360(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ta_aac_ps3(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_ps3_mta2(STREAMFILE *streamFile); + #endif /*_META_H*/ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/mp4.c b/Frameworks/vgmstream/vgmstream/src/meta/mp4.c index 2bbf3ca2d..ee8e56c7b 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/mp4.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/mp4.c @@ -1,6 +1,5 @@ -#include "../vgmstream.h" #include "meta.h" -#include "../util.h" +#include "../coding/coding.h" #ifdef VGM_USE_MP4V2 void* mp4_file_open( const char* name, MP4FileMode mode ) diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_ast.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_ast.c index 6acbb7cd7..3c9ef56bf 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_ast.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_ast.c @@ -1,91 +1,62 @@ #include "meta.h" -#include "../util.h" +#include "../coding/coding.h" -/* AST */ +/* AST - from Koei and Marvelous games (same internal dev?) */ VGMSTREAM * init_vgmstream_ps2_ast(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; off_t start_offset; + int loop_flag, channel_count, variant_type; - int loop_flag = 0; - int channel_count; - int variant_type; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("ast",filename_extension(filename))) goto fail; + /* check extension */ + if (!check_extensions(streamFile,"ast")) goto fail; /* check header */ if (read_32bitBE(0x00,streamFile) != 0x41535400) /* "AST\0" */ goto fail; - /* determine variant */ - if (read_32bitBE(0x10,streamFile) == 0) - { - variant_type = 1; - } - else - { - variant_type = 2; - } - + /* determine variant (after 0x10 is garbage/code data in type 1 until 0x800, but consistent in all songs) */ + if (read_32bitBE(0x10,streamFile) == 0x00000000 || read_32bitBE(0x10,streamFile) == 0x20002000) { + variant_type = 1; /* Koei: P.T.O. IV (0x00000000), Naval Ops: Warship Gunner (0x20002000) */ + channel_count = 2; + } + else { + variant_type = 2; /* Marvelous: Katekyoo Hitman Reborn! Dream Hyper Battle!, Binchou-tan: Shiawasegoyomi */ + channel_count = read_32bitLE(0x0C,streamFile); + } loop_flag = 0; - channel_count = 2; - /* build the VGMSTREAM */ + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ - if (variant_type == 1) - { - start_offset = 0x800; - channel_count = 2; - vgmstream->channels = channel_count; - vgmstream->sample_rate = read_32bitLE(0x04,streamFile); - vgmstream->num_samples = (read_32bitLE(0x0C,streamFile)-start_offset)*28/16/channel_count; - vgmstream->interleave_block_size = read_32bitLE(0x08,streamFile); - loop_flag = 0; - } - else if (variant_type == 2) - { - start_offset = 0x100; - channel_count = read_32bitLE(0x0C,streamFile); - vgmstream->channels = channel_count; - vgmstream->sample_rate = read_32bitLE(0x08,streamFile); - vgmstream->num_samples = (read_32bitLE(0x04,streamFile)-start_offset)*28/16/channel_count; - vgmstream->interleave_block_size = read_32bitLE(0x10,streamFile); - } - else - { + /* fill in the vital statistics */ + if (variant_type == 1) { + start_offset = 0x800; + vgmstream->sample_rate = read_32bitLE(0x04,streamFile); + vgmstream->num_samples = ps_bytes_to_samples(read_32bitLE(0x0C,streamFile)-start_offset,channel_count); + vgmstream->interleave_block_size = read_32bitLE(0x08,streamFile); + } + else if (variant_type == 2) { + start_offset = 0x100; + vgmstream->sample_rate = read_32bitLE(0x08,streamFile); + vgmstream->num_samples = ps_bytes_to_samples(read_32bitLE(0x04,streamFile)-start_offset,channel_count); + vgmstream->interleave_block_size = read_32bitLE(0x10,streamFile); + } + else { goto fail; } vgmstream->layout_type = layout_interleave; - vgmstream->coding_type = coding_PSX; - vgmstream->meta_type = meta_PS2_AST; + vgmstream->coding_type = coding_PSX; + vgmstream->meta_type = meta_PS2_AST; - /* open the file for reading */ - { - int i; - STREAMFILE * file; - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - for (i=0;ich[i].streamfile = file; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=start_offset+ - vgmstream->interleave_block_size*i; - - } - } + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; return vgmstream; - /* clean up anything we may have opened */ fail: - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_mtaf.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_mtaf.c index 8465e3563..0c99479a3 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_mtaf.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_mtaf.c @@ -1,182 +1,94 @@ -//#include #include "meta.h" #include "../util.h" -/* MTAF (Metal Gear Solid 3: Snake Eater) */ + +/* MTAF - found in Metal Gear Solid 3: Snake Eater (Subsistence and HD too) */ VGMSTREAM * init_vgmstream_ps2_mtaf(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; off_t start_offset; + int loop_flag, channel_count; + int32_t loop_start, loop_end; - int stream_count; - int loop_flag = 1; - int channel_count; - int32_t loop_start; - int32_t loop_end; - int i; + /* check extension */ + if ( !check_extensions(streamFile,"mtaf")) + goto fail; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("mtaf",filename_extension(filename))) goto fail; + /* base header */ + if (read_32bitBE(0x00, streamFile) != 0x4d544146) /* "MTAF" */ + goto fail; + /* 0x04(4): pseudo file size (close but smaller) */ + /* 0x08(4): version? (0), 0x0c(20): null, 0x30(32): some kind of id or config? */ - /* check header */ - // master MTAF header (mostly useless) + /* HEAD chunk */ + if (read_32bitBE(0x40, streamFile) != 0x48454144) /* "HEAD" */ + goto fail; + if (read_32bitLE(0x44, streamFile) != 0xB0) /* HEAD size */ + goto fail; - if (read_32bitBE(0, streamFile) != 0x4d544146) // "MTAF" - { - //fprintf(stderr, "no MTAF header at 0x%08lx\n", cur_off); + + /* 0x48(4): null, 0x4c: usually channel count (sometimes 0x10 with 2ch), 0x50(4): 0x7F (vol?), 0x54(2): 0x40 (pan?) */ + channel_count = 2 * read_8bit(0x61, streamFile); /* 0x60(4): full block size (0x110 * channels), but this works */ + /* 0x70(4): ? (00/05/07), 0x80 .. 0xf8: null */ + + loop_start = read_32bitLE(0x58, streamFile); + loop_end = read_32bitLE(0x5c, streamFile); + loop_flag = (loop_start != loop_end); + + /* check loop points vs frame counts */ + if (loop_start/0x100 != read_32bitLE(0x64, streamFile) || + loop_end /0x100 != read_32bitLE(0x68, streamFile) ) { + VGM_LOG("MTAF: wrong loop points\n"); goto fail; } - //const uint32_t pseudo_size = readint32(&mtaf_header_buf[4]); + /* TRKP chunks (x16) */ + /* just seem to contain pan/vol stuff (0x7f/0x40), one TRKP with data per channel and the rest fixed values */ - // check the rest is clear - for (i = 0x8; i < 0x20; i++) - { - if (read_8bit(i, streamFile) != 0) - { - //fprintf(stderr, "unexpected nonzero in MTAF header at 0x%08lx\n", cur_off+i); - goto fail; - } - } - - // ignore the rest for now - - // HEAD chunk header - - if (read_32bitBE(0x40, streamFile) != 0x48454144) // "HEAD" - { - //fprintf(stderr, "no HEAD chunk at 0x%08lx\n", cur_off); - goto fail; - } - - { - uint32_t mtaf_head_chunk_size = read_32bitLE(0x44, streamFile); - if (mtaf_head_chunk_size != 0xB0) - { - //fprintf(stderr, "unexpected size for MTAF header at 0x%08lx\n", cur_off); - goto fail; - } - } - - stream_count = read_8bit(0x61, streamFile); - - // check some standard stuff - if ( 0 != read_32bitLE(0x48, streamFile) || - 0x7F != read_32bitLE(0x50, streamFile) || - 0x40 != read_32bitLE(0x54, streamFile) || - 0 != read_16bitLE(0x62, streamFile) || - 0 != read_32bitLE(0x6c, streamFile)) // || - //5 != readint32(&mtaf_header_buf[0x68])) || - //(dc.streams==3 ? 12:0) != readint32(&mtaf_header_buf[0x7c])) - { - //fprintf(stderr, "unexpected header values at 0x%08lx\n", cur_off); + /* DATA chunk */ + if (read_32bitBE(0x7f8, streamFile) != 0x44415441) /* "DATA" */ goto fail; - } + /* 0x7fc: data size (without blocks in case of blocked layout) */ - // 0 streams should be impossible - if (stream_count == 0) - { - //fprintf(stderr, "0 streams at 0x%08lx\n", cur_off); - goto fail; - } + /* without blocks it should start with 0x00000100 ("frame 1 from track 0") */ + //is_blocked = read_32bitLE(0x800,streamFile) != 0x00000100 && read_32bitLE(0x810,streamFile) == 0x00000100; - // check the other stream count indicator - if (stream_count*0x10 != read_8bit(0x60, streamFile)) - { - //fprintf(stderr, "secondary stream count mismatch at 0x%08lx\n", cur_off); - goto fail; - } - -#if 0 - // maybe this is how to compute channels per stream? - // check total channel count - if (2*stream_count != read_32bitLE(0x4c, streamFile)) - { - //fprintf(stderr, "total channel count does not match stream count at 0x%08lx\n", cur_off); - goto fail; - } -#endif - - // check loop points as frame counts - if (read_32bitLE(0x64, streamFile) != read_32bitLE(0x58, streamFile)/0x100 || - read_32bitLE(0x68, streamFile) != read_32bitLE(0x5c, streamFile)/0x100) - { - //fprintf(stderr, "loop frame count mismatch at 0x%lx\n", cur_off); - goto fail; - } - - // check that rest is clear - for (i = 0x78; i < 0xf8; i++) - { - if (read_8bit(i, streamFile) != 0) - { - //fprintf(stderr, "unexpected nonzero in HEAD chunk at 0x%lx\n", cur_off+i); - goto fail; - } - } - - // check TRKP chunks - for (i = 0; i < 16; i++) - { - if (read_32bitBE(0xf8+0x70*i, streamFile) != 0x54524b50 || // "TRKP" - read_32bitLE(0xf8+0x70*i+4, streamFile) != 0x68) - { - //fprintf(stderr, "missing or unusual TRKP chunk #%d at 0x%lx\n", i, cur_off); - goto fail; - } - } - - // check for grand finale, DATA - if (read_32bitBE(0x7f8, streamFile) != 0x44415441) // "DATA" - { - //fprintf(stderr, "missing DATA header at 0x%lx\n", cur_off); - goto fail; - } start_offset = 0x800; - // seems to always be the case - channel_count = 2 * stream_count; - - loop_start = read_32bitLE(0x58, streamFile); - loop_end = read_32bitLE(0x5c, streamFile); - if (loop_start == loop_end) loop_flag = 0; - + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count, loop_flag); if (!vgmstream) goto fail; - // a guess - vgmstream->channels = channel_count; - vgmstream->sample_rate = 48000; - vgmstream->coding_type = coding_MTAF; - vgmstream->num_samples = read_32bitLE(0x5c, streamFile); - + vgmstream->sample_rate = 48000; /* always */ + vgmstream->num_samples = loop_end; vgmstream->loop_start_sample = loop_start; vgmstream->loop_end_sample = loop_end; - vgmstream->interleave_block_size = 0x110/2; - + 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; - //const uint32_t pseudo_data_size = readint32(&mtaf_header_buf[4]); - // TODO: first block + /* open the file for reading, in a specific way */ + { + int i; + char filename[PATH_LIMIT]; - /* open the file for reading */ - for (i = 0; i < channel_count; i++) { - STREAMFILE * file = streamFile->open(streamFile,filename,vgmstream->interleave_block_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); + streamFile->get_name(streamFile,filename,sizeof(filename)); + for (i = 0; i < channel_count; i++) { + STREAMFILE * file = streamFile->open(streamFile,filename,vgmstream->interleave_block_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); + } } return vgmstream; fail: - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_strlr.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_strlr.c index cdfe4a14f..ce56b8cc7 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_strlr.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_strlr.c @@ -23,6 +23,10 @@ VGMSTREAM * init_vgmstream_ps2_strlr(STREAMFILE *streamFile) { goto fail; #endif + /* don't hijack Sonic & Sega All Stars Racing X360 (xma) */ + if (read_32bitBE(0x00,streamFile) == 0x52494646) /* "RIFF"*/ + goto fail; + loop_flag = 0; channel_count = 2; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_xau.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_xau.c deleted file mode 100644 index 434a43b71..000000000 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_xau.c +++ /dev/null @@ -1,67 +0,0 @@ -#include "meta.h" -#include "../util.h" - -/* XAU (Spectral Force Chronicle [SLPM-65967]) */ -VGMSTREAM * init_vgmstream_ps2_xau(STREAMFILE *streamFile) -{ - VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - off_t start_offset; - - int loop_flag = 0; - int channel_count; - - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("xau",filename_extension(filename))) goto fail; - - /* check header */ - if (read_32bitBE(0x00,streamFile) != 0x58415500) - goto fail; - - loop_flag = 0; - channel_count = read_8bit(0x18,streamFile); - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count, loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - start_offset = 0x800; - - vgmstream->channels = channel_count; - vgmstream->sample_rate = read_32bitBE(0x50, streamFile); - vgmstream->coding_type = coding_PSX; - vgmstream->num_samples = ((read_32bitBE(0x4C, streamFile) * channel_count)/ 16 / channel_count * 28); - - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x8000; - vgmstream->meta_type = meta_PS2_XAU; - - /* open the file for reading */ - { - int i; - STREAMFILE * file; - - file = streamFile->open(streamFile, filename, STREAMFILE_DEFAULT_BUFFER_SIZE); - - if (!file) goto fail; - - for (i=0;ich[i].streamfile = file; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=start_offset + vgmstream->interleave_block_size * i; - - } - } - - return vgmstream; - - /* clean up anything we may have opened */ -fail: - if (vgmstream) close_vgmstream(vgmstream); - return NULL; -} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps3_msf.c b/Frameworks/vgmstream/vgmstream/src/meta/ps3_msf.c index 226f07e74..18e52a37d 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps3_msf.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps3_msf.c @@ -34,7 +34,7 @@ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) { /* byte flags, not in MSFv1 or v2 * 0x01/02/04/08: loop marker 0/1/2/3 - * 0x10: "resample" loop option (may be active with no 0x01 flag set) + * 0x10: resample options (force 44/48khz) * 0x20: VBR MP3 * 0x40: joint stereo MP3 (apparently interleaved stereo for other formats) * 0x80+: (none/reserved) */ @@ -104,17 +104,19 @@ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) { case 0x6: { /* ATRAC3 high (132 kbps, frame size 192) */ ffmpeg_codec_data *ffmpeg_data = NULL; uint8_t buf[100]; - int32_t bytes, block_size, encoder_delay, joint_stereo, max_samples; + int32_t bytes, block_size, encoder_delay, joint_stereo; block_size = (codec_id==4 ? 0x60 : (codec_id==5 ? 0x98 : 0xC0)) * vgmstream->channels; - encoder_delay = 0x0; //todo MSF encoder delay (around 440-450*2) - max_samples = atrac3_bytes_to_samples(data_size, block_size); - joint_stereo = codec_id==4; /* interleaved joint stereo (ch must be even) */ + joint_stereo = (codec_id==4); /* interleaved joint stereo (ch must be even) */ + /* MSF skip samples: from tests with MSEnc and real files (ex. TTT2 eddy.msf v43, v01 demos) seems like 1162 is consistent. + * Atelier Rorona bt_normal01 needs it to properly skip the beginning garbage but usually doesn't matter. + * (note that encoder may add a fade-in with looping/resampling enabled but should be skipped) */ + encoder_delay = 1162; + vgmstream->num_samples = atrac3_bytes_to_samples(data_size, block_size) - encoder_delay; if (vgmstream->sample_rate==0xFFFFFFFF) /* some MSFv1 (Digi World SP) */ vgmstream->sample_rate = 44100;//voice tracks seems to use 44khz, not sure about other tracks - /* make a fake riff so FFmpeg can parse the ATRAC3 */ bytes = ffmpeg_make_riff_atrac3(buf, 100, vgmstream->num_samples, data_size, vgmstream->channels, vgmstream->sample_rate, block_size, joint_stereo, encoder_delay); if (bytes <= 0) goto fail; @@ -124,10 +126,16 @@ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) { vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; - vgmstream->num_samples = max_samples; + + /* manually set skip_samples if FFmpeg didn't do it */ + if (ffmpeg_data->skipSamples <= 0) { + ffmpeg_set_skip_samples(ffmpeg_data, encoder_delay); + } + + /* MSF loop/sample values are offsets so trickier to adjust the skip_samples but this seems correct */ if (loop_flag) { - vgmstream->loop_start_sample = atrac3_bytes_to_samples(loop_start, block_size); - vgmstream->loop_end_sample = atrac3_bytes_to_samples(loop_end, block_size); + vgmstream->loop_start_sample = atrac3_bytes_to_samples(loop_start, block_size) /* - encoder_delay*/; + vgmstream->loop_end_sample = atrac3_bytes_to_samples(loop_end, block_size) - encoder_delay; } break; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps3_mta2.c b/Frameworks/vgmstream/vgmstream/src/meta/ps3_mta2.c new file mode 100644 index 000000000..83825f812 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps3_mta2.c @@ -0,0 +1,104 @@ +#include "meta.h" +#include "../util.h" + + +/* MTA2 - found in Metal Gear Solid 4 */ +VGMSTREAM * init_vgmstream_ps3_mta2(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t header_offset, start_offset; + int loop_flag, channel_count, sample_rate; //block_offset; + int32_t loop_start, loop_end; + + + /* check extension */ + /* .mta2: normal file, .bgm: mta2 with block layout, .dbm: iPod metadata + block layout mta2 */ + if ( !check_extensions(streamFile,"mta2,bgm,dbm")) + 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) */ + /* 0x08(4): version? (1), 0x0c(52): null */ + + + /* HEAD chunk */ + if (read_32bitBE(header_offset+0x40, streamFile) != 0x48454144) /* "HEAD" */ + goto fail; + if (read_32bitBE(header_offset+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 */ + /* 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_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) ) { + 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; + } else { /* default when not specified (most of the time) */ + sample_rate = 48000; + } + + + /* TRKP chunks (x16) */ + /* just seem to contain pan/vol stuff (0x7f/0x40), TRKP per track (sometimes +1 main track?) */ + /* there is channel layout bitmask @ 0x0f (ex. 1ch = 0x04, 3ch = 0x07, 4ch = 0x33, 6ch = 0x3f), surely: + * 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" + goto fail; + /* 0x7fc: data size (without blocks in case of blocked layout) */ + + start_offset = header_offset + 0x800; + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = loop_end; + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end; + + vgmstream->coding_type = coding_MTA2; + vgmstream->layout_type = layout_none; + vgmstream->meta_type = meta_PS3_MTA2; + + /* open the file for reading */ + if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/riff.c b/Frameworks/vgmstream/vgmstream/src/meta/riff.c index 782414310..3eafe01c8 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/riff.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/riff.c @@ -174,7 +174,6 @@ int read_fmt(int big_endian, case 0xFFFE: /* WAVEFORMATEXTENSIBLE / ATRAC3plus */ #endif /* defined */ fmt->coding_type = coding_FFmpeg; - fmt->block_size = 2048; fmt->interleave = 0; break; #endif /* VGM_USE_FFMPEG */ @@ -408,10 +407,20 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { if ( !ffmpeg_data ) goto fail; sample_count = ffmpeg_data->totalSamples; /* fact_sample_count */ - /* the encoder introduces some garbage (usually silent) samples to skip before the stream - * loop values include the skip samples but fact_sample_count doesn't; add them back to fix some edge loops */ - if (fact_sample_skip > 0) - sample_count += fact_sample_skip; + + if (at3) { + /* the encoder introduces some garbage (not always silent) samples to skip before the stream */ + /* manually set skip_samples if FFmpeg didn't do it */ + if (ffmpeg_data->skipSamples <= 0) { + ffmpeg_set_skip_samples(ffmpeg_data, fact_sample_skip); + } + + /* RIFF loop/sample values are absolute (with skip samples), adjust */ + if (loop_flag) { + loop_start_offset -= ffmpeg_data->skipSamples; + loop_end_offset -= ffmpeg_data->skipSamples; + } + } } break; #endif diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sgxd.c b/Frameworks/vgmstream/vgmstream/src/meta/sgxd.c index 5e5e0f3b6..2ed824cae 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sgxd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/sgxd.c @@ -1,49 +1,28 @@ #include "meta.h" -#include "../util.h" +#include "../coding/coding.h" -/* utils to fix AT3 looping */ -typedef struct { - int32_t fact_samples; - int32_t loop_start_sample; - int32_t loop_end_sample; - int32_t skip_samples; -} at3_riff_info; -static int get_at3_riff_info(at3_riff_info* info, STREAMFILE *streamFile, int32_t offset); - - -/* Sony's SGB+SGH / SGD / SGX (variations of the same format) - * PS3: Genji (SGX only), Folklore, Afrika, Tokyo Jungle - * PSP: Brave Story, Sarugetchu Sarusaru Daisakusen, Kurohyo 1/2 - * - * Contains header + chunks, usually: - * WAVE: stream(s) header of ADPCM, AC3, ATRAC3plus, etc - * NAME: stream name(s) - * WSUR, WMRK, BUSS: unknown - * RGND, SEQD: unknown (related to SE) - * Then data, containing the original header if applicable (ex. AT3 RIFF). - * The SGXD header has priority over it (ex. some ATRAC3plus files have 48000 while the data RIFF 44100) - */ +/* SGXD - Sony/SCEI's format (SGB+SGH / SGD / SGX), found in: + * PS3: Genji, Folklore, Afrika (Short VAG), Tokyo Jungle + * PSP: Brave Story, Sarugetchu Sarusaru Daisakusen, Kurohyo 1/2, Pathwork Heroes */ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; STREAMFILE * streamHeader = NULL; - off_t start_offset, data_offset, chunk_offset; size_t data_size; int is_sgx, is_sgb; int loop_flag, channels, type; int sample_rate, num_samples, loop_start_sample, loop_end_sample; - int target_stream = 0, total_streams; /* check extension, case insensitive */ + /* .sgx: header+data (Genji), .sgd: header+data, .sgh/sgd: header/data */ if (!check_extensions(streamFile,"sgx,sgd,sgb")) goto fail; is_sgx = check_extensions(streamFile,"sgx"); is_sgb = check_extensions(streamFile,"sgb"); - //is_sgd = check_extensions(streamFile,"sgd"); /* SGB+SGH: use SGH as header; otherwise use the current file as header */ if (is_sgb) { @@ -69,14 +48,15 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) { } + /* typical chunks: WAVE, NAME (strings), RGND, SEQD (related to SFX), WSUR, WMKR, BUSS */ /* WAVE chunk (size 0x10 + files * 0x38 + optional padding) */ - /* 0x04 SGX: unknown; SGD/SGH: chunk length, 0x08 null */ if (is_sgx) { /* position after chunk+size */ if (read_32bitBE(0x10,streamHeader) != 0x57415645) goto fail; /* "WAVE" */ chunk_offset = 0x18; } else { if (!find_chunk_le(streamHeader, 0x57415645,0x10,0, &chunk_offset,NULL)) goto fail; /* "WAVE" */ } + /* 0x04 SGX: unknown; SGD/SGH: chunk length, 0x08 null */ /* check multi-streams (usually only SE containers; Puppeteer) */ total_streams = read_32bitLE(chunk_offset+0x04,streamHeader); @@ -107,7 +87,7 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) { data_size = read_32bitLE(chunk_offset+0x2c,streamHeader); /* stream size (without padding) / interleave (for type3) */ if (is_sgx) { - stream_offset = 0x0; /* TODO unknown (not seen multi SGX) */ + stream_offset = 0x0; } else{ stream_offset = read_32bitLE(chunk_offset+0x30,streamHeader); } @@ -128,41 +108,53 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) { vgmstream->num_streams = total_streams; vgmstream->meta_type = meta_SGXD; + /* needs -1 to match RIFF AT3's loop chunk + * (maybe SGXD = "loop before this sample" rather than "loop after this sample" as expected by vgmstream) */ + if (vgmstream->loop_end_sample > 0) + vgmstream->loop_end_sample -= 1; + switch (type) { - case 0x03: /* PSX ADPCM */ + case 0x03: /* PS-ADPCM */ vgmstream->coding_type = coding_PSX; vgmstream->layout_type = layout_interleave; if (is_sgx || is_sgb) { vgmstream->interleave_block_size = 0x10; - } else { //todo this only seems to happen with SFX + } else { /* this only seems to happen with SFX */ vgmstream->interleave_block_size = data_size; } break; #ifdef VGM_USE_FFMPEG - case 0x04: { /* ATRAC3plus */ - at3_riff_info info; - ffmpeg_codec_data *ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset, data_size); + case 0x04: { /* ATRAC3plus */ + ffmpeg_codec_data *ffmpeg_data; + + /* internally has a RIFF header; but the SGXD header / sample rate has priority over it (may not match) */ + ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset, data_size); if ( !ffmpeg_data ) goto fail; vgmstream->codec_data = ffmpeg_data; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; - /* manually fix looping due to FFmpeg bugs */ - if (loop_flag && get_at3_riff_info(&info, streamFile, start_offset)) { - if (vgmstream->num_samples == info.fact_samples) { /* use if looks normal */ - /* todo use "skip samples"; for now we just use absolute loop values */ - vgmstream->loop_start_sample = info.loop_start_sample; - vgmstream->loop_end_sample = info.loop_end_sample; - vgmstream->num_samples += info.skip_samples; /* to ensure it always reaches loop_end */ + /* manually read skip_samples if FFmpeg didn't do it */ + if (ffmpeg_data->skipSamples <= 0) { + off_t chunk_offset; + size_t chunk_size, fact_skip_samples = 0; + if (!find_chunk_le(streamFile, 0x66616374,start_offset+0xc,0, &chunk_offset,&chunk_size)) /* find "fact" */ + goto fail; + if (chunk_size == 0x8) { + fact_skip_samples = read_32bitLE(chunk_offset+0x4, streamFile); + } else if (chunk_size == 0xc) { + fact_skip_samples = read_32bitLE(chunk_offset+0x8, streamFile); } + ffmpeg_set_skip_samples(ffmpeg_data, fact_skip_samples); } + /* SGXD loop/sample values are relative (without skip samples) vs RIFF (with skip samples), no need to adjust */ break; } #endif - case 0x05: /* Short VAG ADPCM */ + case 0x05: /* Short PS-ADPCM */ vgmstream->coding_type = coding_PSX_cfg; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x4; @@ -170,13 +162,23 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) { break; #ifdef VGM_USE_FFMPEG - case 0x06: { /* AC3 */ - ffmpeg_codec_data *ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset, data_size); + case 0x06: { /* AC3 */ + ffmpeg_codec_data *ffmpeg_data; + + ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset, data_size); if ( !ffmpeg_data ) goto fail; vgmstream->codec_data = ffmpeg_data; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; + /* manually set skip_samples if FFmpeg didn't do it */ + if (ffmpeg_data->skipSamples <= 0) { + /* PS3 AC3 consistently has 256 encoder delay samples, and there are ~1000-2000 samples after num_samples. + * Skipping them marginally improves full loops in some Tokyo Jungle tracks (ex. a_1.sgd). */ + ffmpeg_set_skip_samples(ffmpeg_data, 256); + } + /* SGXD loop/sample values are relative (without skip samples), no need to adjust */ + break; } #endif @@ -197,50 +199,3 @@ fail: close_vgmstream(vgmstream); return NULL; } - - - -/** - * AT3 RIFF headers have a "skip samples at the beginning" value that the decoder should use, - * and absolute loop values. However the SGXD header loop values assume those samples are skipped. - * - * FFmpeg doesn't support/export this, so we have to manually get the absolute values to fix looping. - */ -static int get_at3_riff_info(at3_riff_info* info, STREAMFILE *streamFile, int32_t offset) { - off_t chunk_offset; - size_t chunk_size; - - memset(info, 0, sizeof(at3_riff_info)); - - if (read_32bitBE(offset+0x0,streamFile)!=0x52494646 /* "RIFF" */ - && read_32bitBE(offset+0x8,streamFile)!=0x57415645 ) /* "WAVE" */ - goto fail; - - /*"smpl"*/ - if (!find_chunk_le(streamFile, 0x736D706C,offset+0xc,0, &chunk_offset,&chunk_size)) goto fail; - if (read_32bitLE(chunk_offset+0x1C, streamFile)==0 - || read_32bitLE(chunk_offset+0x24+0x4, streamFile)!=0 ) - goto fail; - info->loop_start_sample = read_32bitLE(chunk_offset+0x1C+0x8+0x8, streamFile); - info->loop_end_sample = read_32bitLE(chunk_offset+0x1C+0x8+0xc,streamFile); - - /*"fact"*/ - if (!find_chunk_le(streamFile, 0x66616374,offset+0xc,0, &chunk_offset,&chunk_size)) goto fail; - if (chunk_size == 0x8) { - info->fact_samples = read_32bitLE(chunk_offset+0x0, streamFile); - info->skip_samples = read_32bitLE(chunk_offset+0x4, streamFile); - } else if (chunk_size == 0xc) { - info->fact_samples = read_32bitLE(chunk_offset+0x0, streamFile); - info->skip_samples = read_32bitLE(chunk_offset+0x8, streamFile); - } else { - goto fail; - } - - /* found */ - return 1; - -fail: - /* not found */ - return 0; - -} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sqex_scd.c b/Frameworks/vgmstream/vgmstream/src/meta/sqex_scd.c index b44ee2f50..17784a210 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sqex_scd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/sqex_scd.c @@ -1,6 +1,5 @@ #include "meta.h" #include "../coding/coding.h" -#include "../util.h" /* Square-Enix SCD (FF XIII, XIV) */ @@ -358,7 +357,6 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { int32_t bytes; /* post_meta_offset+0x00: fmt0x166 header (BE), post_meta_offset+0x34: seek table */ - bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,200, post_meta_offset,0x34, stream_size, streamFile, 1); if (bytes <= 0) goto fail; @@ -378,30 +376,33 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { /* ATRAC3plus */ /* Lord of Arcana (PSP) */ { ffmpeg_codec_data *ffmpeg_data = NULL; - off_t chunk_offset; - size_t chunk_size, fact_sample_skip = 0; - /* full riff header at start_offset/post_meta_offset (same) */ + /* full RIFF header at start_offset/post_meta_offset (same) */ ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset,stream_size); if (!ffmpeg_data) goto fail; vgmstream->codec_data = ffmpeg_data; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; - vgmstream->num_samples = ffmpeg_data->totalSamples; + vgmstream->num_samples = ffmpeg_data->totalSamples; /* fact samples */ vgmstream->loop_start_sample = loop_start; vgmstream->loop_end_sample = loop_end; - /* manually find encoder_delay to adjust samples since it's not properly used by FFmpeg */ - if (!find_chunk_le(streamFile, 0x66616374,start_offset+0xc,0, &chunk_offset,&chunk_size)) goto fail; /*"fact"*/ - if (chunk_size == 0x8) { - fact_sample_skip = read_32bitLE(chunk_offset+0x4, streamFile); - } else if (chunk_size == 0xc) { - fact_sample_skip = read_32bitLE(chunk_offset+0x8, streamFile); + /* manually read skip_samples if FFmpeg didn't do it */ + if (ffmpeg_data->skipSamples <= 0) { + off_t chunk_offset; + size_t chunk_size, fact_skip_samples = 0; + if (!find_chunk_le(streamFile, 0x66616374,start_offset+0xc,0, &chunk_offset,&chunk_size)) /* find "fact" */ + goto fail; + if (chunk_size == 0x8) { + fact_skip_samples = read_32bitLE(chunk_offset+0x4, streamFile); + } else if (chunk_size == 0xc) { + fact_skip_samples = read_32bitLE(chunk_offset+0x8, streamFile); + } + ffmpeg_set_skip_samples(ffmpeg_data, fact_skip_samples); } - vgmstream->num_samples += fact_sample_skip; - vgmstream->loop_start_sample += fact_sample_skip; - vgmstream->loop_end_sample += fact_sample_skip; + /* SCD loop/sample values are relative (without skip samples) vs RIFF (with skip samples), no need to adjust */ + } break; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xau.c b/Frameworks/vgmstream/vgmstream/src/meta/xau.c new file mode 100644 index 000000000..06d80de63 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/xau.c @@ -0,0 +1,80 @@ +#include "meta.h" +#include "../util.h" +#include "../coding/coding.h" + +/* XAU - XPEC Entertainment sound format (Beat Down PS2/Xbox, Spectral Force Chronicle [SLPM-65967]) */ +VGMSTREAM * init_vgmstream_xau(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + int loop_flag, channel_count, type, loop_start, loop_end; + + + /* check extension */ + if (!check_extensions(streamFile, "xau")) + goto fail; + + /* check header */ + if (read_32bitBE(0x00,streamFile) != 0x58415500) /* "XAU\0" "*/ + goto fail; + if (read_32bitLE(0x08,streamFile) != 0x40) /* header start */ + goto fail; + + /* 0x04: version? (0x100) */ + type = read_32bitBE(0x0c, streamFile); + loop_start = read_32bitLE(0x10, streamFile); + loop_end = read_32bitLE(0x14, streamFile); + loop_flag = (loop_end > 0); + + channel_count = read_8bit(0x18,streamFile); + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->channels = channel_count; + vgmstream->meta_type = meta_XAU; + + /* miniheader over a common header with some tweaks, so we'll simplify parsing */ + switch(type) { + case 0x50533200: /* "PS2\0" */ + if (read_32bitBE(0x40,streamFile) != 0x56414770) goto fail; /* "VAGp" */ + + start_offset = 0x800; + vgmstream->sample_rate = read_32bitBE(0x50, streamFile); + vgmstream->num_samples = ps_bytes_to_samples(read_32bitBE(0x4C,streamFile) * channel_count, channel_count); + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end; + + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x8000; + + break; + case 0x58420000: /* "XB\0\0" */ + if (read_32bitBE(0x40,streamFile) != 0x52494646) goto fail; /* "RIFF" */ + + start_offset = 0x70; + vgmstream->sample_rate = read_32bitLE(0x58, streamFile); + vgmstream->num_samples = ms_ima_bytes_to_samples(read_32bitLE(0x6c, streamFile), read_16bitLE(0x60, streamFile), channel_count); + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end; + /* there is also a "smpl" chunk at the end, same as loop_start/end */ + + vgmstream->coding_type = coding_XBOX; + vgmstream->layout_type = layout_none; + + break; + default: + goto fail; + } + + + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xma.c b/Frameworks/vgmstream/vgmstream/src/meta/xma.c index c54acef08..d8247f179 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/xma.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/xma.c @@ -11,8 +11,8 @@ VGMSTREAM * init_vgmstream_xma(STREAMFILE *streamFile) { /* check extension, case insensitive */ - /* .xma2: Skullgirls, .nps: Beautiful Katamari (renamed .xma) */ - if ( !check_extensions(streamFile, "xma,xma2,nps") ) + /* .xma2: Skullgirls, .nps: Beautiful Katamari (renamed .xma), .str: Sonic & Sega All Stars Racing */ + if ( !check_extensions(streamFile, "xma,xma2,nps,str") ) goto fail; { diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xwb.c b/Frameworks/vgmstream/vgmstream/src/meta/xwb.c index 5cf0c58fd..1fbe9b1d3 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/xwb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/xwb.c @@ -14,7 +14,8 @@ #define XACT2_1_MAX 38 /* Prey (v38) */ // v39 too? #define XACT2_2_MAX 41 /* Blue Dragon (v40) */ #define XACT3_0_MAX 46 /* Ninja Blade (t43 v42), Persona 4 Ultimax NESSICA (t45 v43) */ -#define XACT_TECHLAND 0x10000 /* Sniper Ghost Warrior, Nail'd (PS3/X360) */ +#define XACT_TECHLAND 0x10000 /* Sniper Ghost Warrior, Nail'd (PS3/X360), equivalent to XACT3_0 */ +#define XACT_CRACKDOWN 0x87 /* Crackdown 1, equivalent to XACT2_2 */ static const int wma_avg_bps_index[7] = { 12000, 24000, 4000, 6000, 8000, 20000, 2500 @@ -92,6 +93,10 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { /* read main header (WAVEBANKHEADER) */ xwb.version = read_32bit(0x04, streamFile); /* XACT3: 0x04=tool version, 0x08=header version */ + /* Crackdown 1 X360, essentially XACT2 but may have split header in some cases */ + if (xwb.version == XACT_CRACKDOWN) + xwb.version = XACT2_2_MAX; + /* read segment offsets (SEGIDX) */ if (xwb.version <= XACT1_0_MAX) { xwb.streams = read_32bit(0x0c, streamFile); @@ -306,7 +311,7 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { xwb.loop_end_sample = msd.loop_end_sample; // todo fix properly (XWB loop_start/end seem to count padding samples while XMA1 RIFF doesn't) - //this doesn't seem ok because can fall within 0 to 512 (ie.- first frame) + //this doesn't seem ok because can fall within 0 to 512 (ie.- first frame, 384) //if (xwb.loop_start_sample) xwb.loop_start_sample -= 512; //if (xwb.loop_end_sample) xwb.loop_end_sample -= 512; @@ -314,6 +319,11 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { // (in rare cases this causes a glitch in FFmpeg since it has a bug where it's missing some samples) xwb.num_samples += 64 + 512; } + else if ((xwb.codec == XMA1 || xwb.codec == XMA2) && xwb.loop_flag) { + /* seems to be needed by some edge cases, ex. Crackdown */ + //add padding, see above + xwb.num_samples += 64 + 512; + } /* build the VGMSTREAM */ diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.c b/Frameworks/vgmstream/vgmstream/src/vgmstream.c index e8adb5334..dfe32aa80 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.c +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.c @@ -292,7 +292,7 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = { init_vgmstream_ps2_lpcm, init_vgmstream_dsp_bdsp, init_vgmstream_ps2_vms, - init_vgmstream_ps2_xau, + init_vgmstream_xau, init_vgmstream_gh3_bar, init_vgmstream_ffw, init_vgmstream_dsp_dspw, @@ -360,6 +360,7 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = { init_vgmstream_rsd6xma, init_vgmstream_ta_aac_x360, init_vgmstream_ta_aac_ps3, + init_vgmstream_ps3_mta2, #ifdef VGM_USE_FFMPEG init_vgmstream_mp4_aac_ffmpeg, @@ -868,12 +869,31 @@ void close_vgmstream(VGMSTREAM * vgmstream) { free(vgmstream); } +/* calculate samples based on player's config */ int32_t get_vgmstream_play_samples(double looptimes, double fadeseconds, double fadedelayseconds, VGMSTREAM * vgmstream) { if (vgmstream->loop_flag) { - return vgmstream->loop_start_sample+(vgmstream->loop_end_sample-vgmstream->loop_start_sample)*looptimes+(fadedelayseconds+fadeseconds)*vgmstream->sample_rate; - } else return vgmstream->num_samples; + if (fadeseconds < 0) { /* a bit hack-y to avoid signature change */ + /* Continue playing the file normally after looping, instead of fading. + * Most files cut abruply after the loop, but some do have proper endings. + * With looptimes = 1 this option should give the same output vs loop disabled */ + int loop_count = (int)looptimes; /* no half loops allowed */ + vgmstream->loop_target = loop_count; + return vgmstream->loop_start_sample + + (vgmstream->loop_end_sample - vgmstream->loop_start_sample) * loop_count + + (vgmstream->num_samples - vgmstream->loop_end_sample); + } + else { + return vgmstream->loop_start_sample + + (vgmstream->loop_end_sample - vgmstream->loop_start_sample) * looptimes + + (fadedelayseconds + fadeseconds) * vgmstream->sample_rate; + } + } + else { + return vgmstream->num_samples; + } } +/* decode data into sample buffer */ void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream) { switch (vgmstream->layout_type) { case layout_interleave: @@ -1065,6 +1085,8 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { return 54; case coding_MTAF: return 0x80*2; + case coding_MTA2: + return 0x80*2; case coding_MC3: return 10; case coding_CRI_HCA: @@ -1191,6 +1213,8 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { case coding_MSADPCM: case coding_MTAF: return vgmstream->interleave_block_size; + case coding_MTA2: + return 0x90; case coding_MC3: return 4; default: @@ -1735,6 +1759,13 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to chan, vgmstream->channels); } break; + case coding_MTA2: + for (chan=0;chanchannels;chan++) { + decode_mta2(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, + chan); + } + break; case coding_MC3: for (chan=0;chanchannels;chan++) { decode_mc3(vgmstream, &vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, @@ -1776,13 +1807,21 @@ int vgmstream_samples_to_do(int samples_this_block, int samples_per_frame, VGMST return samples_to_do; } -/* return 1 if we just looped */ +/* loop if end sample is reached, and return 1 if we did loop */ int vgmstream_do_loop(VGMSTREAM * vgmstream) { - /*if (vgmstream->loop_flag) return 0;*/ + /*if (!vgmstream->loop_flag) return 0;*/ - /* is this the loop end? */ + /* is this the loop end? = new loop, continue from loop_start_sample */ if (vgmstream->current_sample==vgmstream->loop_end_sample) { + /* disable looping if target count reached and continue normally + * (only needed with the "play stream end after looping N times" option enabled) */ + vgmstream->loop_count++; + if (vgmstream->loop_target && vgmstream->loop_target == vgmstream->loop_count) { + vgmstream->loop_flag = 0; /* could be improved but works ok */ + return 0; + } + /* against everything I hold sacred, preserve adpcm * history through loop for certain types */ if (vgmstream->meta_type == meta_DSP_STD || diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.h b/Frameworks/vgmstream/vgmstream/src/vgmstream.h index acb0e35bd..24e6be3a6 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.h +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.h @@ -142,6 +142,7 @@ typedef enum { coding_SASSC, /* Activision EXAKT SASSC DPCM */ coding_LSF, /* lsf ADPCM (Fastlane Street Racing iPhone)*/ coding_MTAF, /* Konami MTAF ADPCM (IMA-derived) */ + coding_MTA2, /* Konami MTA2 ADPCM */ coding_MC3, /* Paradigm MC3 3-bit ADPCM */ /* others */ @@ -556,7 +557,7 @@ typedef enum { meta_PS2_LPCM, /* Ah! My Goddess */ meta_DSP_BDSP, /* Ah! My Goddess */ meta_PS2_VMS, /* Autobahn Raser - Police Madness */ - meta_PS2_XAU, /* Spectral Force Chronicle */ + meta_XAU, /* XPEC Entertainment (Beat Down (PS2 Xbox), Spectral Force Chronicle (PS2)) */ meta_GH3_BAR, /* Guitar Hero III Mobile .bar */ meta_FFW, /* Freedom Fighters [NGC] */ meta_DSP_DSPW, /* Sengoku Basara 3 [WII] */ @@ -623,6 +624,7 @@ typedef enum { meta_GTD, /* Knights Contract (X360/PS3), Valhalla Knights 3 (PSV) */ 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_PS3_MTA2, /* Metal Gear Solid 4 MTA2 */ #ifdef VGM_USE_VORBIS meta_OGG_VORBIS, /* Ogg Vorbis */ @@ -734,8 +736,6 @@ typedef struct { off_t next_block_offset; /* offset of header of the next block */ int block_count; /* count of "semi" block in total block */ - int hit_loop; /* have we seen the loop yet? */ - /* loop layout (saved values) */ int32_t loop_sample; /* saved from current_sample, should be loop_start_sample... */ int32_t loop_samples_into_block;/* saved from samples_into_block */ @@ -743,6 +743,12 @@ typedef struct { size_t loop_block_size; /* saved from current_block_size */ off_t loop_next_block_offset; /* saved from next_block_offset */ + /* loop internals */ + int hit_loop; /* have we seen the loop yet? */ + /* counters for "loop + play end of the stream instead of fading" (not used/needed otherwise) */ + int loop_count; /* number of complete loops (1=looped once) */ + int loop_target; /* max loops before continuing with the stream end */ + /* decoder specific */ int codec_endian; /* little/big endian marker; name is left vague but usually means big endian */