diff --git a/Frameworks/vgmstream/vgmstream.xcodeproj/project.pbxproj b/Frameworks/vgmstream/vgmstream.xcodeproj/project.pbxproj index c7bbcf537..f15bb60dc 100644 --- a/Frameworks/vgmstream/vgmstream.xcodeproj/project.pbxproj +++ b/Frameworks/vgmstream/vgmstream.xcodeproj/project.pbxproj @@ -97,6 +97,8 @@ 83345A501F8AEB2800B2EAA4 /* pc_al2.c in Sources */ = {isa = PBXBuildFile; fileRef = 83345A4C1F8AEB2700B2EAA4 /* pc_al2.c */; }; 83345A521F8AEB2800B2EAA4 /* xvag.c in Sources */ = {isa = PBXBuildFile; fileRef = 83345A4E1F8AEB2800B2EAA4 /* xvag.c */; }; 833A7A2E1ED11961003EC53E /* xau.c in Sources */ = {isa = PBXBuildFile; fileRef = 833A7A2D1ED11961003EC53E /* xau.c */; }; + 8342469420C4D23000926E48 /* h4m.c in Sources */ = {isa = PBXBuildFile; fileRef = 8342469020C4D22F00926E48 /* h4m.c */; }; + 8342469620C4D23D00926E48 /* blocked_h4m.c in Sources */ = {isa = PBXBuildFile; fileRef = 8342469520C4D23D00926E48 /* blocked_h4m.c */; }; 8349A8DF1FE6251F00E26435 /* vorbis_custom_utils_vid1.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A8DC1FE6251E00E26435 /* vorbis_custom_utils_vid1.c */; }; 8349A8E11FE6251F00E26435 /* ea_mt_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A8DE1FE6251F00E26435 /* ea_mt_decoder.c */; }; 8349A8E81FE6253900E26435 /* blocked_dec.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A8E21FE6253800E26435 /* blocked_dec.c */; }; @@ -678,6 +680,8 @@ 83345A4C1F8AEB2700B2EAA4 /* pc_al2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pc_al2.c; sourceTree = ""; }; 83345A4E1F8AEB2800B2EAA4 /* xvag.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xvag.c; sourceTree = ""; }; 833A7A2D1ED11961003EC53E /* xau.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xau.c; sourceTree = ""; }; + 8342469020C4D22F00926E48 /* h4m.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = h4m.c; sourceTree = ""; }; + 8342469520C4D23D00926E48 /* blocked_h4m.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_h4m.c; sourceTree = ""; }; 8349A8DC1FE6251E00E26435 /* vorbis_custom_utils_vid1.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vorbis_custom_utils_vid1.c; sourceTree = ""; }; 8349A8DE1FE6251F00E26435 /* ea_mt_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ea_mt_decoder.c; sourceTree = ""; }; 8349A8E21FE6253800E26435 /* blocked_dec.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_dec.c; sourceTree = ""; }; @@ -1276,6 +1280,7 @@ 8306B09A20984550000302D4 /* blocked_emff.c */, 8306B0932098454F000302D4 /* blocked_filp.c */, 8306B0A020984551000302D4 /* blocked_gsb.c */, + 8342469520C4D23D00926E48 /* blocked_h4m.c */, 8306B0962098454F000302D4 /* blocked_halpst.c */, 8306B09B20984550000302D4 /* blocked_hwas.c */, 8349A8E51FE6253800E26435 /* blocked_ivaud.c */, @@ -1379,6 +1384,7 @@ 836F6E4F18BDC2180095E648 /* genh.c */, 836F6E5118BDC2180095E648 /* gsp_gsb.c */, 83709DFF1ECBC1A4005C03D3 /* gtd.c */, + 8342469020C4D22F00926E48 /* h4m.c */, 836F6E5218BDC2180095E648 /* halpst.c */, 83AA5D211F6E2F9C0020821C /* hca_keys.h */, 8323894F1D2246C300482226 /* hca.c */, @@ -2229,6 +2235,7 @@ 8306B0B520984552000302D4 /* blocked_emff.c in Sources */, 836F6FBD18BDC2190095E648 /* nwa.c in Sources */, 83A21F8C201D8982000F04B9 /* kma9.c in Sources */, + 8342469420C4D23000926E48 /* h4m.c in Sources */, 836F6FC418BDC2190095E648 /* pc_snds.c in Sources */, 836F704E18BDC2190095E648 /* xss.c in Sources */, 836F6FD118BDC2190095E648 /* ps2_bg00.c in Sources */, @@ -2257,6 +2264,7 @@ 836F700518BDC2190095E648 /* ps2_vbk.c in Sources */, 836F6FDF18BDC2190095E648 /* ps2_int.c in Sources */, 8306B0AC20984552000302D4 /* blocked_xa.c in Sources */, + 8342469620C4D23D00926E48 /* blocked_h4m.c in Sources */, 836F6F7B18BDC2190095E648 /* dc_idvi.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/coding_utils.c b/Frameworks/vgmstream/vgmstream/src/coding/coding_utils.c index 466054b41..dcdacfd44 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/coding_utils.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/coding_utils.c @@ -190,7 +190,7 @@ int ffmpeg_make_riff_xma1(uint8_t * buf, size_t buf_size, size_t sample_count, s put_32bitLE(buf+off+0x08, 0); /* loop start */ put_32bitLE(buf+off+0x0c, 0); /* loop end */ put_8bit (buf+off+0x10, 0); /* loop subframe */ - put_8bit (buf+off+0x11, channels); + put_8bit (buf+off+0x11, stream_channels); put_16bitLE(buf+off+0x12, speakers); } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.c index ba7f77d96..55d805181 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.c @@ -146,7 +146,8 @@ mpeg_codec_data *init_mpeg_custom(STREAMFILE *streamFile, off_t start_offset, co if (channels <= 0 || channels > 16) goto fail; /* arbitrary max */ if (channels < data->channels_per_frame) goto fail; - if (data->default_buffer_size > 0x8000) goto fail; + //todo simplify/unify XVAG/P3D/SCD/LYN and just feed arbitrary chunks to the decoder + if (data->default_buffer_size > 0x10000) goto fail; /* max for some Ubi Lyn */ /* init streams */ diff --git a/Frameworks/vgmstream/vgmstream/src/formats.c b/Frameworks/vgmstream/vgmstream/src/formats.c index 8ba3bb0ff..4ce2bb39b 100644 --- a/Frameworks/vgmstream/vgmstream/src/formats.c +++ b/Frameworks/vgmstream/vgmstream/src/formats.c @@ -28,9 +28,11 @@ static const char* extension_list[] = { "afc", "agsc", "ahx", - "aifc", - "aifcl", + //"aif", //common + "aifc", //common? + "aifcl", //fake extension, for AIF??? //"aiff", //common + "aiffl", //fake extension, for AIF??? "aix", "akb", "al2", @@ -135,7 +137,9 @@ static const char* extension_list[] = { "gms", "gsb", "gtd", + "gwm", + "h4m", "hca", "hgc1", "his", @@ -258,6 +262,7 @@ static const char* extension_list[] = { "rak", "ras", "raw", + "rda", //FFmpeg/reserved [Rhythm Destruction (PC)] "rkv", "rnd", "rof", @@ -274,6 +279,7 @@ static const char* extension_list[] = { "rwsd", "rwx", "rxw", + "rxx", //txth/reserved [Full Auto (X360)] "s14", "sab", @@ -292,7 +298,7 @@ static const char* extension_list[] = { "scd", "sck", "sd9", - "sdf", + "sdf", //txth/reserved [Gummy Bears Mini Golf (3DS), Agent Hugo - Lemoon Twist (PS2)] "sdt", "seg", "sf0", @@ -618,6 +624,7 @@ static const layout_info layout_info_list[] = { {layout_blocked_ea_wve_au00, "blocked (EA WVE au00)"}, {layout_blocked_ea_wve_ad10, "blocked (EA WVE Ad10)"}, {layout_blocked_sthd, "blocked (STHD)"}, + {layout_blocked_h4m, "blocked (H4M)"}, }; static const meta_info meta_info_list[] = { @@ -684,8 +691,8 @@ static const meta_info meta_info_list[] = { {meta_SADL, "Procyon Studio SADL header"}, {meta_PS2_BMDX, "Beatmania .bmdx header"}, {meta_DSP_WSI, "Alone in the Dark .WSI header"}, - {meta_AIFC, "Audio Interchange File Format AIFF-C"}, - {meta_AIFF, "Audio Interchange File Format"}, + {meta_AIFC, "Apple AIFF-C (Audio Interchange File Format) header"}, + {meta_AIFF, "Apple AIFF (Audio Interchange File Format) header"}, {meta_STR_SNDS, ".str SNDS SHDR chunk"}, {meta_WS_AUD, "Westwood Studios .aud header"}, {meta_WS_AUD_old, "Westwood Studios .aud (old) header"}, @@ -795,7 +802,7 @@ static const meta_info meta_info_list[] = { {meta_YDSP, "Yuke's DSP (YDSP) Header"}, {meta_MSVP, "MSVP Header"}, {meta_NGC_SSM, "SSM DSP Header"}, - {meta_PS2_JOE, "Disney/Pixar JOE Header"}, + {meta_PS2_JOE, "Asobo Studio .JOE header"}, {meta_VGS, "Guitar Hero VGS Header"}, {meta_DC_DCSW_DCS, "Evil Twin DCS file with helper"}, {meta_WII_SMP, "SMP DSP Header"}, @@ -1012,6 +1019,10 @@ static const meta_info meta_info_list[] = { {meta_OPUS_PPP, "AT9 OPUS header"}, {meta_UBI_BAO, "Ubisoft BAO header"}, {meta_DSP_SWITCH_AUDIO, "UE4 Switch Audio header"}, + {meta_TA_AAC_VITA, "tri-Ace AAC (Vita) header"}, + {meta_OGG_GWM, "Ogg Vorbis (GWM header)"}, + {meta_DSP_SADF, "Procyon Studio SADF header"}, + {meta_H4M, "Hudson HVQM4 header"}, #ifdef VGM_USE_FFMPEG {meta_FFmpeg, "FFmpeg supported file format"}, diff --git a/Frameworks/vgmstream/vgmstream/src/layout/blocked.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked.c index d59417d84..240e58c16 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/blocked.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked.c @@ -202,6 +202,9 @@ static void block_update(VGMSTREAM * vgmstream) { case layout_blocked_sthd: block_update_sthd(vgmstream->next_block_offset,vgmstream); break; + case layout_blocked_h4m: + block_update_h4m(vgmstream->next_block_offset,vgmstream); + break; default: break; } diff --git a/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_swvr.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_swvr.c index 080443ad2..1fd324cbd 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_swvr.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_swvr.c @@ -82,7 +82,7 @@ void block_update_ea_swvr(off_t block_offset, VGMSTREAM * vgmstream) { break; default: /* ignore, 0 samples */ - VGM_LOG("EA SWVR: ignored 0x%08x at 0x%lx\n", block_id, block_offset); + //;VGM_LOG("EA SWVR: ignored 0x%08x at 0x%lx\n", block_id, block_offset); break; } diff --git a/Frameworks/vgmstream/vgmstream/src/layout/blocked_h4m.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_h4m.c new file mode 100644 index 000000000..14f64cc8d --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_h4m.c @@ -0,0 +1,102 @@ +#include "layout.h" +#include "../coding/coding.h" + + +/* H4M video blocks with audio frames, based on h4m_audio_decode */ +void block_update_h4m(off_t block_offset, VGMSTREAM * vgmstream) { + STREAMFILE* streamFile = vgmstream->ch[0].streamfile; + int i; + size_t block_size, block_samples; + + + /* use full_block_size as counter (a bit hacky but whatevs) */ + if (vgmstream->full_block_size <= 0) { + /* new full block */ + /* 0x00: last_full_block_size */ + uint32_t full_block_size = read_32bitBE(block_offset+0x04, streamFile); + /* 0x08: vid_frame_count */ + /* 0x0c: aud_frame_count */ + /* 0x10: block_header_unk (0x01000000, except 0 in a couple of Bomberman Jetters files) */ + + vgmstream->full_block_size = full_block_size; /* not including 0x14 block header */ + block_size = 0x14; /* skip header and point to first frame in full block */ + block_samples = 0; /* signal new block_update_h4m */ + } + else { + /* new audio or video frames in the current full block */ + uint16_t frame_type = read_16bitBE(block_offset+0x00, streamFile); + uint16_t frame_format = read_16bitBE(block_offset+0x02, streamFile); + uint32_t frame_size = read_32bitBE(block_offset+0x04, streamFile); /* not including 0x08 frame header */ + + + if (frame_type == 0x00) { + /* HVQM4_AUDIO (there are more checks with frame_format but not too relevant for vgmstream) */ + uint32_t frame_samples = read_32bitBE(block_offset+0x08, streamFile); + size_t block_skip; + + if (vgmstream->codec_version & 0x80) { + frame_samples /= 2; /* ??? */ + } + + block_skip = 0x08 + 0x04; + block_size = 0x08 + frame_size; + block_samples = frame_samples; + + + /* skip data from other audio tracks */ + if (vgmstream->num_streams) { + uint32_t audio_bytes = frame_size - 0x04; + block_skip += (audio_bytes / vgmstream->num_streams) * vgmstream->stream_index; + } + + //VGM_ASSERT(frame_format < 1 && frame_format > 3, "H4M: unknown frame_format %x at %lx\n", frame_format, block_offset); + VGM_ASSERT(frame_format == 1, "H4M: unknown frame_format %x at %lx\n", frame_format, block_offset); + + //todo handle in the decoder? + //todo right channel first? + /* get ADPCM hist (usually every new block) */ + for (i = 0; i < vgmstream->channels; i++) { + if (frame_format == 1) { /* combined hist+index */ + vgmstream->ch[i].adpcm_history1_32 = read_16bitBE(block_offset + block_skip + 0x02*i + 0x00,streamFile) & 0xFFFFFF80; + vgmstream->ch[i].adpcm_step_index = read_8bit(block_offset + block_skip + 0x02*i + 0x01,streamFile) & 0x7f; + vgmstream->ch[i].offset = block_offset + block_skip + 0x02*vgmstream->channels; + } + else if (frame_format == 3) { /* separate hist+index */ + vgmstream->ch[i].adpcm_history1_32 = read_16bitBE(block_offset + block_skip + 0x03*i + 0x00,streamFile); + vgmstream->ch[i].adpcm_step_index = read_8bit(block_offset + block_skip + 0x03*i + 0x02,streamFile); + vgmstream->ch[i].offset = block_offset + block_skip + 0x03*vgmstream->channels; + } + else if (frame_format == 2) { /* no hist/index */ + vgmstream->ch[i].offset = block_offset + block_skip; + } + } + + //todo temp hack, at it must write header sample and ignore the last nibble to get fully correct output + if (frame_format == 1 || frame_format == 3) { + block_samples--; + } + } + else { + block_size = 0x08 + frame_size; + block_samples = 0; /* signal new block_update_h4m */ + } + + vgmstream->full_block_size -= block_size; + } + + /* EOF check, there is some footer/garbage at the end */ + if (block_offset == get_streamfile_size(streamFile) + || block_offset + block_size > get_streamfile_size(streamFile)) { + //block_samples = -1; /* signal end block */ + vgmstream->full_block_size = 0; + vgmstream->current_block_samples = 0; + vgmstream->current_block_offset = get_streamfile_size(streamFile); + vgmstream->next_block_offset = get_streamfile_size(streamFile); + return; + } + + vgmstream->current_block_samples = block_samples; + vgmstream->current_block_offset = block_offset; + vgmstream->next_block_offset = block_offset + block_size; +} + diff --git a/Frameworks/vgmstream/vgmstream/src/layout/layout.h b/Frameworks/vgmstream/vgmstream/src/layout/layout.h index a8fc78343..980904766 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/layout.h +++ b/Frameworks/vgmstream/vgmstream/src/layout/layout.h @@ -43,6 +43,7 @@ void block_update_xvag_subsong(off_t block_offset, VGMSTREAM * vgmstream); void block_update_ea_wve_au00(off_t block_offset, VGMSTREAM * vgmstream); void block_update_ea_wve_ad10(off_t block_offset, VGMSTREAM * vgmstream); void block_update_sthd(off_t block_offset, VGMSTREAM * vgmstream); +void block_update_h4m(off_t block_offset, VGMSTREAM * vgmstream); /* other layouts */ void render_vgmstream_interleave(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/aifc.c b/Frameworks/vgmstream/vgmstream/src/meta/aifc.c index 772dd1813..5ae7d61ce 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/aifc.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/aifc.c @@ -1,11 +1,6 @@ #include "meta.h" #include "../layout/layout.h" -#include "../util.h" -/* Audio Interchange File Format AIFF-C */ -/* also plain AIFF, for good measure */ - -/* Included primarily for 3DO */ /* for reading integers inexplicably packed into 80 bit floats */ static uint32_t read80bitSANE(off_t offset, STREAMFILE *streamFile) { @@ -53,9 +48,10 @@ static uint32_t find_marker(STREAMFILE *streamFile, off_t MarkerChunkOffset, return -1; } + +/* Audio Interchange File Format AIFF/AIFF-C - from Mac/3DO games */ VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; off_t file_size = -1; int channel_count = 0; @@ -82,22 +78,23 @@ VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) { int InstrumentChunkFound =0; off_t InstrumentChunkOffset = -1; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (!strcasecmp("aifc",filename_extension(filename)) || - !strcasecmp("afc",filename_extension(filename)) || - !strcasecmp("aifcl",filename_extension(filename)) || - !strcasecmp("cbd2",filename_extension(filename))) - { + + /* checks */ + /* .aif: common (AIFF or AIFC), .aiff: common AIFF, .aifc: common AIFC + * .cbd2: M2 games, .bgm: Super Street Fighter II Turbo (3DO), aifcl/aiffl: for plugins? */ + if (check_extensions(streamFile, "aif")) { AIFCext = 1; - } - else if (!strcasecmp("aiff",filename_extension(filename)) || - !strcasecmp("aif",filename_extension(filename)) || - !strcasecmp("aiffl",filename_extension(filename))) - { AIFFext = 1; } - else goto fail; + else if (check_extensions(streamFile, "aifc,aifcl,afc,cbd2,bgm")) { + AIFCext = 1; + } + else if (check_extensions(streamFile, "aiff,aiffl")) { + AIFFext = 1; + } + else { + goto fail; + } /* check header */ if ((uint32_t)read_32bitBE(0,streamFile)==0x464F524D && /* "FORM" */ @@ -115,13 +112,16 @@ VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) { AIFF = 1; } else goto fail; - } else goto fail; - + } + else { + goto fail; + } + file_size = get_streamfile_size(streamFile); /* read through chunks to verify format and find metadata */ { - off_t current_chunk = 0xc; /* start with first chunk within FORM */ + off_t current_chunk = 0x0c; /* start with first chunk within FORM */ while (current_chunk < file_size) { uint32_t chunk_type = read_32bitBE(current_chunk,streamFile); @@ -134,54 +134,55 @@ VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) { if (current_chunk+8+chunk_size > file_size) goto fail; switch(chunk_type) { - case 0x46564552: /* FVER */ - /* only one per file */ + case 0x46564552: /* "FVER" (version info) */ if (FormatVersionChunkFound) goto fail; - /* plain AIFF shouldn't have */ - if (AIFF) goto fail; + if (AIFF) goto fail; /* plain AIFF shouldn't have */ FormatVersionChunkFound = 1; /* specific size */ if (chunk_size != 4) goto fail; /* Version 1 of AIFF-C spec timestamp */ - if ((uint32_t)read_32bitBE(current_chunk+8,streamFile) != - 0xA2805140) goto fail; + if ((uint32_t)read_32bitBE(current_chunk+8,streamFile) != 0xA2805140) goto fail; break; - case 0x434F4D4D: /* COMM */ - /* only one per file */ + + case 0x434F4D4D: /* "COMM" (main header) */ if (CommonChunkFound) goto fail; CommonChunkFound = 1; channel_count = read_16bitBE(current_chunk+8,streamFile); if (channel_count <= 0) goto fail; - sample_count = (uint32_t)read_32bitBE(current_chunk+0xa,streamFile); - - sample_size = read_16bitBE(current_chunk+0xe,streamFile); - + sample_count = (uint32_t)read_32bitBE(current_chunk+0x0a,streamFile); /* number of blocks, actually */ + sample_size = read_16bitBE(current_chunk+0x0e,streamFile); sample_rate = read80bitSANE(current_chunk+0x10,streamFile); if (AIFC) { switch (read_32bitBE(current_chunk+0x1a,streamFile)) { - case 0x53445832: /* SDX2 */ + case 0x53445832: /* "SDX2" [3DO games: Super Street Fighter II Turbo (3DO), etc] */ coding_type = coding_SDX2; - interleave = 1; + interleave = 0x01; break; - case 0x43424432: /* CBD2 */ + case 0x43424432: /* "CBD2" [M2 (arcade 3DO) games: IMSA Racing (M2), etc] */ coding_type = coding_CBD2; - interleave = 1; + interleave = 0x01; break; - case 0x41445034: /* ADP4 */ + case 0x41445034: /* "ADP4" */ coding_type = coding_DVI_IMA_int; - /* don't know how stereo DVI is laid out */ - if (channel_count != 1) break; + if (channel_count != 1) break; /* don't know how stereo DVI is laid out */ + break; + case 0x696D6134: /* "ima4" [Alida (PC) Lunar SSS (iOS)] */ + coding_type = coding_APPLE_IMA4; + interleave = 0x22; + sample_count = sample_count * ((interleave-0x2)*2); break; default: - /* we should probably support uncompressed here */ + VGM_LOG("AIFC: unknown codec\n"); goto fail; } - } else if (AIFF) { + /* string size and human-readable AIFF-C codec follows */ + } + else if (AIFF) { switch (sample_size) { case 8: coding_type = coding_PCM8; @@ -191,39 +192,40 @@ VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) { coding_type = coding_PCM16BE; interleave = 2; break; - /* 32 is a possibility, but we don't see it and I - * don't have a reader for it yet */ default: + VGM_LOG("AIFF: unknown codec\n"); goto fail; } } - - /* we don't check the human-readable portion of AIFF-C*/ - break; - case 0x53534E44: /* SSND */ - /* at most one per file */ + + case 0x53534E44: /* "SSND" (main data) */ if (SoundDataChunkFound) goto fail; SoundDataChunkFound = 1; start_offset = current_chunk + 16 + read_32bitBE(current_chunk+8,streamFile); break; - case 0x4D41524B: /* MARK */ + + case 0x4D41524B: /* "MARK" (loops) */ if (MarkerChunkFound) goto fail; MarkerChunkFound = 1; + MarkerChunkOffset = current_chunk; break; - case 0x494E5354: /* INST */ + + case 0x494E5354: /* "INST" (loops) */ if (InstrumentChunkFound) goto fail; InstrumentChunkFound = 1; + InstrumentChunkOffset = current_chunk; break; + default: /* spec says we can skip unrecognized chunks */ break; } - current_chunk += 8+chunk_size; + current_chunk += 0x08+chunk_size; } } @@ -235,6 +237,7 @@ VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) { goto fail; } + /* read loop points */ if (InstrumentChunkFound && MarkerChunkFound) { int start_marker; @@ -262,49 +265,31 @@ VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) { } } - /* build the VGMSTREAM */ + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ - vgmstream->num_samples = sample_count; vgmstream->sample_rate = sample_rate; - - vgmstream->coding_type = coding_type; - if (channel_count > 1) - vgmstream->layout_type = layout_interleave; - else - vgmstream->layout_type = layout_none; - vgmstream->interleave_block_size = interleave; + vgmstream->num_samples = sample_count; vgmstream->loop_start_sample = loop_start; vgmstream->loop_end_sample = loop_end; + vgmstream->coding_type = coding_type; + vgmstream->layout_type = (channel_count > 1) ? layout_interleave : layout_none; + vgmstream->interleave_block_size = interleave; + if (AIFC) vgmstream->meta_type = meta_AIFC; else if (AIFF) vgmstream->meta_type = meta_AIFF; - /* open the file, set up each channel */ - { - int i; - - vgmstream->ch[0].streamfile = streamFile->open(streamFile,filename, - STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!vgmstream->ch[0].streamfile) goto fail; - - for (i=0;ich[i].streamfile = vgmstream->ch[0].streamfile; - vgmstream->ch[i].offset = vgmstream->ch[i].channel_start_offset = - start_offset+i*interleave; - } - } - + 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/ea_swvr.c b/Frameworks/vgmstream/vgmstream/src/meta/ea_swvr.c index fcdd7aa7a..7321d2538 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ea_swvr.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_swvr.c @@ -55,6 +55,9 @@ VGMSTREAM * init_vgmstream_ea_swvr(STREAMFILE *streamFile) { else if (read_32bit(start_offset+0x10, streamFile) == 0x53484452) /* "SHDR" (Future Cop PC) */ start_offset += read_32bit(start_offset+0x04, streamFile); + if (read_32bit(start_offset+0x00, streamFile) == 0x46494C4C) /* "FILL" (Freekstyle) */ + start_offset += read_32bit(start_offset+0x04, streamFile); + total_subsongs = 1; block_id = read_32bit(start_offset, streamFile); @@ -65,7 +68,7 @@ VGMSTREAM * init_vgmstream_ea_swvr(STREAMFILE *streamFile) { coding = coding_PSX; if (read_16bit(start_offset+0x1a, streamFile) == 0x0024) { total_subsongs = read_32bit(start_offset+0x0c, streamFile)+1; - sample_rate = 24000; + sample_rate = 22050; } else { sample_rate = 14008; @@ -75,7 +78,7 @@ VGMSTREAM * init_vgmstream_ea_swvr(STREAMFILE *streamFile) { case 0x56414742: /* "VAGB" */ coding = coding_PSX; if (read_16bit(start_offset+0x1a, streamFile) == 0x6400) { - sample_rate = 24000; + sample_rate = 22050; } else { sample_rate = 14008; @@ -85,13 +88,13 @@ VGMSTREAM * init_vgmstream_ea_swvr(STREAMFILE *streamFile) { case 0x4453504D: /* "DSPM" */ coding = coding_NGC_DSP; total_subsongs = read_32bit(start_offset+0x0c, streamFile)+1; - sample_rate = 24000; + sample_rate = 22050; channel_count = 2; break; case 0x44535042: /* "DSPB" */ coding = coding_NGC_DSP; channel_count = 1; - sample_rate = 24000; + sample_rate = 22050; break; case 0x4D534943: /* "MSIC" */ coding = coding_PCM8_U_int; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/h4m.c b/Frameworks/vgmstream/vgmstream/src/meta/h4m.c new file mode 100644 index 000000000..47ec0ecec --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/h4m.c @@ -0,0 +1,105 @@ +#include "meta.h" +#include "../layout/layout.h" +#include "../coding/coding.h" + +/* H4M - from Hudson HVQM4 videos [Resident Evil 0 (GC), Tales of Symphonia (GC)] + * (info from hcs/Nisto's h4m_audio_decode) */ +VGMSTREAM * init_vgmstream_h4m(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + int loop_flag, channel_count; + int format, extra_tracks, sample_rate; + int total_subsongs, target_subsong = streamFile->stream_index; + + + /* checks */ + if (!check_extensions(streamFile, "h4m")) + goto fail; + + if (read_32bitBE(0x00,streamFile) != 0x4856514D && /* "HVQM" */ + read_32bitBE(0x04,streamFile) != 0x3420312E) /* "4 1." */ + goto fail; + if (read_32bitBE(0x08,streamFile) != 0x33000000 && /* "3\0\0\0" */ + read_32bitBE(0x08,streamFile) != 0x35000000) /* "5\0\0\0" */ + goto fail; + + /* header */ + start_offset = read_32bitBE(0x10, streamFile); /* header_size */ + if (start_offset != 0x44) /* known size */ + goto fail; + if (read_32bitBE(0x14, streamFile) != get_streamfile_size(streamFile) - start_offset) /* body_size */ + goto fail; + if (read_32bitBE(0x18, streamFile) == 0) /* blocks */ + goto fail; + /* 0x1c: video_frames */ + if (read_32bitBE(0x20, streamFile) == 0) /* audio_frames */ + goto fail; + /* 0x24: frame interval */ + /* 0x28: max_video_frame_size */ + /* 0x2c: unk2C (0) */ + if (read_32bitBE(0x30, streamFile) == 0) /* max_audio_frame_size */ + goto fail; + /* 0x34: hres */ + /* 0x36: vres */ + /* 0x38: h_srate */ + /* 0x39: v_srate */ + /* 0x3a: unk3A (0 or 0x12) */ + /* 0x3b: unk3B (0) */ + channel_count = read_8bit(0x3c,streamFile); + if (read_8bit(0x3d,streamFile) != 16) /* bitdepth */ //todo Pikmin not working + goto fail; + format = read_8bit(0x3e,streamFile); /* flags? */ + extra_tracks = read_8bit(0x3f,streamFile); + sample_rate = read_32bitBE(0x40,streamFile); + + loop_flag = 0; + + total_subsongs = extra_tracks + 1; /* tracks for languages [Pokemon Channel], or sometimes used to fake multichannel [Tales of Symphonia] */ + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = sample_rate; + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = get_streamfile_size(streamFile) / total_subsongs; /* approx... */ + vgmstream->codec_version = format; /* for blocks */ + vgmstream->meta_type = meta_H4M; + vgmstream->layout_type = layout_blocked_h4m; + + switch(format & 0x7F) { + case 0x00: + vgmstream->coding_type = coding_DVI_IMA; //todo H4M_IMA + break; + /* no games known to use this, h4m_audio_decode may decode them */ + case 0x01: /* Uncompressed PCM */ + case 0x04: /* 8-bit (A)DPCM */ + default: + VGM_LOG("H4M: unknown codec %x\n", format); + goto fail; + } + + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + + /* calc num_samples manually */ + { + vgmstream->next_block_offset = start_offset; + do { + block_update_h4m(vgmstream->next_block_offset,vgmstream); + vgmstream->num_samples += vgmstream->current_block_samples; + } + while (vgmstream->next_block_offset < get_streamfile_size(streamFile)); + } + + block_update_h4m(start_offset, vgmstream); + + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h b/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h index fce656081..67cf66cbd 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h @@ -231,6 +231,12 @@ static const hcakey_info hcakey_list[] = { // Oira (Cygames) [iOS/Android] {46460622}, // 0000000002C4EECE + // Dragon Ball Legends (Bandai Namco) [iOS/Android] + {7335633962698440504}, // 65CD683924EE7F38 + + // Princess Connect Re:Dive (iOS/Android/PC) + {3201512}, // 000000000030D9E8 + }; #endif/*_HCA_KEYS_H_*/ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ktss.c b/Frameworks/vgmstream/vgmstream/src/meta/ktss.c index 463ac152c..2f2bccc6f 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ktss.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ktss.c @@ -9,6 +9,7 @@ VGMSTREAM * init_vgmstream_ktss(STREAMFILE *streamFile) { int8_t version; int32_t loop_length, coef_start_offset, coef_spacing; off_t start_offset; + int8_t channelMultiplier; if (!check_extensions(streamFile, "kns,ktss")) goto fail; @@ -31,7 +32,15 @@ VGMSTREAM * init_vgmstream_ktss(STREAMFILE *streamFile) { loop_length = read_32bitLE(0x38, streamFile); loop_flag = loop_length > 0; - channel_count = read_8bit(0x29, streamFile); + + // For unknown reasons, a channel multiplier is necessary in Hyrule Warriors (Switch) + // It seems to be present in other Koei Tecmo KNS but the channel count was always + // explicitly defined in the 0x29 byte. Here, 10 channel files have '2' in 0x29* + // and '5' in 0x28 whereas previous titles usually contained '1' + // This is super meh on KT's part but whatever + channelMultiplier = read_8bit(0x28, streamFile); + + channel_count = read_8bit(0x29, streamFile) * channelMultiplier; /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count, loop_flag); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/meta.h b/Frameworks/vgmstream/vgmstream/src/meta/meta.h index c32d47152..00bb84a22 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/meta.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/meta.h @@ -647,6 +647,7 @@ VGMSTREAM * init_vgmstream_ta_aac_x360(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ta_aac_ps3(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ta_aac_mobile(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ta_aac_mobile_vorbis(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_ta_aac_vita(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ps3_mta2(STREAMFILE *streamFile); @@ -673,6 +674,7 @@ VGMSTREAM * init_vgmstream_opus_std(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_opus_n1(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_opus_capcom(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_opus_nop(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_opus_shinen(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_pc_al2(STREAMFILE * streamFile); @@ -750,4 +752,9 @@ VGMSTREAM * init_vgmstream_opus_ppp(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ubi_bao_pk(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_dsp_switch_audio(STREAMFILE *streamFile); + +VGMSTREAM * init_vgmstream_dsp_sadf(STREAMFILE *streamFile); + +VGMSTREAM * init_vgmstream_h4m(STREAMFILE *streamFile); + #endif /*_META_H*/ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c b/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c index 990a1c210..840812a7e 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c @@ -629,19 +629,26 @@ VGMSTREAM * init_vgmstream_3ds_idsp(STREAMFILE *streamFile) { /* try NUS3BANK container */ if (read_32bitBE(0x00,streamFile) == 0x4E555333) { /* "NUS3" */ - offset = 0x14 + read_32bitLE(0x10, streamFile); /* header size */ - offset += read_32bitLE(0x1C, streamFile) + 0x08; - offset += read_32bitLE(0x24, streamFile) + 0x08; - offset += read_32bitLE(0x2C, streamFile) + 0x08; - offset += read_32bitLE(0x34, streamFile) + 0x08; - offset += read_32bitLE(0x3C, streamFile) + 0x08; - offset += read_32bitLE(0x44, streamFile) + 0x08; - offset += 0x08; + int i, chunk_count; + + offset = 0x14 + read_32bitLE(0x10, streamFile); /* TOC size */ + chunk_count = read_32bitLE(0x14, streamFile); /* rarely not 7 (ex. SMB U's snd_bgm_CRS12_Simple_Result_Final) */ + + for (i = 0; i < chunk_count; i++) { + if (read_32bitBE(0x18 + i*0x08 + 0x00, streamFile) == 0x5041434B) { /* "PACK" */ + offset += 0x08; + break; /* contains "IDSP", should appear last anyway */ + } + else { + offset += 0x08 + read_32bitLE(0x18 + i*0x08 + 0x04, streamFile); + } + } } else { offset = 0x00; } + if (read_32bitBE(offset,streamFile) != 0x49445350) /* "IDSP" */ goto fail; /* 0x0c: sample rate, 0x10: num_samples, 0x14: loop_start_sample, 0x18: loop_start_sample */ @@ -688,6 +695,49 @@ fail: return NULL; } +/* sadf - Procyon Studio Header Variant [Xenoblade Chronicles 2 (Switch)] (sfx) */ +VGMSTREAM * init_vgmstream_dsp_sadf(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + int channel_count, loop_flag; + off_t start_offset; + + /* checks */ + if (!check_extensions(streamFile, "sad")) + goto fail; + if (read_32bitBE(0x00, streamFile) != 0x73616466) /* "sadf" */ + goto fail; + + channel_count = read_8bit(0x18, streamFile); + loop_flag = read_8bit(0x19, streamFile); + start_offset = read_32bitLE(0x1C, streamFile); + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->num_samples = read_32bitLE(0x28, streamFile); + vgmstream->sample_rate = read_32bitLE(0x24, streamFile); + if (loop_flag) { + vgmstream->loop_start_sample = read_32bitLE(0x2c, streamFile); + vgmstream->loop_end_sample = read_32bitLE(0x30, streamFile); + } + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = channel_count == 1 ? 0x8 : + read_32bitLE(0x20, streamFile) / channel_count; + vgmstream->meta_type = meta_DSP_SADF; + + dsp_read_coefs_le(vgmstream, streamFile, 0x80, 0x80); + + if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} + #define WSI_MAX_CHANNELS 2 /* .wsi - blocked dsp [Alone in the Dark (Wii)] */ VGMSTREAM * init_vgmstream_wsi(STREAMFILE *streamFile) { diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis.c b/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis.c index 67d21149a..93b3b955b 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis.c @@ -215,6 +215,17 @@ static void ys8_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, vo } } +static void gwm_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource) { + size_t bytes_read = size*nmemb; + ogg_vorbis_streamfile * const ov_streamfile = datasource; + int i; + + /* bytes are xor'd with key */ + for (i = 0; i < bytes_read; i++) { + ((uint8_t*)ptr)[i] ^= (uint8_t)ov_streamfile->xor_value; + } +} + /* Ogg Vorbis, by way of libvorbisfile; may contain loop comments */ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { ogg_vorbis_meta_info_t ovmi = {0}; @@ -223,17 +234,15 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { int is_ogg = 0; int is_um3 = 0; int is_kovs = 0; - int is_psychic = 0; int is_sngw = 0; int is_isd = 0; - int is_l2sd = 0; int is_rpgmvo = 0; int is_eno = 0; - int is_ys8 = 0; + int is_gwm = 0; /* check extension */ - /* .ogg: standard/psychic, .logg: renamed for plugins, + /* .ogg: standard/various, .logg: renamed for plugins, * .adx: KID [Remember11 (PC)], * .rof: The Rhythm of Fighters (Mobile) * .acm: Planescape Torment Enhanced Edition (PC) */ @@ -251,26 +260,32 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { is_rpgmvo = 1; } else if (check_extensions(streamFile,"eno")) { /* .eno: Metronomicon (PC) */ is_eno = 1; + } else if (check_extensions(streamFile,"gwm")) { /* .gwm: Adagio: Cloudburst (PC) */ + is_gwm = 1; } else { goto fail; } - /* check standard Ogg Vorbis */ + /* check standard Ogg Vorbis and variations */ if (is_ogg) { - if (read_32bitBE(0x00,streamFile) == 0x2c444430) { /* Psychic Software obfuscation [Darkwind: War on Wheels (PC)] */ - is_psychic = 1; + if (read_32bitBE(0x00,streamFile) == 0x2c444430) { /* Psychic Software [Darkwind: War on Wheels (PC)] */ ovmi.decryption_callback = psychic_ogg_decryption_callback; + ovmi.meta_type = meta_OGG_PSYCHIC; } else if (read_32bitBE(0x00,streamFile) == 0x4C325344) { /* "L2SD" [Lineage II Chronicle 4 (PC)] */ - is_l2sd = 1; ovmi.decryption_callback = l2sd_ogg_decryption_callback; + ovmi.meta_type = meta_OGG_L2SD; } else if (read_32bitBE(0x00,streamFile) == 0x048686C5) { /* XOR'ed + bitswapped "OggS" [Ys VIII (PC)] */ - is_ys8 = 1; + ovmi.xor_value = 0xF0; ovmi.decryption_callback = ys8_ogg_decryption_callback; + ovmi.meta_type = meta_OGG_YS8; } - else if (read_32bitBE(0x00,streamFile) != 0x4f676753) { /* "OggS" */ - goto fail; /* unknown/not ogg (ex. Wwise) */ + else if (read_32bitBE(0x00,streamFile) == 0x4f676753) { /* "OggS" */ + ovmi.meta_type = meta_OGG_VORBIS; + } + else { + goto fail; /* unknown/not Ogg Vorbis (ex. Wwise) */ } } @@ -279,9 +294,10 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { if (read_32bitBE(0x00,streamFile) != 0x4f676753) { /* "OggS" */ ovmi.decryption_callback = um3_ogg_decryption_callback; } + ovmi.meta_type = meta_OGG_UM3; } - /* check KOVS (Koei Tecmo games), encrypted and has an actual header */ + /* check KOVS (Koei Tecmo games), header + encrypted */ if (is_kovs) { if (read_32bitBE(0x00,streamFile) != 0x4b4f5653) { /* "KOVS" */ goto fail; @@ -289,6 +305,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { ovmi.loop_start = read_32bitLE(0x08,streamFile); ovmi.loop_flag = (ovmi.loop_start != 0); ovmi.decryption_callback = kovs_ogg_decryption_callback; + ovmi.meta_type = meta_OGG_KOVS; start_offset = 0x20; } @@ -299,11 +316,13 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { ovmi.xor_value = read_32bitBE(0x00,streamFile); ovmi.decryption_callback = sngw_ogg_decryption_callback; } + ovmi.meta_type = meta_OGG_SNGW; } - /* check ISD (Gunvolt PC) */ + /* check ISD [Gunvolt (PC)], encrypted */ if (is_isd) { ovmi.decryption_callback = isd_ogg_decryption_callback; + ovmi.meta_type = meta_OGG_ISD; //todo looping unknown, not in Ogg comments // game has sound/GV_steam.* files with info about sound/stream/*.isd @@ -314,52 +333,36 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { // 0x0c(2): PCM block size, 0x0e(2): PCM bps, 0x10: null, 0x18: samples (in PCM bytes) } - /* check RPGMKVO (RPG Maker MV), header + minor encryption */ + /* check RPGMKVO [RPG Maker MV (PC)], header + partially encrypted */ if (is_rpgmvo) { if (read_32bitBE(0x00,streamFile) != 0x5250474D && /* "RPGM" */ read_32bitBE(0x00,streamFile) != 0x56000000) { /* "V\0\0\0" */ goto fail; } ovmi.decryption_callback = rpgmvo_ogg_decryption_callback; + ovmi.meta_type = meta_OGG_RPGMV; start_offset = 0x10; } - /* check ENO [Metronomicon (PC)] */ + /* check ENO [Metronomicon (PC)], key + encrypted */ if (is_eno) { /* first byte probably derives into xor key, but this works too */ ovmi.xor_value = read_8bit(0x05,streamFile); /* always zero = easy key */ ovmi.decryption_callback = eno_ogg_decryption_callback; + ovmi.meta_type = meta_OGG_ENO; + start_offset = 0x01; } - /* check Ys VIII (PC) */ - if (is_ys8) { - ovmi.xor_value = 0xF0; - ovmi.decryption_callback = ys8_ogg_decryption_callback; + + /* check GWM [Adagio: Cloudburst (PC)], encrypted */ + if (is_gwm) { + ovmi.xor_value = 0x5D; + ovmi.decryption_callback = gwm_ogg_decryption_callback; + ovmi.meta_type = meta_OGG_GWM; } - if (is_um3) { - ovmi.meta_type = meta_OGG_UM3; - } else if (is_kovs) { - ovmi.meta_type = meta_OGG_KOVS; - } else if (is_psychic) { - ovmi.meta_type = meta_OGG_PSYCHIC; - } else if (is_sngw) { - ovmi.meta_type = meta_OGG_SNGW; - } else if (is_isd) { - ovmi.meta_type = meta_OGG_ISD; - } else if (is_l2sd) { - ovmi.meta_type = meta_OGG_L2SD; - } else if (is_rpgmvo) { - ovmi.meta_type = meta_OGG_RPGMV; - } else if (is_eno) { - ovmi.meta_type = meta_OGG_ENO; - } else if (is_ys8) { - ovmi.meta_type = meta_OGG_YS8; - } else { - ovmi.meta_type = meta_OGG_VORBIS; - } return init_vgmstream_ogg_vorbis_callbacks(streamFile, NULL, start_offset, &ovmi); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/opus.c b/Frameworks/vgmstream/vgmstream/src/meta/opus.c index 2a4f152b0..60aef3572 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/opus.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/opus.c @@ -88,6 +88,7 @@ fail: /* standard Switch Opus, Nintendo header + raw data (generated by opus_test.c?) [Lego City Undercover (Switch)] */ VGMSTREAM * init_vgmstream_opus_std(STREAMFILE *streamFile) { + STREAMFILE * PSIFile = NULL; off_t offset = 0; int num_samples = 0, loop_start = 0, loop_end = 0; @@ -95,10 +96,23 @@ VGMSTREAM * init_vgmstream_opus_std(STREAMFILE *streamFile) { if (!check_extensions(streamFile,"opus,lopus")) goto fail; - offset = 0x00; - num_samples = 0; - loop_start = 0; - loop_end = 0; + /* BlazBlue: Cross Tag Battle (Switch) PSI Metadata for corresponding Opus */ + /* Maybe future Arc System Works games will use this too? */ + PSIFile = open_streamfile_by_ext(streamFile, "psi"); + + offset = 0x00; + + if (PSIFile){ + num_samples = read_32bitLE(0x8C, PSIFile); + loop_start = read_32bitLE(0x84, PSIFile); + loop_end = read_32bitLE(0x88, PSIFile); + close_streamfile(PSIFile); + } + else { + num_samples = 0; + loop_start = 0; + loop_end = 0; + } return init_vgmstream_opus(streamFile, meta_OPUS, offset, num_samples,loop_start,loop_end); fail: @@ -233,3 +247,29 @@ VGMSTREAM * init_vgmstream_opus_nop(STREAMFILE *streamFile) { fail: return NULL; } + +/* Shin'en variation [Fast RMX (Switch)] */ +VGMSTREAM * init_vgmstream_opus_shinen(STREAMFILE *streamFile) { + off_t offset = 0; + int num_samples = 0, loop_start = 0, loop_end = 0; + + /* checks */ + if ( !check_extensions(streamFile,"opus,lopus")) + goto fail; + + if (read_32bitBE(0x08,streamFile) != 0x01000080) + goto fail; + + offset = 0x08; + num_samples = 0; + loop_start = read_32bitLE(0x00,streamFile); + loop_end = read_32bitLE(0x04,streamFile); /* 0 if no loop */ + + if (loop_start > loop_end) + goto fail; /* just in case */ + + return init_vgmstream_opus(streamFile, meta_OPUS, offset, num_samples,loop_start,loop_end); +fail: + return NULL; +} + diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_joe.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_joe.c index 5856f528f..5590cd4e8 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_joe.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_joe.c @@ -1,114 +1,119 @@ #include "meta.h" -#include "../util.h" +#include "../coding/coding.h" -/* JOE (found in Wall-E and some more Pixar games) */ +/* .JOE - from Asobo Studio games [Up (PS2), Wall-E (PS2)] */ VGMSTREAM * init_vgmstream_ps2_joe(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; off_t start_offset; - uint8_t testBuffer[0x10]; - off_t loopStart = 0; - off_t loopEnd = 0; - off_t readOffset = 0; - off_t blockOffset = 0; - off_t sampleOffset = 0; - size_t fileLength; - size_t dataLength; - size_t dataInterleave; + size_t file_size, data_size, unknown1, unknown2, interleave; int loop_flag; - int channel_count; + int channel_count; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("joe",filename_extension(filename))) goto fail; - - /* check header */ - // if (read_32bitBE(0x0C,streamFile) != 0xCCCCCCCC) - // goto fail; + /* checks */ + if (!check_extensions(streamFile, "joe")) + goto fail; loop_flag = 1; channel_count = 2; - /* build the VGMSTREAM */ + file_size = get_streamfile_size(streamFile); + data_size = read_32bitLE(0x04,streamFile); + unknown1 = read_32bitLE(0x08,streamFile); + unknown2 = read_32bitLE(0x0c,streamFile); + + /* detect version */ + if (data_size/2 == file_size - 0x10 + && unknown1 == 0x0045039A && unknown2 == 0x00108920) { /* Super Farm */ + data_size = data_size / 2; + interleave = 0x4000; + } + else if (data_size/2 == file_size - 0x10 + && unknown1 == 0xCCCCCCCC && unknown2 == 0xCCCCCCCC) { /* Sitting Ducks */ + data_size = data_size / 2; + interleave = 0x8000; + } + else if (data_size == file_size - 0x10 + && unknown1 == 0xCCCCCCCC && unknown2 == 0xCCCCCCCC) { /* The Mummy: The Animated Series */ + data_size = data_size / 2; + interleave = 0x8000; + } + else if (data_size == file_size - 0x4020) { /* CT Special Forces (and all games beyond) */ + data_size = data_size / 2; + interleave = unknown1; /* always 0? */ + if (!interleave) + interleave = 0x10; + /* header padding contains garbage */ + } + else { + goto fail; + } + + start_offset = file_size - data_size; + + + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - fileLength = get_streamfile_size(streamFile); - dataLength = read_32bitLE(0x4,streamFile); - dataInterleave = read_32bitLE(0x8,streamFile); - - if (!dataInterleave) - dataInterleave = 16; /* XXX */ - - /* fill in the vital statistics */ - start_offset = fileLength - dataLength; - vgmstream->channels = channel_count; - vgmstream->sample_rate = read_32bitLE(0x0,streamFile); + vgmstream->sample_rate = read_32bitLE(0x00,streamFile); vgmstream->coding_type = coding_PSX; - vgmstream->num_samples = dataLength*28/16/channel_count; - - - readOffset = start_offset; - do { - off_t blockRead = (off_t)read_streamfile(testBuffer,readOffset,0x10,streamFile); + vgmstream->num_samples = ps_bytes_to_samples(data_size, channel_count); - readOffset += blockRead; - blockOffset += blockRead; - - if (blockOffset >= dataInterleave) { - readOffset += dataInterleave; - blockOffset -= dataInterleave; - } - - /* Loop Start */ - if(testBuffer[0x01]==0x06) { - if(loopStart == 0) loopStart = sampleOffset; - /* break; */ - } - - sampleOffset += 28; - - /* Loop End */ - if(testBuffer[0x01]==0x03) { - if(loopEnd == 0) loopEnd = sampleOffset; - /* break; */ - } - - } while (streamFile->get_offset(streamFile)<(int32_t)fileLength); - - if(loopStart == 0 && loopEnd == 0) { - loop_flag = 0; - vgmstream->num_samples = dataLength*28/16/channel_count; - } else { - loop_flag = 1; - vgmstream->loop_start_sample = loopStart; - vgmstream->loop_end_sample = loopEnd; - } - - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = dataInterleave; - vgmstream->meta_type = meta_PS2_JOE; - - /* open the file for reading */ + //todo improve, not working 100% with early .joe { - int i; - STREAMFILE * file; - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - for (i=0;ich[i].streamfile = file; + uint8_t testBuffer[0x10]; + off_t blockOffset = 0; + off_t sampleOffset = 0; + off_t readOffset = 0; + off_t loopStart = 0, loopEnd = 0; - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=start_offset+ - vgmstream->interleave_block_size*i; + readOffset = start_offset; + do { + off_t blockRead = (off_t)read_streamfile(testBuffer,readOffset,0x10,streamFile); + readOffset += blockRead; + blockOffset += blockRead; + + if (blockOffset >= interleave) { + readOffset += interleave; + blockOffset -= interleave; + } + + /* Loop Start */ + if(testBuffer[0x01]==0x06) { + if(loopStart == 0) + loopStart = sampleOffset; + /* break; */ + } + + sampleOffset += 28; + + /* Loop End */ + if(testBuffer[0x01]==0x03) { + if(loopEnd == 0) + loopEnd = sampleOffset; + /* break; */ + } + + } while (streamFile->get_offset(streamFile)<(int32_t)file_size); + + if (loopStart == 0 && loopEnd == 0) { + vgmstream->loop_flag = 0; + } else { + vgmstream->loop_start_sample = loopStart; + vgmstream->loop_end_sample = loopEnd; } } + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = interleave; + vgmstream->meta_type = meta_PS2_JOE; + + 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/riff.c b/Frameworks/vgmstream/vgmstream/src/meta/riff.c index c6a5953b2..ad1353787 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/riff.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/riff.c @@ -257,8 +257,9 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { /* .lwav: to avoid hijacking .wav, .xwav: fake for Xbox games (unneded anymore) */ /* .da: The Great Battle VI (PS), .cd: Exector (PS), .med: Psi Ops (PC), .snd: Layton Brothers (iOS/Android), * .adx: Remember11 (PC) sfx - * .adp: Headhunter (DC) */ - if ( check_extensions(streamFile, "wav,lwav,xwav,da,cd,med,snd,adx,adp") ) { + * .adp: Headhunter (DC) + * .xss: Spider-Man The Movie (Xbox) */ + if ( check_extensions(streamFile, "wav,lwav,xwav,da,cd,med,snd,adx,adp,xss") ) { ; } else if ( check_extensions(streamFile, "mwv") ) { diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ta_aac.c b/Frameworks/vgmstream/vgmstream/src/meta/ta_aac.c index c6ce49f0b..2d3c6a97b 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ta_aac.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ta_aac.c @@ -261,8 +261,8 @@ VGMSTREAM * init_vgmstream_ta_aac_mobile(STREAMFILE *streamFile) { /* check extension, case insensitive */ - /* .aac: expected, .laac/ace: for players to avoid hijacking MP4/AAC */ - if (!check_extensions(streamFile, "aac,laac,ace")) + /* .aac: expected, .laac: for players to avoid hijacking MP4/AAC */ + if (!check_extensions(streamFile, "aac,laac")) goto fail; if (read_32bitLE(0x00, streamFile) != 0x41414320) /* "AAC " */ @@ -297,8 +297,8 @@ VGMSTREAM * init_vgmstream_ta_aac_mobile(STREAMFILE *streamFile) { vgmstream->layout_type = layout_none; vgmstream->num_samples = yamaha_bytes_to_samples(data_size, channel_count); - vgmstream->loop_start_sample = yamaha_bytes_to_samples(read_32bitLE(0x130, streamFile), channel_count);; - vgmstream->loop_end_sample = yamaha_bytes_to_samples(read_32bitLE(0x134, streamFile), channel_count);; + vgmstream->loop_start_sample = yamaha_bytes_to_samples(read_32bitLE(0x130, streamFile), channel_count); + vgmstream->loop_end_sample = yamaha_bytes_to_samples(read_32bitLE(0x134, streamFile), channel_count); break; default: @@ -313,3 +313,66 @@ fail: close_vgmstream(vgmstream); return NULL; } + +/* Vita variants [Judas Code (Vita)] */ +VGMSTREAM * init_vgmstream_ta_aac_vita(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + int channel_count, loop_flag; + + + /* check extension, case insensitive */ + /* .aac: expected, .laac: for players to avoid hijacking MP4/AAC */ + if (!check_extensions(streamFile, "aac,laac")) + goto fail; + + if (read_32bitLE(0x00, streamFile) != 0x41414320) /* "AAC " */ + goto fail; + if (read_32bitLE(0x14, streamFile) != 0x56495441) /* "VITA" */ + goto fail; + if (read_32bitLE(0x10d0, streamFile) != 0x57415645) /* "WAVE" */ + goto fail; + + /* there is a bunch of chunks but we simplify */ + + /* 0x10E4: codec 0x08? */ + channel_count = read_8bit(0x10E5, streamFile); + start_offset = read_32bitLE(0x1100, streamFile); + loop_flag = (read_32bitLE(0x1114, streamFile) > 0); + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = read_32bitLE(0x10e8, streamFile); + vgmstream->meta_type = meta_TA_AAC_VITA; + +#ifdef VGM_USE_ATRAC9 + { + atrac9_config cfg = {0}; + + cfg.channels = vgmstream->channels; + cfg.encoder_delay = read_32bitLE(0x1124,streamFile); + cfg.config_data = read_32bitBE(0x1128,streamFile); + + vgmstream->codec_data = init_atrac9(&cfg); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_ATRAC9; + vgmstream->layout_type = layout_none; + + vgmstream->num_samples = atrac9_bytes_to_samples(read_32bitLE(0x10EC, streamFile), vgmstream->codec_data); + vgmstream->num_samples -= cfg.encoder_delay; + vgmstream->loop_start_sample = atrac9_bytes_to_samples(read_32bitLE(0x1110, streamFile), vgmstream->codec_data); + vgmstream->loop_end_sample = atrac9_bytes_to_samples(read_32bitLE(0x1114, streamFile), vgmstream->codec_data); + } +#endif + + 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/txth.c b/Frameworks/vgmstream/vgmstream/src/meta/txth.c index 25a7a6bd7..235b9ddd8 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/txth.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/txth.c @@ -63,6 +63,8 @@ typedef struct { uint32_t coef_big_endian; uint32_t coef_mode; + int num_samples_data_size; + } txth_header; static STREAMFILE * open_txth(STREAMFILE * streamFile); @@ -343,25 +345,29 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { } #ifdef VGM_USE_FFMPEG - if (txth.sample_type_bytes && (txth.codec == XMA1 || txth.codec == XMA2)) { + if ((txth.sample_type_bytes || txth.num_samples_data_size) && (txth.codec == XMA1 || txth.codec == XMA2)) { /* manually find sample offsets */ - ms_sample_data msd; - memset(&msd,0,sizeof(ms_sample_data)); + ms_sample_data msd = {0}; msd.xma_version = 1; msd.channels = txth.channels; msd.data_offset = txth.start_offset; msd.data_size = txth.data_size; - msd.loop_flag = txth.loop_flag; - msd.loop_start_b = txth.loop_start_sample; - msd.loop_end_b = txth.loop_end_sample; - msd.loop_start_subframe = txth.loop_adjust & 0xF; /* lower 4b: subframe where the loop starts, 0..4 */ - msd.loop_end_subframe = txth.loop_adjust >> 4; /* upper 4b: subframe where the loop ends, 0..3 */ + if (txth.sample_type_bytes) { + msd.loop_flag = txth.loop_flag; + msd.loop_start_b = txth.loop_start_sample; + msd.loop_end_b = txth.loop_end_sample; + msd.loop_start_subframe = txth.loop_adjust & 0xF; /* lower 4b: subframe where the loop starts, 0..4 */ + msd.loop_end_subframe = txth.loop_adjust >> 4; /* upper 4b: subframe where the loop ends, 0..3 */ + } xma_get_samples(&msd, streamFile); + vgmstream->num_samples = msd.num_samples; - vgmstream->loop_start_sample = msd.loop_start_sample; - vgmstream->loop_end_sample = msd.loop_end_sample; + if (txth.sample_type_bytes) { + vgmstream->loop_start_sample = msd.loop_start_sample; + vgmstream->loop_end_sample = msd.loop_end_sample; + } //skip_samples = msd.skip_samples; //todo add skip samples } #endif @@ -524,6 +530,7 @@ static int parse_keyval(STREAMFILE * streamFile, STREAMFILE * streamText, txth_h else if (0==strcmp(key,"num_samples")) { if (0==strcmp(val,"data_size")) { txth->num_samples = get_bytes_to_samples(txth, txth->data_size); + txth->num_samples_data_size = 1; } else { if (!parse_num(streamFile,val, &txth->num_samples)) goto fail; @@ -680,13 +687,15 @@ static int get_bytes_to_samples(txth_header * txth, uint32_t bytes) { if (!txth->interleave) return 0; return bytes / txth->interleave * 256 * txth->channels; - /* untested */ case IMA: case DVI_IMA: + return ima_bytes_to_samples(bytes, txth->channels); + case AICA: + return aica_bytes_to_samples(bytes, txth->channels); + + /* untested */ case SDX2: return bytes; - case AICA: - return bytes * 2 / txth->channels; case NGC_DTK: return bytes / 32 * 28; /* always stereo? */ case APPLE_IMA4: diff --git a/Frameworks/vgmstream/vgmstream/src/meta/txtp.c b/Frameworks/vgmstream/vgmstream/src/meta/txtp.c index 1a5176222..a0a8dcf22 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/txtp.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/txtp.c @@ -18,6 +18,8 @@ typedef struct { size_t loop_start_segment; size_t loop_end_segment; + + size_t is_layered; } txtp_header; static txtp_header* parse_txtp(STREAMFILE* streamFile); @@ -28,7 +30,9 @@ static void clean_txtp(txtp_header* txtp); VGMSTREAM * init_vgmstream_txtp(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; txtp_header* txtp = NULL; - segmented_layout_data *data = NULL; + segmented_layout_data *data_s = NULL; + layered_layout_data * data_l = NULL; + int i; /* checks */ @@ -56,16 +60,14 @@ VGMSTREAM * init_vgmstream_txtp(STREAMFILE *streamFile) { vgmstream->channel_mask = txtp->entry[0].channel_mask; } - else { - /* multi file */ - int num_samples, loop_start_sample = 0, loop_end_sample = 0; - int i; - int loop_flag, channel_count; + else if (txtp->is_layered) { + /* layered multi file */ + int channel_count = 0, loop_flag; /* init layout */ - data = init_layout_segmented(txtp->entry_count); - if (!data) goto fail; + data_l = init_layout_layered(txtp->entry_count); + if (!data_l) goto fail; /* open each segment subfile */ for (i = 0; i < txtp->entry_count; i++) { @@ -73,15 +75,61 @@ VGMSTREAM * init_vgmstream_txtp(STREAMFILE *streamFile) { if (!temp_streamFile) goto fail; temp_streamFile->stream_index = txtp->entry[i].subsong; - data->segments[i] = init_vgmstream_from_STREAMFILE(temp_streamFile); + data_l->layers[i] = init_vgmstream_from_STREAMFILE(temp_streamFile); close_streamfile(temp_streamFile); - if (!data->segments[i]) goto fail; + if (!data_l->layers[i]) goto fail; - data->segments[i]->channel_mask = txtp->entry[0].channel_mask; + channel_count += data_l->layers[i]->channels; + } + + /* setup layered VGMSTREAMs */ + if (!setup_layout_layered(data_l)) + goto fail; + + loop_flag = data_l->layers[0]->loop_flag; + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = data_l->layers[0]->sample_rate; + vgmstream->num_samples = data_l->layers[0]->num_samples; + vgmstream->loop_start_sample = data_l->layers[0]->loop_start_sample; + vgmstream->loop_end_sample = data_l->layers[0]->loop_end_sample; + + vgmstream->meta_type = meta_TXTP; + vgmstream->coding_type = data_l->layers[0]->coding_type; + vgmstream->layout_type = layout_layered; + + vgmstream->channel_mask = txtp->entry[0].channel_mask; + + vgmstream->layout_data = data_l; + } + else { + /* segmented multi file */ + int num_samples, loop_start_sample = 0, loop_end_sample = 0; + int loop_flag, channel_count; + + + /* init layout */ + data_s = init_layout_segmented(txtp->entry_count); + if (!data_s) goto fail; + + /* open each segment subfile */ + for (i = 0; i < txtp->entry_count; i++) { + STREAMFILE* temp_streamFile = open_streamfile_by_filename(streamFile, txtp->entry[i].filename); + if (!temp_streamFile) goto fail; + temp_streamFile->stream_index = txtp->entry[i].subsong; + + data_s->segments[i] = init_vgmstream_from_STREAMFILE(temp_streamFile); + close_streamfile(temp_streamFile); + if (!data_s->segments[i]) goto fail; + + data_s->segments[i]->channel_mask = txtp->entry[0].channel_mask; } /* setup segmented VGMSTREAMs */ - if (!setup_layout_segmented(data)) + if (!setup_layout_segmented(data_s)) goto fail; /* get looping and samples */ @@ -89,37 +137,37 @@ VGMSTREAM * init_vgmstream_txtp(STREAMFILE *streamFile) { txtp->loop_end_segment = txtp->entry_count; loop_flag = (txtp->loop_start_segment > 0 && txtp->loop_start_segment <= txtp->entry_count); num_samples = 0; - for (i = 0; i < data->segment_count; i++) { + for (i = 0; i < data_s->segment_count; i++) { if (loop_flag && txtp->loop_start_segment == i+1) { loop_start_sample = num_samples; } - num_samples += data->segments[i]->num_samples; + num_samples += data_s->segments[i]->num_samples; if (loop_flag && txtp->loop_end_segment == i+1) { loop_end_sample = num_samples; } } - channel_count = data->segments[0]->channels; + channel_count = data_s->segments[0]->channels; /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - vgmstream->sample_rate = data->segments[0]->sample_rate; + vgmstream->sample_rate = data_s->segments[0]->sample_rate; vgmstream->num_samples = num_samples; vgmstream->loop_start_sample = loop_start_sample; vgmstream->loop_end_sample = loop_end_sample; vgmstream->meta_type = meta_TXTP; - vgmstream->coding_type = data->segments[0]->coding_type; + vgmstream->coding_type = data_s->segments[0]->coding_type; vgmstream->layout_type = layout_segmented; - vgmstream->layout_data = data; + vgmstream->layout_data = data_s; if (loop_flag) - data->loop_segment = txtp->loop_start_segment-1; + data_s->loop_segment = txtp->loop_start_segment-1; } @@ -129,7 +177,8 @@ VGMSTREAM * init_vgmstream_txtp(STREAMFILE *streamFile) { fail: clean_txtp(txtp); close_vgmstream(vgmstream); - free_layout_segmented(data); + free_layout_segmented(data_s); + free_layout_layered(data_l); return NULL; } @@ -189,7 +238,7 @@ static int add_filename(txtp_header * txtp, char *filename) { if (sscanf(config, "%d~%d", &subsong_start, &subsong_end) == 2) { if (subsong_start > 0 && subsong_end > 0) { range_start = subsong_start-1; - range_end = subsong_end-1; + range_end = subsong_end; } } else if (sscanf(config, "%u", &subsong_start) == 1) { @@ -267,6 +316,14 @@ static int parse_keyval(txtp_header * txtp, const char * key, const char * val) else if (0==strcmp(key,"loop_end_segment")) { if (!parse_num(val, &txtp->loop_end_segment)) goto fail; } + else if (0==strcmp(key,"mode")) { + if (0==strcmp(val,"layers")) { + txtp->is_layered = 1; + } + else { + goto fail; + } + } else { VGM_LOG("TXTP: unknown key=%s, val=%s\n", key,val); goto fail; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ubi_jade.c b/Frameworks/vgmstream/vgmstream/src/meta/ubi_jade.c index aab40b98c..479063989 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ubi_jade.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ubi_jade.c @@ -122,8 +122,9 @@ VGMSTREAM * init_vgmstream_ubi_jade(STREAMFILE *streamFile) { switch(codec) { case 0x0069: /* Xbox */ - if (block_size != 0x24*channel_count) - goto fail; + if (fmt_size != 0x12) goto fail; + if (block_size != 0x24*channel_count) goto fail; + vgmstream->coding_type = coding_XBOX_IMA; vgmstream->layout_type = layout_none; @@ -136,8 +137,9 @@ VGMSTREAM * init_vgmstream_ubi_jade(STREAMFILE *streamFile) { break; case 0xFFFF: /* PS2 */ - if (block_size != 0x10) - goto fail; + if (fmt_size != 0x12) goto fail; + if (block_size != 0x10) goto fail; + vgmstream->coding_type = coding_PSX; vgmstream->layout_type = layout_interleave; @@ -159,8 +161,9 @@ VGMSTREAM * init_vgmstream_ubi_jade(STREAMFILE *streamFile) { break; case 0xFFFE: /* GC/Wii */ - if (block_size != 0x08) - goto fail; + if (fmt_size != 0x12) goto fail; + if (block_size != 0x08) goto fail; + vgmstream->coding_type = coding_NGC_DSP; vgmstream->layout_type = layout_interleave; @@ -201,8 +204,9 @@ VGMSTREAM * init_vgmstream_ubi_jade(STREAMFILE *streamFile) { break; case 0x0002: /* PC */ - if (block_size != 0x24*channel_count) - goto fail; + if (fmt_size != 0x12) goto fail; + if (block_size != 0x24*channel_count) goto fail; + vgmstream->coding_type = coding_MSADPCM; vgmstream->layout_type = layout_none; vgmstream->interleave_block_size = 0x24*channel_count; @@ -219,8 +223,8 @@ VGMSTREAM * init_vgmstream_ubi_jade(STREAMFILE *streamFile) { VGMSTREAM *temp_vgmstream = NULL; STREAMFILE *temp_streamFile = NULL; - if (block_size != 0x02*channel_count) - goto fail; + if (fmt_size != 0x10) goto fail; + if (block_size != 0x02*channel_count) goto fail; /* a MSF (usually ATRAC3) masquerading as PCM */ if (read_32bitBE(start_offset, streamFile) != 0x4D534643) /* "MSF\43" */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ubi_lyn.c b/Frameworks/vgmstream/vgmstream/src/meta/ubi_lyn.c index 77c714a16..9d4c934a6 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ubi_lyn.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ubi_lyn.c @@ -159,12 +159,9 @@ VGMSTREAM * init_vgmstream_ubi_lyn(STREAMFILE *streamFile) { mpeg_custom_config cfg = {0}; int i; - if (read_32bitLE(start_offset+0x00,streamFile) != 2) /* id? */ - goto fail; - cfg.interleave = read_32bitLE(start_offset+0x04,streamFile); - cfg.chunk_size = read_32bitLE(start_offset+0x08,streamFile); - /* 0x08: frame size, 0x0c: frame per interleave, 0x10: samples per frame */ + cfg.chunk_size = read_32bitLE(start_offset+0x08,streamFile); /* frame size (not counting MPEG padding?) */ + /* 0x00: id? (2=Tintin, 3=Michael Jackson), 0x0c: frame per interleave, 0x10: samples per frame */ /* skip seek tables and find actual start */ start_offset += 0x14; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/wwise.c b/Frameworks/vgmstream/vgmstream/src/meta/wwise.c index 53d1e3e3d..4871a53f7 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/wwise.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/wwise.c @@ -40,7 +40,7 @@ typedef struct { /* Wwise - Audiokinetic Wwise (Wave Works Interactive Sound Engine) middleware */ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - wwise_header ww; + wwise_header ww = {0}; off_t start_offset, first_offset = 0xc; int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; @@ -57,7 +57,6 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { (read_32bitBE(0x08,streamFile) != 0x58574D41)) /* "XWMA" */ goto fail; - memset(&ww,0,sizeof(wwise_header)); ww.big_endian = read_32bitBE(0x00,streamFile) == 0x52494658;/* RIFX */ if (ww.big_endian) { /* Wwise honors machine's endianness (PC=RIFF, X360=RIFX --unlike XMA) */ @@ -165,10 +164,19 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { VGM_LOG("WWISE: unknown codec 0x%x \n", ww.format); goto fail; } + /* fix for newer Wwise DSP with coefs: Epic Mickey 2 (Wii), Batman Arkham Origins Blackgate (3DS) */ if (ww.format == 0x0002 && ww.extra_size == 0x0c + ww.channels * 0x2e) { ww.codec = DSP; } + else if (ww.format == 0x0002 && ww.block_align == 0x104 * ww.channels) { + //ww.codec = SWITCH_ADPCM; + /* unknown codec, found in Bayonetta 2 (Switch) + * frames of 0x104 per ch, possibly frame header is hist1(2)/hist2(2)/predictor(1) + * (may write 2 header samples + FF*2 nibbles = 0x200 samples per block?) */ + goto fail; + } + /* Some Wwise files (ex. Oddworld PSV, Bayonetta 2 WiiU, often in BGM.bnk) are truncated mirrors of another file. * They come in RAM banks, probably to play the beginning while the rest of the real stream loads. @@ -196,6 +204,7 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { switch(ww.codec) { case PCM: /* common */ /* normally riff.c has priority but it's needed when .wem is used */ + if (ww.fmt_size != 0x10 && ww.fmt_size != 0x18) goto fail; /* old, new */ if (ww.bits_per_sample != 16) goto fail; vgmstream->coding_type = (ww.big_endian ? coding_PCM16BE : coding_PCM16LE); @@ -209,8 +218,10 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { /* slightly modified XBOX-IMA */ /* Wwise reuses common codec ids (ex. 0x0002 MSADPCM) for IMA so this parser should go AFTER riff.c avoid misdetection */ + if (ww.fmt_size != 0x28 && ww.fmt_size != 0x18) goto fail; /* old, new */ if (ww.bits_per_sample != 4) goto fail; if (ww.block_align != 0x24 * ww.channels) goto fail; + vgmstream->coding_type = coding_WWISE_IMA; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = ww.block_align / ww.channels; @@ -227,9 +238,8 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { /* Wwise uses custom Vorbis, which changed over time (config must be detected to pass to the decoder). */ off_t vorb_offset, data_offsets, block_offsets; size_t vorb_size, setup_offset, audio_offset; - vorbis_custom_config cfg; + vorbis_custom_config cfg = {0}; - memset(&cfg, 0, sizeof(vorbis_custom_config)); cfg.channels = ww.channels; cfg.sample_rate = ww.sample_rate; cfg.big_endian = ww.big_endian; @@ -369,11 +379,12 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { size_t wiih_size; int i; + //if (ww.fmt_size != 0x28 && ww.fmt_size != ?) goto fail; /* old, new */ if (ww.bits_per_sample != 4) goto fail; vgmstream->coding_type = coding_NGC_DSP; vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x8; /* ww.block_align = 0x8 in older Wwise, samples per block in newer Wwise */ + vgmstream->interleave_block_size = 0x08; /* ww.block_align = 0x8 in older Wwise, samples per block in newer Wwise */ /* find coef position */ if (find_chunk(streamFile, 0x57696948,first_offset,0, &wiih_offset,&wiih_size, ww.big_endian, 0)) { /*"WiiH"*/ /* older Wwise */ @@ -406,6 +417,7 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { off_t xma2_offset; size_t xma2_size; + if (ww.fmt_size != 0x20 && ww.fmt_size != 0x34 && ww.fmt_size != 0x40) goto fail; /* XMA1, XMA2old, XMA2new */ if (!ww.big_endian) goto fail; /* must be Wwise (real XMA are LE and parsed elsewhere) */ if (find_chunk(streamFile, 0x584D4132,first_offset,0, &xma2_offset,&xma2_size, ww.big_endian, 0)) { /*"XMA2"*/ /* older Wwise */ @@ -413,7 +425,6 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { } else { /* newer Wwise */ bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,0x100, ww.fmt_offset, ww.fmt_size, ww.data_size, streamFile, ww.big_endian); } - if (bytes <= 0) goto fail; vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, ww.data_offset,ww.data_size); if ( !vgmstream->codec_data ) goto fail; @@ -435,11 +446,10 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { uint8_t buf[0x100]; int bytes; + if (ww.fmt_size != 0x18) goto fail; if (!ww.big_endian) goto fail; /* must be from Wwise X360 (PC LE XWMA is parsed elsewhere) */ bytes = ffmpeg_make_riff_xwma(buf,0x100, ww.format, ww.data_size, vgmstream->channels, vgmstream->sample_rate, ww.average_bps, ww.block_align); - if (bytes <= 0) goto fail; - ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, ww.data_offset,ww.data_size); if ( !ffmpeg_data ) goto fail; vgmstream->codec_data = ffmpeg_data; @@ -449,8 +459,7 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { /* manually find total samples, why don't they put this in the header is beyond me */ { - ms_sample_data msd; - memset(&msd,0,sizeof(ms_sample_data)); + ms_sample_data msd = {0}; msd.channels = ww.channels; msd.data_offset = ww.data_offset; @@ -472,6 +481,8 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { case AAC: { /* iOS/Mac */ ffmpeg_codec_data * ffmpeg_data = NULL; + + if (ww.fmt_size != 0x24) goto fail; if (ww.block_align != 0 || ww.bits_per_sample != 0) goto fail; /* extra: size 0x12, unknown values */ @@ -489,7 +500,7 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { case OPUS: { /* Switch */ uint8_t buf[0x100]; size_t bytes, skip; - ffmpeg_custom_config cfg; + ffmpeg_custom_config cfg = {0}; /* values up to 0x14 seem fixed and similar to HEVAG's (block_align 0x02/04, bits_per_sample 0x10) */ if (ww.fmt_size == 0x28) { @@ -508,13 +519,10 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { skip = 0; /* Wwise doesn't seem to use it? (0x138 /0x3E8 ~default) */ - bytes = ffmpeg_make_opus_header(buf,0x100, ww.channels, skip, ww.sample_rate); - if (bytes <= 0) goto fail; - - memset(&cfg, 0, sizeof(ffmpeg_custom_config)); cfg.type = FFMPEG_SWITCH_OPUS; //cfg.big_endian = ww.big_endian; /* internally BE */ + bytes = ffmpeg_make_opus_header(buf,0x100, ww.channels, skip, ww.sample_rate); vgmstream->codec_data = init_ffmpeg_config(streamFile, buf,bytes, start_offset,ww.data_size, &cfg); if (!vgmstream->codec_data) goto fail; @@ -529,6 +537,7 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { //ww.bits_per_sample; /* unknown (0x10) */ //if (ww.bits_per_sample != 4) goto fail; + if (ww.fmt_size != 0x18) goto fail; if (ww.big_endian) goto fail; /* extra_data: size 0x06, @0x00: samples per block (0x1c), @0x04: channel config */ @@ -543,6 +552,8 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { #ifdef VGM_USE_ATRAC9 case ATRAC9: { /* PSV/PS4 */ atrac9_config cfg = {0}; + + if (ww.fmt_size != 0x24) goto fail; if (ww.extra_size != 0x12) goto fail; cfg.channels = vgmstream->channels; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xwb.c b/Frameworks/vgmstream/vgmstream/src/meta/xwb.c index 8c104f465..e970c78b4 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/xwb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/xwb.c @@ -1,6 +1,7 @@ #include "meta.h" #include "../util.h" #include "../coding/coding.h" +#include /* most info from XWBtool, xactwb.h, xact2wb.h and xact3wb.h */ @@ -25,7 +26,7 @@ static const int wma_block_align_index[17] = { }; -typedef enum { PCM, XBOX_ADPCM, MS_ADPCM, XMA1, XMA2, WMA, XWMA, ATRAC3, OGG } xact_codec; +typedef enum { PCM, XBOX_ADPCM, MS_ADPCM, XMA1, XMA2, WMA, XWMA, ATRAC3, OGG, DSP, ATRAC9_RIFF } xact_codec; typedef struct { int little_endian; int version; @@ -69,6 +70,7 @@ typedef struct { } xwb_header; static void get_name(char * buf, size_t maxsize, int target_subsong, xwb_header * xwb, STREAMFILE *streamFile); +static STREAMFILE* setup_subfile_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, const char* fake_ext); /* XWB - XACT Wave Bank (Microsoft SDK format for XBOX/XBOX360/Windows) */ @@ -292,33 +294,45 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { } } - /* Techland's bizarre format hijack (Nail'd, Sniper: Ghost Warrior PS3). - * Somehow they used XWB + ATRAC3 in their PS3 games, very creative */ + + /* format hijacks from creative devs, using non-official codecs */ if (xwb.version == XACT_TECHLAND && xwb.codec == XMA2 /* XACT_TECHLAND used in their X360 games too */ - && (xwb.block_align == 0x60 || xwb.block_align == 0x98 || xwb.block_align == 0xc0) ) { - xwb.codec = ATRAC3; /* standard ATRAC3 blocks sizes; no other way to identify (other than reading data) */ + && (xwb.block_align == 0x60 || xwb.block_align == 0x98 || xwb.block_align == 0xc0) ) { /* standard ATRAC3 blocks sizes */ + /* Techland ATRAC3 [Nail'd (PS3), Sniper: Ghost Warrior (PS3)] */ + xwb.codec = ATRAC3; /* num samples uses a modified entry_info format (maybe skip samples + samples? sfx use the standard format) * ignore for now and just calc max samples */ xwb.num_samples = atrac3_bytes_to_samples(xwb.stream_size, xwb.block_align * xwb.channels); } - - /* Oddworld: Stranger's Wrath iOS/Android format hijack, with changed meanings */ - if (xwb.codec == OGG) { + else if (xwb.codec == OGG) { + /* Oddworld: Stranger's Wrath (iOS/Android) */ xwb.num_samples = xwb.stream_size / (2 * xwb.channels); /* uncompressed bytes */ xwb.stream_size = xwb.loop_end; xwb.loop_start = 0; xwb.loop_end = 0; } + else if (xwb.version == XACT3_0_MAX && xwb.codec == XMA2 + && xwb.bits_per_sample == 0x01 && xwb.block_align == 0x04 + && xwb.data_size == 0x55951c1c) { /* some kind of id? */ + /* Stardew Valley (Switch), full interleaved DSPs (including headers) */ + xwb.codec = DSP; + } + else if (xwb.version == XACT3_0_MAX && xwb.codec == XMA2 + && xwb.bits_per_sample == 0x01 && xwb.block_align == 0x04 + && xwb.data_size == 0x4e0a1000) { /* some kind of id? */ + /* Stardew Valley (Vita), standard RIFF with ATRAC9 */ + xwb.codec = ATRAC9_RIFF; + } /* test loop after the above fixes */ xwb.loop_flag = (xwb.loop_end > 0 || xwb.loop_end_sample > xwb.loop_start) && !(xwb.entry_flags & WAVEBANKENTRY_FLAGS_IGNORELOOP); - if (xwb.codec != OGG) { - /* for Oddworld OGG the data_size value is size of uncompressed bytes instead */ - /* some BlazBlue Centralfiction songs have padding after data size (maybe wrong rip?) */ + /* Oddworld OGG the data_size value is size of uncompressed bytes instead; DSP uses some id/config as value */ + if (xwb.codec != OGG && xwb.codec != DSP && xwb.codec != ATRAC9_RIFF) { + /* some low-q rips don't remove padding, relax validation a bit */ if (xwb.data_offset + xwb.data_size > get_streamfile_size(streamFile)) goto fail; } @@ -462,7 +476,7 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { break; } - case XWMA: { /* WMAudio2 (WMA v2): BlazBlue (X360), WMAudio3 (WMA Pro): ? */ + case XWMA: { /* WMAudio2 (WMA v2): BlazBlue (X360), WMAudio3 (WMA Pro): Bullet Witch (PC) voices */ uint8_t buf[100]; int bytes, bps_index, block_align, block_index, avg_bps, wma_codec; @@ -485,7 +499,7 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { break; } - case ATRAC3: { /* Techland PS3 extension: Sniper Ghost Warrior (PS3) */ + case ATRAC3: { /* Techland PS3 extension [Sniper Ghost Warrior (PS3)] */ uint8_t buf[200]; int bytes; @@ -503,14 +517,48 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { break; } - case OGG: { /* Oddworld: Strangers Wrath iOS/Android extension */ + case OGG: { /* Oddworld: Strangers Wrath (iOS/Android) extension */ vgmstream->codec_data = init_ffmpeg_offset(streamFile, xwb.stream_offset, xwb.stream_size); if ( !vgmstream->codec_data ) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; break; } +#endif + case DSP: { /* Stardew Valley (Switch) extension */ + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = xwb.stream_size / xwb.channels; + + dsp_read_coefs(vgmstream,streamFile,xwb.stream_offset + 0x1c,vgmstream->interleave_block_size,!xwb.little_endian); + dsp_read_hist (vgmstream,streamFile,xwb.stream_offset + 0x3c,vgmstream->interleave_block_size,!xwb.little_endian); + xwb.stream_offset += 0x60; /* skip DSP header */ + break; + } + +#ifdef VGM_USE_ATRAC9 + case ATRAC9_RIFF: { /* Stardew Valley (Vita) extension */ + VGMSTREAM *temp_vgmstream = NULL; + STREAMFILE *temp_streamFile = NULL; + + /* standard RIFF, use subfile (seems doesn't use xwb loops) */ + VGM_ASSERT(xwb.loop_flag, "XWB: RIFF ATRAC9 loop flag found\n"); + + temp_streamFile = setup_subfile_streamfile(streamFile, xwb.stream_offset,xwb.stream_size, "at9"); + if (!temp_streamFile) goto fail; + + temp_vgmstream = init_vgmstream_riff(temp_streamFile); + close_streamfile(temp_streamFile); + if (!temp_vgmstream) goto fail; + + temp_vgmstream->num_streams = vgmstream->num_streams; + temp_vgmstream->stream_size = vgmstream->stream_size; + temp_vgmstream->meta_type = vgmstream->meta_type; + + close_vgmstream(vgmstream); + return temp_vgmstream; + } #endif default: @@ -529,6 +577,30 @@ fail: return NULL; } +/* ****************************************************************************** */ + +static STREAMFILE* setup_subfile_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, const char* fake_ext) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + + /* setup subfile */ + 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; + + new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,fake_ext); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + return temp_streamFile; + +fail: + close_streamfile(temp_streamFile); + return NULL; +} /* ****************************************************************************** */ @@ -591,7 +663,7 @@ typedef struct { /* try to find the stream name in a companion XSB file, a comically complex cue format. */ -static int get_xsb_name(char * buf, size_t maxsize, int target_subsong, xwb_header * xwb, STREAMFILE *streamXwb) { +static int get_xsb_name(char * buf, size_t maxsize, int target_subsong, xwb_header * xwb, STREAMFILE *streamXwb, char* filename) { STREAMFILE *streamFile = NULL; int i,j, start_sound, cfg__start_sound = 0, cfg__selected_wavebank = 0; int xsb_version; @@ -601,7 +673,10 @@ static int get_xsb_name(char * buf, size_t maxsize, int target_subsong, xwb_head xsb_header xsb = {0}; - streamFile = open_streamfile_by_ext(streamXwb, "xsb"); + if (filename) + streamFile = open_streamfile_by_filename(streamXwb, filename); + else + streamFile = open_streamfile_by_ext(streamXwb, "xsb"); if (!streamFile) goto fail; /* check header */ @@ -718,8 +793,10 @@ static int get_xsb_name(char * buf, size_t maxsize, int target_subsong, xwb_head } else { suboff = size - 0x08; } + //} else if (flag==0x11) { /* Stardew Valley (Switch) */ + // suboff = size; //??? } else { - VGM_LOG("XSB: xsb flag 0x%x at offset 0x%08lx not implemented\n", flag, off); + VGM_LOG("XSB: xsb flag 0x%x (size=%x) at offset 0x%08lx not implemented\n", flag, size, off); goto fail; } } @@ -871,18 +948,34 @@ fail: static void get_name(char * buf, size_t maxsize, int target_subsong, xwb_header * xwb, STREAMFILE *streamFile) { int name_found; + char xwb_filename[PATH_LIMIT]; + char xsb_filename[PATH_LIMIT]; /* try inside this xwb */ name_found = get_xwb_name(buf, maxsize, target_subsong, xwb, streamFile); if (name_found) return; - /* try again in external .xsb */ - get_xsb_name(buf, maxsize, target_subsong, xwb, streamFile); + + /* try again in external .xsb, using a bunch of possible name pairs */ + get_streamfile_filename(streamFile,xwb_filename,PATH_LIMIT); + + if (strcmp(xwb_filename,"Wave Bank.xwb")==0) { + strcpy(xsb_filename,"Sound Bank.xsb"); + } + else if (strcmp(xwb_filename,"UIMusicBank.xwb")==0) { + strcpy(xsb_filename,"UISoundBank.xsb"); + } + else { + xsb_filename[0] = '\0'; + } + //todo try others: InGameMusic.xwb + ingamemusic.xsb, NB_BGM_m0100_WB.xwb + NB_BGM_m0100_SB.xsb, etc + + if (xsb_filename[0] != '\0') { + name_found = get_xsb_name(buf, maxsize, target_subsong, xwb, streamFile, xsb_filename); + if (name_found) return; + } - //todo try again with common names (xwb and xsb often are named slightly differently using a common convention): - // InGameMusic.xwb + ingamemusic.xsb - // UIMusicBank.xwb + UISoundBank.xsb - // NB_BGM_m0100_WB.xwb + NB_BGM_m0100_SB.xsb - // Wave Bank.xwb + Sound Bank.xsb + /* one last time with same name */ + get_xsb_name(buf, maxsize, target_subsong, xwb, streamFile, NULL); } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xwc.c b/Frameworks/vgmstream/vgmstream/src/meta/xwc.c index a9f7b4c54..fbc5c28ef 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/xwc.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/xwc.c @@ -4,26 +4,44 @@ /* .XWC - Starbreeze games [Chronicles of Riddick: Assault on Dark Athena, Syndicate] */ VGMSTREAM * init_vgmstream_xwc(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - off_t start_offset; + off_t start_offset, extra_offset; size_t data_size; - int loop_flag, channel_count, codec; + int loop_flag, channel_count, codec, num_samples; /* check extensions (.xwc is the extension of the bigfile, individual files don't have one) */ if ( !check_extensions(streamFile,"xwc")) goto fail; - if(read_32bitBE(0x00,streamFile) != 0x00040000 && /* version? */ - read_32bitBE(0x04,streamFile) != 0x00900000) - goto fail; - data_size = read_32bitLE(0x08, streamFile); /* including subheader */ - channel_count = read_32bitLE(0x0c, streamFile); - /* 0x10: num_samples */ - /* 0x14: 0x8000? */ - codec = read_32bitBE(0x24, streamFile); - /* 0x28: num_samples */ - /* 0x2c: config data? (first nibble: 0x4=mono, 0x8=stereo) */ - /* 0x30+: codec dependant */ + /* version */ + if (read_32bitBE(0x00,streamFile) == 0x00030000 && + read_32bitBE(0x04,streamFile) == 0x00900000) { /* The Darkness */ + data_size = read_32bitLE(0x08, streamFile); /* including subheader */ + channel_count = read_32bitLE(0x0c, streamFile); + /* 0x10: num_samples */ + /* 0x14: 0x8000? */ + /* 0x18: null */ + codec = read_32bitBE(0x1c, streamFile); + num_samples = read_32bitLE(0x20, streamFile); + /* 0x24: config data >> 2? (0x00(1): channels; 0x01(2): ?, 0x03(2): sample_rate) */ + extra_offset = 0x28; + } + else if (read_32bitBE(0x00,streamFile) == 0x00040000 && + read_32bitBE(0x04,streamFile) == 0x00900000) { /* Riddick, Syndicate */ + data_size = read_32bitLE(0x08, streamFile); /* including subheader */ + channel_count = read_32bitLE(0x0c, streamFile); + /* 0x10: num_samples */ + /* 0x14: 0x8000? */ + codec = read_32bitBE(0x24, streamFile); + num_samples = read_32bitLE(0x28, streamFile); + /* 0x2c: config data >> 2? (0x00(1): channels; 0x01(2): ?, 0x03(2): sample_rate) */ + /* 0x30+: codec dependant */ + extra_offset = 0x30; + } + else { + goto fail; + } + loop_flag = 0; /* seemingly not in the file */ @@ -31,7 +49,7 @@ VGMSTREAM * init_vgmstream_xwc(STREAMFILE *streamFile) { vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - vgmstream->num_samples = read_32bitLE(0x28, streamFile); + vgmstream->num_samples = num_samples; vgmstream->meta_type = meta_XWC; switch(codec) { @@ -40,8 +58,8 @@ VGMSTREAM * init_vgmstream_xwc(STREAMFILE *streamFile) { mpeg_custom_config cfg = {0}; start_offset = 0x800; - vgmstream->num_samples = read_32bitLE(0x30, streamFile); /* with encoder delay */ //todo improve - cfg.data_size = read_32bitLE(0x34, streamFile); //data_size - 0x28; + vgmstream->num_samples = read_32bitLE(extra_offset+0x00, streamFile); /* with encoder delay */ //todo improve + cfg.data_size = read_32bitLE(extra_offset+0x04, streamFile); //data_size - 0x28; vgmstream->codec_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_STANDARD, &cfg); if (!vgmstream->codec_data) goto fail; @@ -56,13 +74,13 @@ VGMSTREAM * init_vgmstream_xwc(STREAMFILE *streamFile) { uint8_t buf[0x100]; int32_t bytes, seek_size, block_size, block_count, sample_rate; - seek_size = read_32bitLE(0x30, streamFile); - start_offset = 0x34 + seek_size + read_32bitLE(0x34+seek_size, streamFile) + 0x08; + seek_size = read_32bitLE(extra_offset+0x00, streamFile); + start_offset = extra_offset+0x04 + seek_size + read_32bitLE(extra_offset+0x04+seek_size, streamFile) + 0x08; start_offset += (start_offset % 0x800) ? 0x800 - (start_offset % 0x800) : 0; /* padded */ - sample_rate = read_32bitBE(0x34+seek_size+0x10, streamFile); - block_size = read_32bitBE(0x34+seek_size+0x1c, streamFile); - block_count = read_32bitBE(0x34+seek_size+0x28, streamFile); + sample_rate = read_32bitBE(extra_offset+0x04+seek_size+0x10, streamFile); + block_size = read_32bitBE(extra_offset+0x04+seek_size+0x1c, streamFile); + block_count = read_32bitBE(extra_offset+0x04+seek_size+0x28, streamFile); /* others: scrambled RIFF fmt BE values */ bytes = ffmpeg_make_riff_xma2(buf,0x100, vgmstream->num_samples, data_size, vgmstream->channels, sample_rate, block_count, block_size); @@ -93,10 +111,6 @@ VGMSTREAM * init_vgmstream_xwc(STREAMFILE *streamFile) { goto fail; } - if (vgmstream->sample_rate != 48000) { /* get from config data instead of codecs? */ - VGM_LOG("XWC: unexpected sample rate %i\n",vgmstream->sample_rate); - goto fail; - } if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) goto fail; diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.c b/Frameworks/vgmstream/vgmstream/src/vgmstream.c index 832b36b12..0679eddec 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.c +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.c @@ -353,6 +353,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_ta_aac_ps3, init_vgmstream_ta_aac_mobile, init_vgmstream_ta_aac_mobile_vorbis, + init_vgmstream_ta_aac_vita, init_vgmstream_ps3_mta2, init_vgmstream_ngc_ulw, init_vgmstream_pc_xa30, @@ -367,6 +368,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_opus_n1, init_vgmstream_opus_capcom, init_vgmstream_opus_nop, + init_vgmstream_opus_shinen, init_vgmstream_pc_al2, init_vgmstream_pc_ast, init_vgmstream_naac, @@ -407,6 +409,8 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_opus_ppp, init_vgmstream_ubi_bao_pk, init_vgmstream_dsp_switch_audio, + init_vgmstream_dsp_sadf, + init_vgmstream_h4m, init_vgmstream_txth, /* should go at the end (lower priority) */ #ifdef VGM_USE_FFMPEG @@ -933,6 +937,7 @@ void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstre case layout_blocked_ea_wve_au00: case layout_blocked_ea_wve_ad10: case layout_blocked_sthd: + case layout_blocked_h4m: render_vgmstream_blocked(buffer,sample_count,vgmstream); break; case layout_aix: diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.h b/Frameworks/vgmstream/vgmstream/src/vgmstream.h index d53fa4197..02452edc1 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.h +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.h @@ -259,6 +259,7 @@ typedef enum { layout_blocked_ea_wve_au00, /* EA WVE au00 blocks */ layout_blocked_ea_wve_ad10, /* EA WVE Ad10 blocks */ layout_blocked_sthd, /* Dream Factory STHD */ + layout_blocked_h4m, /* H4M video */ /* otherwise odd */ layout_aix, /* CRI AIX's wheels within wheels */ @@ -289,6 +290,7 @@ typedef enum { meta_DSP_WII_WSD, /* Phantom Brave (WII) */ meta_WII_NDP, /* Vertigo (Wii) */ meta_DSP_YGO, /* Konami: Yu-Gi-Oh! The Falsebound Kingdom (NGC), Hikaru no Go 3 (NGC) */ + meta_DSP_SADF, /* Procyon Studio SADF - Xenoblade Chronicles 2 (Switch) */ /* Nintendo */ meta_STRM, /* Nintendo STRM */ @@ -682,6 +684,9 @@ typedef enum { meta_OPUS_PPP, /* .at9 Opus [Penny-Punching Princess (Switch)] */ meta_UBI_BAO, /* Ubisoft BAO */ meta_DSP_SWITCH_AUDIO, /* Gal Gun 2 (Switch) */ + meta_TA_AAC_VITA, /* tri-Ace AAC (Judas Code) */ + meta_OGG_GWM, /* Ogg Vorbis with encryption [Metronomicon (PC)] */ + meta_H4M, /* Hudson HVQM4 video [Resident Evil 0 (GC), Tales of Symphonia (GC)] */ #ifdef VGM_USE_FFMPEG meta_FFmpeg,