From 0e16a309e2726026dfa8cc8b7a3cb16b542e4abc Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 27 Oct 2018 23:10:03 +0200 Subject: [PATCH] Add 2MSF and clean WMSF [Super Robot Taisen OG: The Moon Dwellers (PS4)] --- src/coding/coding.h | 4 +++ src/coding/coding_utils.c | 28 +++++++++++++++ src/libvgmstream.vcproj | 4 +++ src/libvgmstream.vcxproj | 1 + src/libvgmstream.vcxproj.filters | 3 ++ src/meta/meta.h | 3 ++ src/meta/msf_banpresto.c | 62 ++++++++++++++++++++++++++++++++ src/meta/ps3_msf.c | 48 ++++++++++++------------- src/vgmstream.c | 2 ++ 9 files changed, 129 insertions(+), 26 deletions(-) create mode 100644 src/meta/msf_banpresto.c diff --git a/src/coding/coding.h b/src/coding/coding.h index 6dcd1784..79b3b816 100644 --- a/src/coding/coding.h +++ b/src/coding/coding.h @@ -352,4 +352,8 @@ typedef struct { int r_bits(vgm_bitstream * ib, int num_bits, uint32_t * value); int w_bits(vgm_bitstream * ob, int num_bits, uint32_t value); + +/* helper to pass a wrapped, clamped, fake extension-ed, SF to another meta */ +STREAMFILE* setup_subfile_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, char* extension); + #endif /*_CODING_H*/ diff --git a/src/coding/coding_utils.c b/src/coding/coding_utils.c index 38915e1d..b63e65d6 100644 --- a/src/coding/coding_utils.c +++ b/src/coding/coding_utils.c @@ -1080,3 +1080,31 @@ int w_bits(vgm_bitstream * ob, int num_bits, uint32_t value) { else return w_bits_msf(ob,num_bits,value); } + +/* ******************************************** */ +/* CUSTOM STREAMFILES */ +/* ******************************************** */ + +STREAMFILE* setup_subfile_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, char* extension) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + + new_streamFile = open_wrap_streamfile(streamFile); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + new_streamFile = open_clamp_streamfile(temp_streamFile, subfile_offset,subfile_size); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + if (extension) { + new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,extension); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + } + + return temp_streamFile; + +fail: + close_streamfile(temp_streamFile); + return NULL; +} diff --git a/src/libvgmstream.vcproj b/src/libvgmstream.vcproj index 660cb833..82ad20b7 100644 --- a/src/libvgmstream.vcproj +++ b/src/libvgmstream.vcproj @@ -1073,6 +1073,10 @@ + + + diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters index 74f584ab..5e67de9a 100644 --- a/src/libvgmstream.vcxproj.filters +++ b/src/libvgmstream.vcxproj.filters @@ -1390,6 +1390,9 @@ meta\Source Files + + meta\Source Files + coding\Source Files diff --git a/src/meta/meta.h b/src/meta/meta.h index 08ce7489..cde39f62 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -801,4 +801,7 @@ VGMSTREAM * init_vgmstream_xopus(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_vs_ffx(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_msf_banpresto_wmsf(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_msf_banpresto_2msf(STREAMFILE * streamFile); + #endif /*_META_H*/ diff --git a/src/meta/msf_banpresto.c b/src/meta/msf_banpresto.c new file mode 100644 index 00000000..ca63a961 --- /dev/null +++ b/src/meta/msf_banpresto.c @@ -0,0 +1,62 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* WMSF - Banpresto MSFx wrapper [Dai-2-Ji Super Robot Taisen OG: The Moon Dwellers (PS3)] */ +VGMSTREAM * init_vgmstream_msf_banpresto_wmsf(STREAMFILE *streamFile) { + VGMSTREAM *vgmstream = NULL; + STREAMFILE *temp_streamFile = NULL; + off_t subfile_offset = 0x10; + size_t subfile_size = get_streamfile_size(streamFile) - subfile_offset; + + + /* checks */ + if ( !check_extensions(streamFile,"msf")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x574D5346) /* "WMSF" */ + goto fail; + /* 0x04: size, 0x08: flags? 0x0c: null? */ + + temp_streamFile = setup_subfile_streamfile(streamFile, subfile_offset,subfile_size, NULL); + if (!temp_streamFile) goto fail; + + vgmstream = init_vgmstream_ps3_msf(temp_streamFile); + if (!vgmstream) goto fail; + + close_streamfile(temp_streamFile); + return vgmstream; + +fail: + close_streamfile(temp_streamFile); + close_vgmstream(vgmstream); + return NULL; +} + +/* 2MSF - Banpresto RIFF wrapper [Dai-2-Ji Super Robot Taisen OG: The Moon Dwellers (PS4)] */ +VGMSTREAM * init_vgmstream_msf_banpresto_2msf(STREAMFILE *streamFile) { + VGMSTREAM *vgmstream = NULL; + STREAMFILE *temp_streamFile = NULL; + off_t subfile_offset = 0x14; + size_t subfile_size = get_streamfile_size(streamFile) - subfile_offset; + + + /* checks */ + if ( !check_extensions(streamFile,"at9")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x324D5346) /* "2MSF" */ + goto fail; + /* 0x04: size, 0x08: flags? 0x0c: null?, 0x10: 0x01? (BE values even though RIFF is LE) */ + + temp_streamFile = setup_subfile_streamfile(streamFile, subfile_offset,subfile_size, NULL); + if (!temp_streamFile) goto fail; + + vgmstream = init_vgmstream_riff(temp_streamFile); + if (!vgmstream) goto fail; + + close_streamfile(temp_streamFile); + return vgmstream; + +fail: + close_streamfile(temp_streamFile); + close_vgmstream(vgmstream); + return NULL; +} diff --git a/src/meta/ps3_msf.c b/src/meta/ps3_msf.c index d76850ef..52e10b06 100644 --- a/src/meta/ps3_msf.c +++ b/src/meta/ps3_msf.c @@ -4,32 +4,30 @@ /* MSF - Sony's PS3 SDK format (MultiStream File) */ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - off_t start_offset, header_offset = 0; + off_t start_offset; uint32_t data_size, loop_start = 0, loop_end = 0; uint32_t id, codec_id, flags; int loop_flag = 0, channel_count; /* checks */ - /* .msf: standard, .at3: Silent Hill HD Collection, .mp3: Darkstalkers Resurrection */ + /* .msf: standard + * .at3: Silent Hill HD Collection (PS3) + * .mp3: Darkstalkers Resurrection (PS3) */ if (!check_extensions(streamFile,"msf,at3,mp3")) goto fail; - /* "WMSF" variation with a mini header over the MSFC header [Dai-2-Ji Super Robot Generations (PS3)] */ - if (read_32bitBE(0x00,streamFile) == 0x574D5346) { - header_offset = 0x10; - } - start_offset = header_offset+0x40; /* MSF header is always 0x40 */ + start_offset = 0x40; - /* check header "MSF" + version-char - * usually "MSF\0\1", "MSF\0\2", "MSF0"(\3\0), "MSF5"(\3\5), "MSFC"(\4\3) (last/common version) */ - id = read_32bitBE(header_offset+0x00,streamFile); + /* check header "MSF" + version-char, usually: + * 0x01, 0x02, 0x30 ("0"), 0x35 ("5"), 0x43 ("C") (last/most common version) */ + id = read_32bitBE(0x00,streamFile); if ((id & 0xffffff00) != 0x4D534600) goto fail; - codec_id = read_32bitBE(header_offset+0x04,streamFile); - channel_count = read_32bitBE(header_offset+0x08,streamFile); - data_size = read_32bitBE(header_offset+0x0C,streamFile); /* without header */ + codec_id = read_32bitBE(0x04,streamFile); + channel_count = read_32bitBE(0x08,streamFile); + data_size = read_32bitBE(0x0C,streamFile); /* without header */ if (data_size == 0xFFFFFFFF) /* unneeded? */ data_size = get_streamfile_size(streamFile) - start_offset; @@ -39,15 +37,15 @@ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) { * 0x20: VBR MP3 source (changed into simplified 0x1a1 CBR) * 0x40: joint stereo MP3 (apparently interleaved stereo for other formats) * 0x80+: (none/reserved) */ - flags = read_32bitBE(header_offset+0x14,streamFile); + flags = read_32bitBE(0x14,streamFile); /* sometimes loop_start/end is set with flag 0x10, but from tests it only loops if 0x01/02 is set * 0x10 often goes with 0x01 but not always (Castlevania HoD); Malicious PS3 uses flag 0x2 instead */ loop_flag = flags != 0xffffffff && ((flags & 0x01) || (flags & 0x02)); /* loop markers (marker N @ 0x18 + N*(4+4), but in practice only marker 0 is used) */ if (loop_flag) { - loop_start = read_32bitBE(header_offset+0x18,streamFile); - loop_end = read_32bitBE(header_offset+0x1C,streamFile); /* loop duration */ + loop_start = read_32bitBE(0x18,streamFile); + loop_end = read_32bitBE(0x1C,streamFile); /* loop duration */ loop_end = loop_start + loop_end; /* usually equals data_size but not always */ if (loop_end > data_size)/* not seen */ loop_end = data_size; @@ -58,9 +56,9 @@ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) { vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* sample rate hack for strange MSFv1 files that don't have it */ - vgmstream->sample_rate = read_32bitBE(header_offset+0x10,streamFile); - if (vgmstream->sample_rate == 0x00000000) /* PS ADPCM only? */ + vgmstream->sample_rate = read_32bitBE(0x10,streamFile); + /* sample rate hack for strange MSFv1 files (PS ADPCM only?) */ + if (vgmstream->sample_rate == 0x00000000) vgmstream->sample_rate = 48000; vgmstream->meta_type = meta_PS3_MSF; @@ -115,12 +113,10 @@ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) { * (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 + 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 */ 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; - ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,data_size); if (!ffmpeg_data) goto fail; vgmstream->codec_data = ffmpeg_data; @@ -156,7 +152,7 @@ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) { if (loop_flag) { vgmstream->loop_start_sample = (int64_t)loop_start * ffmpeg_data->sampleRate * 8 / ffmpeg_data->bitrate; vgmstream->loop_end_sample = (int64_t)loop_end * ffmpeg_data->sampleRate * 8 / ffmpeg_data->bitrate; - /* loops are always CBR frame beginnings */ + /* loops are always aligned to CBR frame beginnings */ } /* encoder delay varies between 1152 (1f), 528, 576, etc; probably not actually skipped */ @@ -164,7 +160,7 @@ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) { } #endif #ifdef VGM_USE_MPEG - case 0x07: { /* MPEG (CBR LAME MP3) []Dengeki Bunko Fighting Climax (PS3) */ + case 0x07: { /* MPEG (CBR LAME MP3) [Dengeki Bunko Fighting Climax (PS3)] */ mpeg_codec_data *mpeg_data = NULL; mpeg_data = init_mpeg(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels); @@ -176,7 +172,7 @@ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) { if (loop_flag) { vgmstream->loop_start_sample = mpeg_bytes_to_samples(loop_start, mpeg_data); vgmstream->loop_end_sample = mpeg_bytes_to_samples(loop_end, mpeg_data); - /* loops are always CBR frame beginnings */ + /* loops are always aligned to CBR frame beginnings */ } /* encoder delay varies between 1152 (1f), 528, 576, etc; probably not actually skipped */ diff --git a/src/vgmstream.c b/src/vgmstream.c index 9bda15d1..320afdb6 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -445,6 +445,8 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_xwma, init_vgmstream_xopus, init_vgmstream_vs_ffx, + init_vgmstream_msf_banpresto_wmsf, + init_vgmstream_msf_banpresto_2msf, /* lowest priority metas (should go after all metas, and TXTH should go before raw formats) */ init_vgmstream_txth, /* proper parsers should supersede TXTH, once added */